다음을 통해 공유


자습서: 데이터 바인딩 만들기

당신이 멋진 UI를 설계하고 구현했다고 가정해 보겠습니다. 이 UI는 자리 표시자 이미지, "lorem ipsum" 상용구 텍스트, 그리고 아직 아무 작업도 수행하지 않는 컨트롤로 채워져 있습니다. 다음으로, 실제 데이터에 연결하고 디자인 프로토타입에서 살아있는 앱으로 변환하려고 합니다.

이 자습서에서는 상용구를 데이터 바인딩으로 바꾸고 UI와 데이터 간에 다른 직접 링크를 만드는 방법을 알아봅니다. 표시할 데이터의 서식을 지정하거나 변환하고 UI와 데이터를 동기화 상태로 유지하는 방법도 알아봅니다. 이 자습서를 완료하면 XAML 및 C# 코드의 단순성과 구성을 개선하여 더 쉽게 유지 관리하고 확장할 수 있습니다.

PhotoLab 샘플의 간소화된 버전부터 시작합니다. 이 시작 버전에는 전체 데이터 계층과 기본 XAML 페이지 레이아웃이 포함되어 있으며 코드를 더 쉽게 탐색할 수 있도록 많은 기능이 제외됩니다. 이 자습서는 전체 앱까지 빌드되지 않으므로 최종 버전을 확인하여 사용자 지정 애니메이션 및 적응형 레이아웃과 같은 기능을 확인해야 합니다. 최종 버전은 Windows-appsample-photo-lab 리포지토리의 루트 폴더에서 찾을 수 있습니다.

PhotoLab 샘플 앱에는 두 페이지가 있습니다. 메인 페이지는 각 이미지 파일에 대한 일부 정보와 함께 사진 갤러리 뷰를 표시합니다.

사진 랩 기본 페이지의 스크린샷

세부 정보 페이지는 사진이 선택된 후 단일 사진을 표시합니다. 플라이아웃 편집 메뉴를 사용하면 사진을 변경하고 이름을 바꾸고 저장할 수 있습니다.

사진 랩 세부 정보 페이지의 스크린샷

필수 조건

0부: GitHub에서 시작 코드 가져오기

이 자습서에서는 간단한 버전의 PhotoLab 샘플부터 시작합니다.

  1. 샘플에 대한 GitHub 페이지로 이동합니다. https://github.com/Microsoft/Windows-appsample-photo-lab.

  2. 다음으로 샘플을 복제하거나 다운로드해야 합니다. 클론 또는 다운로드 버튼을 선택합니다. 하위 메뉴가 나타납니다. PhotoLab 샘플의 GitHub 페이지 복제 또는 다운로드 메뉴

    GitHub에 익숙하지 않은 경우:

    a. ZIP 다운로드 선택하고 파일을 로컬로 저장합니다. 필요한 모든 프로젝트 파일이 포함된 .zip 파일을 다운로드합니다.

    b. 파일을 추출합니다. 파일 탐색기를 사용하여 방금 다운로드한 .zip 파일의 위치로 이동한 후, 파일을 오른쪽 클릭하고 모두 추출...를 선택합니다.

    다. 샘플의 로컬 복사본을 찾아보고, Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding 디렉터리로 이동하십시오.

    GitHub에 익숙한 경우:

    a. 리포지토리의 주 분기를 로컬로 복제합니다.

    b. Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding 디렉터리로 이동합니다.

  3. Photolab.sln 두 번 클릭하여 Visual Studio에서 솔루션을 엽니다.

1부: 자리 표시자 바꾸기

여기서는 데이터 템플릿 XAML에서 일회성 바인딩을 만들어 자리 표시자 콘텐츠 대신 실제 이미지 및 이미지 메타데이터를 표시합니다.

일회성 바인딩은 읽기 전용이고, 데이터를 교환하지 않는 데 사용됩니다. 즉, 고성능이며 쉽게 만들 수 있으므로 GridViewListView 컨트롤에 큰 데이터 집합을 표시할 수 있습니다.

자리 표시자를 일회성 바인딩으로 바꿉니다.

  1. xaml-basics-starting-points\data-binding 폴더를 열고 Visual Studio에서 PhotoLab.sln 파일을 시작합니다.

  2. Solution Platform Arm이 아닌 x86 또는 x64로 설정되어 있는지 확인한 다음 앱을 실행합니다. 바인딩을 추가하기 전에 UI 자리 표시기로 앱의 상태를 보여줍니다.

    자리 표시자 이미지 및 텍스트 사용하여 실행 중인 앱

  3. MainPage.xaml을 열고 DataTemplate라는 이름의 을 찾습니다. 데이터 바인딩을 사용하도록 이 템플릿을 업데이트합니다.

    이전:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
    

    x:Key 값은 데이터를 표시하기 위해 ImageGridView이 이 템플릿을 선택할 때 사용됩니다.

  4. 템플릿에 x:DataType 값을 추가합니다.

    후:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
    

    x:DataType은 이 템플릿이 어떤 유형을 위한 것인지 나타냅니다. 이 경우 ImageFileInfo 클래스에 대한 템플릿입니다(여기서 local: 파일의 위쪽에 있는 xmlns 선언에 정의된 대로 로컬 네임스페이스를 나타낸다).

    x:DataType 다음에 설명된 대로 데이터 템플릿에서 x:Bind 식을 사용할 때 필요합니다.

  5. DataTemplate에서 Image로 명명된 ItemImage 요소를 찾아서 보여진 대로 Source 값을 바꿉니다.

    이전:

    <Image x:Name="ItemImage"
           Source="/Assets/StoreLogo.png"
           Stretch="Uniform" />
    

    후:

    <Image x:Name="ItemImage"
           Source="{x:Bind ImageSource}"
           Stretch="Uniform" />
    

    x:Name은 XAML 요소를 식별하여, 이를 XAML의 다른 부분이나 코드 비하인드에서 참조할 수 있도록 합니다.

    x:Bind 식은 데이터 개체 속성에서 값을 가져오면 UI 속성에 값을 제공합니다. 템플릿에서 표시된 속성은 x:DataType으로 설정된 항목의 속성입니다. 따라서 이 경우 데이터 원본은 ImageFileInfo.ImageSource 속성입니다.

    비고

    또한 x:Bind 값을 사용하면 편집기에서 데이터 형식에 대해 알 수 있으므로 x:Bind 식에서 속성 이름을 입력하는 대신 IntelliSense를 사용할 수 있습니다. 방금 붙여넣은 코드에서 사용해 보세요. x:Bind 바로 뒤에 커서를 놓고 스페이스바를 눌러 바인딩할 수 있는 속성 목록을 확인합니다.

  6. 다른 UI 컨트롤의 값을 같은 방식으로 바꿉니다. 복사/붙여넣기 대신 IntelliSense를 사용하여 이 작업을 수행해 보세요.)

    이전:

    <TextBlock Text="Placeholder" ... />
    <StackPanel ... >
        <TextBlock Text="PNG file" ... />
        <TextBlock Text="50 x 50" ... />
    </StackPanel>
    <muxc:RatingControl Value="3" ... />
    

    후:

    <TextBlock Text="{x:Bind ImageTitle}" ... />
    <StackPanel ... >
        <TextBlock Text="{x:Bind ImageFileType}" ... />
        <TextBlock Text="{x:Bind ImageDimensions}" ... />
    </StackPanel>
    <muxc:RatingControl Value="{x:Bind ImageRating}" ... />
    

앱을 실행하여 지금까지의 모양을 확인합니다. 더 이상 자리 표시자가 없습니다! 우리는 좋은 시작에 있어.

자리 표시자 대신 실제 이미지 및 텍스트로 앱 실행

비고

더 실험하고 싶다면, 데이터 템플릿에 새 TextBlock을 추가하고 x:Bind IntelliSense 기능을 사용하여 표시할 속성을 선택해 보세요.

여기서는 XAML 페이지에 한 번만 바인딩을 만들어 갤러리 보기를 이미지 컬렉션에 연결하고, 기존에 코드 뒤에서 이 작업을 수행하던 절차 코드를 대체합니다. 또한 삭제 단추를 만들어 컬렉션에서 이미지를 제거할 때 갤러리 보기가 어떻게 변경되는지 확인합니다. 동시에 기존 이벤트 처리기에서 제공하는 것보다 더 많은 유연성을 위해 이벤트를 이벤트 처리기에 바인딩하는 방법을 알아봅니다.

지금까지 다루는 모든 바인딩은 데이터 템플릿 내에 있으며 x:DataType 값으로 표시된 클래스의 속성을 참조합니다. 페이지의 나머지 XAML은 어떻습니까?

데이터 템플릿 외부의 x:Bind 표현식은 항상 페이지 자체에 바인딩됩니다. 페이지에 있는 다른 UI 컨트롤의 사용자 지정 특성 및 속성을 포함하여 코드 비하인드 또는 XAML에서 선언하는 모든 항목을 참조할 수 있습니다(단, x:Name 값이 있는 경우).

PhotoLab 샘플에서 이와 같은 바인딩의 한 가지 용도는 코드 비하인드에서 수행하는 대신에 주 GridView 컨트롤을 이미지 컬렉션에 직접 연결하는 것입니다. 나중에 다른 예제가 표시됩니다.

이미지 컬렉션에 기본 GridView 컨트롤 바인딩

  1. MainPage.xaml.cs에서 GetItemsAsync 메서드를 찾아 ItemsSource을 설정하는 코드를 제거하세요.

    이전:

    ImageGridView.ItemsSource = Images;
    

    후:

    // Replaced with XAML binding:
    // ImageGridView.ItemsSource = Images;
    
  2. MainPage.xaml에서 GridView이라는 ImageGridView을 찾아 ItemsSource 특성을 추가합니다. 값으로 코드 비하인드에서 구현된 x:Bind 속성을 참조하는 Images 식을 사용합니다.

    이전:

    <GridView x:Name="ImageGridView"
    

    후:

    <GridView x:Name="ImageGridView"
              ItemsSource="{x:Bind Images}"
    

    Images 속성은 ObservableCollection<ImageFileInfo>형식이므로 GridView 표시되는 개별 항목은 ImageFileInfo형식입니다. 이는 1부에 설명된 x:DataType 값과 일치합니다.

지금까지 살펴본 모든 바인딩은 일반 x:Bind 식의 기본 동작인 일회성 읽기 전용 바인딩입니다. 데이터는 초기화 시에만 로드되므로 고성능 바인딩이 가능하며, 대용량 데이터 집합의 복잡한 여러 보기를 지원하기에 완벽합니다.

방금 추가한 ItemsSource 바인딩조차도 흔들리지 않는 속성 값에 대한 일회성 읽기 전용 바인딩이지만 여기서는 중요한 차이점이 있습니다. Images 속성의 변경되지 않는 값은 컬렉션의 특정 인스턴스로, 여기에 표시된 대로 한 번 초기화됩니다.

private ObservableCollection<ImageFileInfo> Images { get; }
    = new ObservableCollection<ImageFileInfo>();

속성 값은 변경되지 않지만 속성이 형식이므로 컬렉션의 내용이 변경될 수 있으며 바인딩은 변경 내용을 자동으로 확인하고 UI를 업데이트합니다.

이를 테스트하기 위해 현재 선택한 이미지를 삭제하는 단추를 일시적으로 추가하겠습니다. 이미지를 선택하면 세부 정보 페이지로 이동하므로 이 단추는 최종 버전에 없습니다. 그러나 ObservableCollection<T> 메서드 호출을 통해 페이지 생성자에서 XAML이 초기화되지만 InitializeComponent 컬렉션은 Images 메서드의 뒷부분에서 채워지므로 최종 PhotoLab 샘플에서는 GetItemsAsync 동작이 여전히 중요합니다.

삭제 단추 추가

  1. MainPage.xaml에서 MainCommandBar라는 CommandBar 찾아 확대/축소 단추 앞에 새 단추를 추가합니다. (확대/축소 컨트롤은 아직 작동하지 않습니다. 자습서의 다음 부분에서 해당 항목을 연결합니다.)

    <AppBarButton Icon="Delete"
                  Label="Delete selected image"
                  Click="{x:Bind DeleteSelectedImage}" />
    

    XAML에 이미 익숙한 경우 이 Click 값이 비정상적으로 보일 수 있습니다. 이전 버전의 XAML에서는 일반적으로 이벤트 보낸 사람 및 이벤트별 인수 개체에 대한 매개 변수를 포함하여 특정 이벤트 처리기 서명이 있는 메서드로 설정해야 했습니다. 이벤트 인수가 필요한 경우에도 이 기술을 사용할 수 있지만 x:Bind사용하면 다른 메서드에도 연결할 수 있습니다. 예를 들어 이벤트 데이터가 필요하지 않은 경우 여기서처럼 매개 변수가 없는 메서드에 연결할 수 있습니다.

  2. MainPage.xaml.cs DeleteSelectedImage 메서드를 추가합니다.

    private void DeleteSelectedImage() =>
        Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
    

    이 메서드는 선택한 이미지를 Images 컬렉션에서 삭제하기만 하면 됩니다.

이제 앱을 실행하고 단추를 사용하여 몇 가지 이미지를 삭제합니다. 보듯이 데이터 바인딩 및 ObservableCollection<T> 형식 덕분에 UI가 자동으로 업데이트됩니다.

비고

이 코드는 실행 중인 앱의 ImageFileInfo 컬렉션에서 Images 인스턴스만 삭제합니다. 컴퓨터에서 이미지 파일을 삭제하지 않습니다.

3부: 확대/축소 슬라이더 설정

이 부분에서는 데이터 템플릿의 컨트롤에서 템플릿 외부에 있는 확대/축소 슬라이더로 단방향 바인딩을 만듭니다. 또한 TextBlock.TextImage.Source같은 가장 명백한 속성뿐만 아니라 많은 컨트롤 속성과 함께 데이터 바인딩을 사용할 수 있다는 것도 알아봅니다.

이미지 데이터 템플릿을 확대/축소 슬라이더에 바인딩

  • DataTemplate 명명된 ImageGridView_DefaultItemTemplate 찾아 템플릿 맨 위에 있는 **Height** 컨트롤의 WidthGrid 값을 바꿉다.

    이전

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="200"
              Width="200"
              Margin="{StaticResource LargeItemMargin}">
    

    이후

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
              Width="{Binding Value, ElementName=ZoomSlider}"
              Margin="{StaticResource LargeItemMargin}">
    

이러한 식이 Binding 식이고 x:Bind 식이 아니라는 것을 알아차렸나요? 이는 데이터 바인딩을 수행하는 이전 방식이며 대부분 사용되지 않습니다. x:BindBinding이 하는 거의 모든 일을 하고, 그 이상을 합니다. 그러나 데이터 템플릿에서 x:Bind 사용하면 x:DataType 값에 선언된 형식에 바인딩됩니다. 그렇다면 템플릿의 요소를 페이지 XAML 또는 코드 비하인드의 항목에 어떻게 바인딩할 수 있을까요? 이전 스타일 Binding 식을 사용해야 합니다.

Binding 식은 x:DataType 값을 인식하지 못하지만 이러한 Binding 식에는 거의 동일한 방식으로 작동하는 ElementName 값이 있습니다. 지정된 요소의 속성에 대한 바인딩을 나타내는 Value이 그 페이지의 해당 x:Name 값을 가진 요소와 연결된다는 것을 바인딩 엔진에 알려줍니다. 코드 숨김의 속성에 바인딩하려는 경우 {Binding MyCodeBehindProperty, ElementName=page} XAML의 page 요소에 설정된 x:Name 값을 참조하는 Page 표시됩니다.

비고

기본적으로 Binding 식은방식입니다. 즉, 바인딩된 속성 값이 변경되면 자동으로 UI가 업데이트됩니다.

반면에 x:Bind의 기본값은 한 번-로, 바운드 속성에 대한 모든 변경 내용을 무시합니다. 이는 가장 고성능 옵션이며 대부분의 바인딩은 정적 읽기 전용 데이터에 있기 때문에 기본값입니다.

여기서 교훈은 값이 변경될 수 있는 속성과 함께 x:Bind을 사용할 때, 반드시 Mode=OneWay 또는 Mode=TwoWay를 추가해야 한다는 것입니다. 이 예제는 다음 섹션에서 확인할 수 있습니다.

앱을 실행하고 슬라이더를 사용하여 이미지 템플릿 차원을 변경합니다. 당신이 볼 수 있듯이, 효과는 많은 코드없이 매우 강력하다.

표시된 확대/축소 슬라이더가 있는 실행 중인 앱

비고

도전 과제로서, 다른 UI 속성을 확대/축소 슬라이더 Value 속성에 바인딩하거나, 확대/축소 슬라이더 후에 추가한 다른 슬라이더에 바인딩해 보세요. 예를 들어, FontSizeTitleTextBlock 속성을 기본값이 24인 새 슬라이더에 바인딩할 수 있습니다. 적절한 최소값과 최대값을 설정해야 합니다.

4부: 확대/축소 환경 개선

이 부분에서는 코드 비하인드에 사용자 지정 ItemSize 속성을 추가하고, 이미지 템플릿에서 새 속성으로 단방향 바인딩을 만듭니다. ItemSize 값은 확대/축소 슬라이더 및 창 크기, 화면에 맞추기 토글과 같은 기타 요소에 의해 업데이트되어 보다 세련된 사용자 경험을 제공합니다.

기본 제공 컨트롤 속성과 달리 사용자 지정 속성은 단방향 및 양방향 바인딩을 사용하더라도 UI를 자동으로 업데이트하지 않습니다. 1-시간 바인딩에서 잘 작동하지만 속성 변경 내용이 실제로 UI에 표시되도록 하려면 몇 가지 작업을 수행해야 합니다.

UI를 업데이트하도록 ItemSize 속성을 만듭니다.

  1. MainPage.xaml.cs에서 MainPage 클래스가 INotifyPropertyChanged 인터페이스를 구현할 수 있도록 서명을 변경합니다.

    이전:

    public sealed partial class MainPage : Page
    

    후:

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    

    그러면 바인딩 시스템에 MainPage UI를 업데이트하기 위해 바인딩이 수신 대기할 수 있는 PropertyChanged 이벤트(다음에 추가됨)가 있음을 알릴 수 있습니다.

  2. PropertyChanged 클래스에 MainPage 이벤트를 추가합니다.

    public event PropertyChangedEventHandler PropertyChanged;
    

    이 이벤트는 INotifyPropertyChanged 인터페이스에 필요한 전체 구현을 제공합니다. 그러나 영향을 미치려면 사용자 지정 속성에서 이벤트를 명시적으로 발생시켜야 합니다.

  3. ItemSize 속성을 추가하고, setter에서 PropertyChanged 이벤트를 발생시킵니다.

    public double ItemSize
    {
        get => _itemSize;
        set
        {
            if (_itemSize != value)
            {
                _itemSize = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemSize)));
            }
        }
    }
    private double _itemSize;
    

    ItemSize 속성은 프라이빗 _itemSize 필드의 값을 노출합니다. 이와 같은 지원 필드를 사용하면 잠재적으로 불필요한 PropertyChanged 이벤트가 발생하기 전에 새 값이 이전 값과 같은지 확인할 수 있습니다.

    이벤트 자체는 Invoke 메서드에 의해 트리거됩니다. 물음표는 PropertyChanged 이벤트가 null인지, 즉 이벤트 처리기가 아직 추가되었는지 여부를 확인합니다. 모든 단방향 또는 양방향 바인딩은 백그라운드에서 이벤트 처리기를 추가하지만, 듣는 사람이 없으면 그 이후로는 아무 일도 일어나지 않습니다. PropertyChanged이(가) null이 아닌 경우, Invoke이 이벤트 소스(페이지 자체를 나타내는 this 키워드로 표시됨)에 대한 참조와 속성 이름을 나타내는 이벤트 인수 객체를 사용하여 호출됩니다. 이 정보를 통해 ItemSize 속성에 대한 단방향 또는 양방향 바인딩이 변경 사항을 통지받아 바인딩된 UI를 업데이트할 수 있습니다.

  4. MainPage.xaml에서 DataTemplate라는 이름의 ImageGridView_DefaultItemTemplate을 찾아 템플릿 맨 위에 있는 Height 컨트롤의 WidthGrid 값을 바꿉니다. (이 자습서의 이전 부분에서 제어-제어 바인딩을 한 경우, 유일한 변경 내용은 ValueItemSize로, ZoomSliderpage으로 바꾸는 것입니다. 이 작업을 HeightWidth에도 반드시 수행해야 합니다.)

    이전

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
            Width="{Binding Value, ElementName=ZoomSlider}"
            Margin="{StaticResource LargeItemMargin}">
    

    이후

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding ItemSize, ElementName=page}"
              Width="{Binding ItemSize, ElementName=page}"
              Margin="{StaticResource LargeItemMargin}">
    

이제 UI가 ItemSize 변경에 응답할 수 있으므로 실제로 몇 가지 변경을 수행해야 합니다. 앞에서 설명한 것처럼 ItemSize 값은 다양한 UI 컨트롤의 현재 상태에서 계산되지만 해당 컨트롤이 상태를 변경할 때마다 계산을 수행해야 합니다. 이렇게 하기 위해서, 특정 UI 변경이 ItemSize을 업데이트하는 도우미 메서드를 호출하도록 이벤트 바인딩을 사용합니다.

ItemSize 속성 값 업데이트

  1. MainPage.xaml.cs DetermineItemSize 메서드를 추가합니다.

    private void DetermineItemSize()
    {
        if (FitScreenToggle != null
            && FitScreenToggle.IsOn == true
            && ImageGridView != null
            && ZoomSlider != null)
        {
            // The 'margins' value represents the total of the margins around the
            // image in the grid item. 8 from the ItemTemplate root grid + 8 from
            // the ItemContainerStyle * (Right + Left). If those values change,
            // this value needs to be updated to match.
            int margins = (int)this.Resources["LargeItemMarginValue"] * 4;
            double gridWidth = ImageGridView.ActualWidth -
                (int)this.Resources["DefaultWindowSidePaddingValue"];
            double ItemWidth = ZoomSlider.Value + margins;
            // We need at least 1 column.
            int columns = (int)Math.Max(gridWidth / ItemWidth, 1);
    
            // Adjust the available grid width to account for margins around each item.
            double adjustedGridWidth = gridWidth - (columns * margins);
    
            ItemSize = (adjustedGridWidth / columns);
        }
        else
        {
            ItemSize = ZoomSlider.Value;
        }
    }
    
  2. MainPage.xaml에서 파일의 맨 위로 이동하고 SizeChanged 요소에 Page 이벤트 바인딩을 추가합니다.

    이전:

    <Page x:Name="page"
    

    후:

    <Page x:Name="page"
          SizeChanged="{x:Bind DetermineItemSize}"
    
  3. Slider 섹션에서 ZoomSlider이라는 이름의 Page.Resources를 찾아 ValueChanged 이벤트 바인딩을 추가하세요.

    이전:

    <Slider x:Name="ZoomSlider"
    

    후:

    <Slider x:Name="ZoomSlider"
            ValueChanged="{x:Bind DetermineItemSize}"
    
  4. ToggleSwitch이라는 이름의 FitScreenToggle을 찾아 Toggled 이벤트 바인딩을 추가합니다.

    이전:

    <ToggleSwitch x:Name="FitScreenToggle"
    

    후:

    <ToggleSwitch x:Name="FitScreenToggle"
                  Toggled="{x:Bind DetermineItemSize}"
    

앱을 실행하고 확대/축소 슬라이더와 화면에 맞추기 토글을 사용하여 이미지 템플릿의 크기를 변경합니다. 볼 수 있듯이 최신 변경 내용을 사용하면 코드를 잘 구성하면서 더욱 구체화된 확대/축소/크기 조정 환경을 사용할 수 있습니다.

화면에 맞도록 설정된 을 실행 중인 앱

비고

챌린지의 경우 TextBlockZoomSlider 추가하고 Text 속성을 ItemSize 속성에 바인딩해 보세요. 데이터 템플릿에 없기 때문에 이전 x:Bind 바인딩처럼 Binding 대신 ItemSize 사용할 수 있습니다.

5부: 사용자 편집 허용

여기서는 사용자가 이미지 제목, 등급 및 다양한 시각 효과를 포함한 값을 업데이트할 수 있도록 양방향 바인딩을 만듭니다.

이를 위해 단일 이미지 뷰어, 확대/축소 컨트롤 및 편집 UI를 제공하는 기존 DetailPage을 업데이트할 것입니다.

먼저 우선, 사용자가 갤러리에서 이미지를 클릭하면 앱이 DetailPage로 이동하도록 연결해야 합니다.

DetailPage를 첨부하십시오

  1. MainPage.xaml에서 GridView이라는 이름의 ImageGridView을 찾습니다. 항목을 클릭할 수 있도록 하려면 IsItemClickEnabledTrue 설정하고 ItemClick 이벤트 처리기를 추가합니다.

    팁 (조언)

    복사/붙여넣기 대신 아래 변경 내용을 입력하면 "<새 이벤트 처리기>"라는 IntelliSense 팝업이 표시됩니다. Tab 키를 누르면 기본 메서드 처리기 이름으로 값이 채워지고 다음 단계에 표시된 메서드가 자동으로 스텁됩니다. 그런 다음 F12 키를 눌러 코드 비하인드의 메서드로 이동할 수 있습니다.

    이전:

    <GridView x:Name="ImageGridView">
    

    후:

    <GridView x:Name="ImageGridView"
              IsItemClickEnabled="True"
              ItemClick="ImageGridView_ItemClick">
    

    비고

    여기서는 x:Bind 식 대신 기존 이벤트 처리기를 사용합니다. 이는 다음에 표시된 것처럼 이벤트 데이터를 확인해야 하기 때문입니다.

  2. MainPage.xaml.cs에 이벤트 처리기를 추가하거나, 지난 단계에서 팁을 사용한 경우에는 채워놓습니다.

    private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e)
    {
        this.Frame.Navigate(typeof(DetailPage), e.ClickedItem);
    }
    

    이 메서드는 단순히 세부 정보 페이지로 이동하여 클릭된 항목을 전달합니다. 이 항목은 ImageFileInfo 페이지 초기화에 사용되는 개체입니다. 이 자습서에서는 해당 메서드를 구현할 필요가 없지만 이 메서드가 수행하는 작업을 살펴볼 수 있습니다.

  3. (선택 사항) 현재 선택한 이미지에서 작동하는 이전 플레이 포인트에서 추가한 컨트롤을 삭제하거나 주석 처리합니다. 이러한 이미지를 유지해도 아무 것도 손상되지 않지만, 이제 세부 정보 페이지로 이동하지 않고 이미지를 선택하는 것이 훨씬 더 어려워집니다.

이제 두 페이지를 연결했으므로 앱을 실행하고 둘러보세요. 값을 변경하려고 할 때 응답하지 않는 편집 창의 컨트롤을 제외한 모든 항목이 작동합니다.

볼 수 있듯이 제목 텍스트 상자에 제목이 표시되고 변경 내용을 입력할 수 있습니다. 변경 내용을 커밋하려면 포커스를 다른 컨트롤로 변경해야 하지만 화면의 왼쪽 위 모서리에 있는 제목은 아직 업데이트되지 않습니다.

1부에서 설명한 일반 x:Bind 식을 사용하여 모든 컨트롤이 이미 바인딩되어 있습니다. 기억하신다면, 이는 모두 일회성 바인딩이라서 값 변경 내용이 등록되지 않는 것을 설명합니다. 이 문제를 해결하려면 양방향 바인딩으로 변환하기만 하면 됩니다.

편집 컨트롤을 대화형으로 만들기

  1. DetailPage.xaml에서 TextBlock으로 명명된 TitleTextBlock와 그 뒤의 RatingControl 컨트롤을 찾아 그들의 x:Bind 식을 Mode=TwoWay을 포함하도록 업데이트합니다.

    이전:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating}"
                            ... >
    

    후:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle, Mode=TwoWay}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating, Mode=TwoWay}"
                            ... >
    
  2. 등급 제어 후에 오는 모든 효과 슬라이더에 대해 동일한 작업을 수행합니다.

    <Slider Header="Exposure"    ... Value="{x:Bind item.Exposure, Mode=TwoWay}" ...
    <Slider Header="Temperature" ... Value="{x:Bind item.Temperature, Mode=TwoWay}" ...
    <Slider Header="Tint"        ... Value="{x:Bind item.Tint, Mode=TwoWay}" ...
    <Slider Header="Contrast"    ... Value="{x:Bind item.Contrast, Mode=TwoWay}" ...
    <Slider Header="Saturation"  ... Value="{x:Bind item.Saturation, Mode=TwoWay}" ...
    <Slider Header="Blur"        ... Value="{x:Bind item.Blur, Mode=TwoWay}" ...
    

양방향 모드는 예상대로 양쪽에 변경 내용이 있을 때마다 데이터가 양방향으로 이동한다는 것을 의미합니다.

앞에서 설명한 단방향 바인딩과 마찬가지로 이러한 양방향 바인딩은 이제 INotifyPropertyChanged 클래스의 ImageFileInfo 구현 덕분에 바인딩된 속성이 변경될 때마다 UI를 업데이트합니다. 그러나 양방향 바인딩을 사용하면 사용자가 컨트롤과 상호 작용할 때마다 값이 UI에서 바인딩된 속성으로 이동합니다. XAML 쪽에는 더 이상 필요하지 않습니다.

앱을 실행하고 편집 컨트롤을 시도합니다. 볼 수 있듯이 변경하면 이미지 값에 영향을 줍니다. 그러면 기본 페이지로 다시 이동할 때 이러한 변경 내용이 유지됩니다.

6부: 함수 바인딩을 통해 값 서식 지정

마지막 문제 중 하나가 남아 있습니다. 효과 슬라이더를 이동할 때 옆에 있는 레이블은 변경되지 않습니다.

기본 레이블 값이 있는 효과 슬라이더

이 자습서의 마지막 부분은 표시할 슬라이더 값의 서식을 지정하는 바인딩을 추가하는 것입니다.

효과 슬라이더 레이블을 바인딩하고 표시할 값의 서식을 지정합니다.

  1. TextBlock 슬라이더 뒤의 Exposure 찾아 Text 값을 여기에 표시된 바인딩 식으로 바꿉다.

    이전:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="0.00" />
    

    후:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="{x:Bind item.Exposure.ToString('N', culture), Mode=OneWay}" />
    

    메서드의 반환 값에 바인딩하기 때문에 함수 바인딩이라고 합니다. 데이터 템플릿에 있는 경우, 페이지의 백엔드 코드 또는 x:DataType 유형을 통해 메서드에 접근할 수 있어야 합니다. 이 경우 메서드는 페이지의 항목 속성을 통해 접근한 후, 항목의 ToString 속성을 통해 접근할 수 있는 익숙한 .NET Exposure 메서드입니다. 연결 체인에 깊이 중첩된 메서드 및 속성에 바인딩하는 방법을 보여 줍니다.

    함수 바인딩은 다른 바인딩 원본을 메서드 인수로 전달할 수 있으므로 표시할 값의 서식을 지정하는 이상적인 방법이며 바인딩 식은 단방향 모드에서 예상대로 해당 값의 변경 내용을 수신 대기합니다. 이 예제에서 문화권 인수는 코드 비하인드에서 구현된 변경 불가능한 필드에 대한 참조이지만, PropertyChanged 이벤트를 발생시키는 속성일 수도 있습니다. 이 경우 속성 값이 변경되면, x:Bind 식이 새 값을 사용하여 ToString을 호출하고, 그 결과로 UI를 업데이트하게 됩니다.

  2. 다른 효과 슬라이더에 레이블을 지정하는 TextBlock대해 동일한 작업을 수행합니다.

    <Slider Header="Temperature" ... />
    <TextBlock ... Text="{x:Bind item.Temperature.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Tint" ... />
    <TextBlock ... Text="{x:Bind item.Tint.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Contrast" ... />
    <TextBlock ... Text="{x:Bind item.Contrast.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Saturation" ... />
    <TextBlock ... Text="{x:Bind item.Saturation.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Blur" ... />
    <TextBlock ... Text="{x:Bind item.Blur.ToString('N', culture), Mode=OneWay}" />
    

이제 앱을 실행하면 슬라이더 레이블을 포함하여 모든 것이 작동합니다.

효과 슬라이더와 작업 레이블

결론

이 자습서에서는 데이터 바인딩의 맛을 제공하고 사용 가능한 몇 가지 기능을 보여 주었습니다. 마무리하기 전에 한 가지 주의 사항: 모든 항목을 바인딩할 수 있는 것은 아니며, 연결하려는 값이 바인딩하려는 속성과 호환되지 않는 경우도 있습니다. 바인딩에는 많은 유연성이 있지만 모든 상황에서는 작동하지 않습니다.

바인딩으로 해결되지 않는 문제의 한 가지 예는 컨트롤에 세부 정보 페이지 확대/축소 기능과 같이 바인딩할 적절한 속성이 없는 경우입니다. 이 확대/축소 슬라이더는 이미지를 표시하는 ScrollViewer과 상호작용해야 하지만 ScrollViewerChangeView 메서드를 통해서만 업데이트할 수 있습니다. 이 경우 기존 이벤트 처리기를 사용하여 ScrollViewer 및 확대/축소 슬라이더를 동기화 상태로 유지합니다. 자세한 내용은 ZoomSlider_ValueChangedMainImageScroll_ViewChangedDetailPage 메서드를 참조하세요.

그럼에도 불구하고 바인딩은 코드를 단순화하고 UI 논리를 데이터 논리와 별도로 유지하는 강력하고 유연한 방법입니다. 이렇게 하면 다른 쪽에서 버그를 도입할 위험을 줄이면서 이 나누기의 양쪽을 훨씬 쉽게 조정할 수 있습니다.

UI 및 데이터 분리의 한 가지 예는 ImageFileInfo.ImageTitle 속성입니다. 값이 필드가 아닌 파일 메타데이터(ImageRating 형식을 통해 노출됨)에 저장되므로 이 속성(및 ItemSize 속성)은 4부에서 만든 ImageProperties 속성과 약간 다릅니다. 또한 ImageTitle 파일 메타데이터에 제목이 없으면 ImageName 값(파일 이름으로 설정)을 반환합니다.

public string ImageTitle
{
    get => String.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
    set
    {
        if (ImageProperties.Title != value)
        {
            ImageProperties.Title = value;
            var ignoreResult = ImageProperties.SavePropertiesAsync();
            OnPropertyChanged();
        }
    }
}

보듯이 setter는 ImageProperties.Title 속성을 업데이트한 다음 SavePropertiesAsync 호출하여 파일에 새 값을 씁니다. (비동기 메서드이지만 속성에 await 키워드를 사용할 수 없으며 속성 getter 및 setter가 즉시 완료되어야 하므로 원하지 않을 것입니다. 따라서 대신 메서드를 호출하고 반환하는 Task 개체를 무시합니다.)

더 나아가기

이제 이 랩을 완료했으므로 스스로 문제를 해결할 수 있을 만큼 충분한 지식을 갖추게 되었습니다.

앞에서 알 수 있듯이 세부 정보 페이지에서 확대/축소 수준을 변경하면 뒤로 이동한 다음 동일한 이미지를 다시 선택하면 자동으로 다시 설정됩니다. 각 이미지의 확대/축소 수준을 개별적으로 유지하고 복원하는 방법을 알아낼 수 있나요? 행운을 빌어!

이 자습서에는 필요한 모든 정보가 포함되어 있지만, 더 많은 지침이 필요하다면 데이터 바인딩 문서를 클릭 한 번으로 쉽게 찾을 수 있습니다. 여기에서 시작합니다.