Xamarin.Forms 바인딩 모드Xamarin.Forms Binding Mode

샘플 다운로드 샘플 다운로드Download Sample Download the sample

이전 문서에서는 Alternative Code Binding(대체 코드 바인딩)과 Alternative XAML Binding(대체 XAML 바인딩) 페이지에 Scale 속성이 있는 LabelSliderValue에 바인딩되는 것을 설명했습니다.In the previous article, the Alternative Code Binding and Alternative XAML Binding pages featured a Label with its Scale property bound to the Value property of a Slider. Slider 초기 값이 0이라서 LabelScale 속성이 1이 아닌 0으로 설정되어 Label이 사라졌습니다.Because the Slider initial value is 0, this caused the Scale property of the Label to be set to 0 rather than 1, and the Label disappeared.

DataBindingDemos 샘플의 Reverse Binding(역방향 바인딩) 페이지는 이전 문서의 프로그램과 유사하며, 데이터 바인딩이 Label에 정의되지 않고 Slider에 정의되는 것만 다릅니다.In the DataBindingDemos sample, the Reverse Binding page is similar to the programs in the previous article, except that the data binding is defined on the Slider rather than on the Label:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.ReverseBindingPage"
             Title="Reverse Binding">
    <StackLayout Padding="10, 0">

        <Label x:Name="label"
               Text="TEXT"
               FontSize="80"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Slider x:Name="slider"
                VerticalOptions="CenterAndExpand"
                Value="{Binding Source={x:Reference label},
                                Path=Opacity}" />
    </StackLayout>
</ContentPage>

처음에는 반대 방향처럼 보일 수 있습니다. 이제 Label은 데이터 바인딩 소스이고 Slider는 대상입니다.At first, this might seem backwards: Now the Label is the data-binding source, and the Slider is the target. 바인딩은 LabelOpacity 속성을 참조하며, 기본값은 1일입니다.The binding references the Opacity property of the Label, which has a default value of 1.

예상대로 SliderLabel의 초기 Opacity 값에서 1로 초기화됩니다.As you might expect, the Slider is initialized to the value 1 from the initial Opacity value of Label. 왼쪽의 iOS 스크린샷이 이 경우를 보여줍니다.This is shown in the iOS screenshot on the left:

역방향 바인딩Reverse Binding

하지만 놀랍게도 Slider는 Android와 UWP 스크린샷에 표시된 것처럼 계속 작동합니다.But you might be surprised that the Slider continues to work, as the Android and UWP screenshots demonstrate. 이를 통해 알 수 있는 것은 데이터 바인딩은 Label보다는 Slider가 바인딩 대상일 때 더 잘 작동하며 그 이유는 초기화가 예상대로 작동하기 때문이라는 점입니다.This seems to suggest that the data binding works better when the Slider is the binding target rather than the Label because the initialization works like we might expect.

Reverse Binding(역방향 바인딩) 샘플과 이전 샘플의 차이는 바인딩 모드입니다.The difference between the Reverse Binding sample and the earlier samples involves the binding mode.

기본 바인딩 모드The Default Binding Mode

바인딩 모드는 BindingMode 열거형의 멤버를 통해 지정됩니다.The binding mode is specified with a member of the BindingMode enumeration:

  • Default
  • TwoWay – 데이터가 원본과 대상 사이에서 양방향으로 이동합니다.TwoWay – data goes both ways between source and target
  • OneWay – 데이터가 원본에서 대상으로 이동합니다.OneWay – data goes from source to target
  • OneWayToSource – 데이터가 대상에서 원본으로 이동합니다.OneWayToSource – data goes from target to source
  • OneTimeBindingContext가 변경되는 경우에만 데이터가 원본에서 대상으로 이동합니다(Xamarin.Forms 3.0의 새로운 기능).OneTime – data goes from source to target, but only when the BindingContext changes (new with Xamarin.Forms 3.0)

바인딩할 수 있는 모든 속성에는 바인딩할 수 있는 속성이 생성될 때 설정되는 기본 바인딩 모드가 있으며, BindableProperty 개체의 DefaultBindingMode 속성에 제공됩니다.Every bindable property has a default binding mode that is set when the bindable property is created, and which is available from the DefaultBindingMode property of the BindableProperty object. 기본 바인딩 모드는 속성이 데이터 바인딩 대상일 때 적용되는 모드를 나타냅니다.This default binding mode indicates the mode in effect when that property is a data-binding target.

Rotation, ScaleOpacity와 같은 대부분의 속성에 대한 기본 바인딩 모드는 OneWay입니다.The default binding mode for most properties such as Rotation, Scale, and Opacity is OneWay. 이러한 속성이 데이터 바인딩 대상인 경우에는 대상 속성이 원본에서 설정됩니다.When these properties are data-binding targets, then the target property is set from the source.

하지만 SliderValue 속성에 대한 기본 바인딩 모드는 TwoWay입니다.However, the default binding mode for the Value property of Slider is TwoWay. 즉, Value 속성이 데이터 바인딩 대상이면 대상이 원본에서(일반적으로) 설정되지만 원본 역시 대상에서 설정됩니다.This means that when the Value property is a data-binding target, then the target is set from the source (as usual) but the source is also set from the target. 따라서 Slider가 초기 Opacity 값에서 설정될 수 있습니다.This is what allows the Slider to be set from the initial Opacity value.

양방향 바인딩은 무한 루프를 만드는 것처럼 보일 수도 있지만 그런 상황은 발생하지 않습니다.This two-way binding might seem to create an infinite loop, but that doesn't happen. 바인딩할 수 있는 속성은 속성이 실제로 변경되는 경우가 아니면 속성의 변화를 신호로 알리지 않습니다.Bindable properties do not signal a property change unless the property actually changes. 이를 통해 무한 루프가 방지됩니다.This prevents an infinite loop.

양방향 바인딩Two-Way Bindings

바인딩할 수 있는 대부분의 속성에는 OneWay라는 기본 바인딩 모드가 있지만 다음 속성의 기본 바인딩 모드는 TwoWay입니다.Most bindable properties have a default binding mode of OneWay but the following properties have a default binding mode of TwoWay:

  • DatePickerDate 속성Date property of DatePicker
  • Editor, Entry, SearchBar, EntryCellText 속성Text property of Editor, Entry, SearchBar, and EntryCell
  • ListViewIsRefreshing 속성IsRefreshing property of ListView
  • MultiPageSelectedItem 속성SelectedItem property of MultiPage
  • PickerSelectedIndexSelectedItem 속성SelectedIndex and SelectedItem properties of Picker
  • Slider, StepperValue 속성Value property of Slider and Stepper
  • SwitchIsToggled 속성IsToggled property of Switch
  • SwitchCellOn 속성On property of SwitchCell
  • TimePickerTime 속성Time property of TimePicker

특정 속성이 TwoWay로 정의되는 데에는 매우 합당한 이유가 있습니다.These particular properties are defined as TwoWay for a very good reason:

데이터 바인딩이 MVVM(Model-View-ViewModel) 애플리케이션 아키텍처에 사용되는 경우에는 ViewModel 클래스가 데이터 바인딩 소스이고 View(Slider와 같은 뷰로 구성됨)는 데이터 바인딩 대상이 됩니다.When data bindings are used with the Model-View-ViewModel (MVVM) application architecture, the ViewModel class is the data-binding source, and the View, which consists of views such as Slider, are data-binding targets. MVVM 바인딩은 이전 샘플의 바인딩보다 Reverse Binding(역방향 바인딩) 샘플과 더 유사합니다.MVVM bindings resemble the Reverse Binding sample more than the bindings in the previous samples. 페이지의 각 뷰를 ViewModel의 해당 속성 값으로 초기화할 가능성이 높지만 뷰의 변경 내용은 ViewModel 속성에도 영향을 미쳐야 합니다.It is very likely that you want each view on the page to be initialized with the value of the corresponding property in the ViewModel, but changes in the view should also affect the ViewModel property.

기본 바인딩 모드가 TwoWay인 속성이 MVVM 시나리오에서 가장 많이 사용되는 속성입니다.The properties with default binding modes of TwoWay are those properties most likely to be used in MVVM scenarios.

One-Way-to-Source 바인딩One-Way-to-Source Bindings

바인딩할 수 있는 읽기 전용 속성의 기본 바인딩 모드는 OneWayToSource입니다.Read-only bindable properties have a default binding mode of OneWayToSource. 기본 바인딩 모드로 OneWayToSource가 사용되는 바인딩할 수 있는 읽기/쓰기 속성은 하나뿐입니다.There is only one read/write bindable property that has a default binding mode of OneWayToSource:

  • ListViewSelectedItem 속성SelectedItem property of ListView

SelectedItem 속성에 바인딩을 하면 바인딩 소스가 설정되어야 하기 때문입니다.The rationale is that a binding on the SelectedItem property should result in setting the binding source. 이 문서의 뒷부분에 나오는 예제에서 이 동작이 재정의됩니다.An example later in this article overrides that behavior.

One-Time 바인딩One-Time Bindings

EntryIsTextPredictionEnabled 속성을 포함하여 몇 가지 속성은 OneTime의 기본 바인딩 모드가 있습니다.Several properties have a default binding mode of OneTime, including the IsTextPredictionEnabled property of Entry.

바인딩 모드가 OneTime인 대상 속성은 바인딩 컨텍스트가 변경되는 경우에만 업데이트됩니다.Target properties with a binding mode of OneTime are updated only when the binding context changes. 이러한 대상 속성의 바인딩은 원본 속성의 변경 내용을 모니터링할 필요가 없기 때문에 바인딩 인프라가 간소화됩니다.For bindings on these target properties, this simplifies the binding infrastructure because it is not necessary to monitor changes in the source properties.

ViewModels 및 Property-Change 알림ViewModels and Property-Change Notifications

Simple Color Selector(간단한 색 선택기) 페이지는 간단한 ViewModel 사용을 보여줍니다.The Simple Color Selector page demonstrates the use of a simple ViewModel. 데이터 바인딩을 사용하여 사용자가 색상, 채도 및 광도에 대한 세 가지 Slider 요소를 사용하여 색상을 선택할 수 있습니다.Data bindings allow the user to select a color using three Slider elements for the hue, saturation, and luminosity.

ViewModel은 데이터 바인딩 소스입니다.The ViewModel is the data-binding source. ViewModel 은 바인딩할 수 있는 속성을 정의하지는 않지만 속성 값이 변경되면 바인딩 인프라에 알릴 수 있는 알림 메커니즘을 구현합니다.The ViewModel does not define bindable properties, but it does implement a notification mechanism that allows the binding infrastructure to be notified when the value of a property changes. 이 알림 메커니즘은 INotifyPropertyChanged 인터페이스이며 PropertyChanged라는 단일 이벤트를 정의합니다.This notification mechanism is the INotifyPropertyChanged interface, which defines a single event named PropertyChanged. 공용 속성 중 하나의 값이 변경되면 이 인터페이스를 구현하는 클래스가 이벤트를 발생시킵니다.A class that implements this interface generally fires the event when one of its public properties changes value. 속성이 전혀 변경되지 않으면 이벤트가 실행될 필요가 없습니다.The event does not need to be fired if the property never changes. (INotifyPropertyChanged 인터페이스도 BindableObject에 의해 구현되며 PropertyChanged 이벤트는 바인딩할 수 있는 속성의 값이 변하면 실행됩니다.)(The INotifyPropertyChanged interface is also implemented by BindableObject and a PropertyChanged event is fired whenever a bindable property changes value.)

HslColorViewModel 클래스는 5개 속성을 정의합니다. Hue, Saturation, LuminosityColor 속성은 서로 관련됩니다.The HslColorViewModel class defines five properties: The Hue, Saturation, Luminosity, and Color properties are interrelated. 세 가지 색 구성 요소 중 하나라도 값이 변경되면 Color 속성이 다시 계산되고 네 가지 속성 모두에 대해 PropertyChanged 이벤트가 실행됩니다.When any one of the three color components changes value, the Color property is recalculated, and PropertyChanged events are fired for all four properties:

public class HslColorViewModel : INotifyPropertyChanged
{
    Color color;
    string name;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Hue
    {
        set
        {
            if (color.Hue != value)
            {
                Color = Color.FromHsla(value, color.Saturation, color.Luminosity);
            }
        }
        get
        {
            return color.Hue;
        }
    }

    public double Saturation
    {
        set
        {
            if (color.Saturation != value)
            {
                Color = Color.FromHsla(color.Hue, value, color.Luminosity);
            }
        }
        get
        {
            return color.Saturation;
        }
    }

    public double Luminosity
    {
        set
        {
            if (color.Luminosity != value)
            {
                Color = Color.FromHsla(color.Hue, color.Saturation, value);
            }
        }
        get
        {
            return color.Luminosity;
        }
    }

    public Color Color
    {
        set
        {
            if (color != value)
            {
                color = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Hue"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Saturation"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Luminosity"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));

                Name = NamedColor.GetNearestColorName(color);
            }
        }
        get
        {
            return color;
        }
    }

    public string Name
    {
        private set
        {
            if (name != value)
            {
                name = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
            }
        }
        get
        {
            return name;
        }
    }
}

Color 속성이 변경되면 NamedColor 클래스(DataBindingDemos 솔루션에도 포함되어 있음)의 정적 GetNearestColorName 메서드가 가장 가까이 명명된 색을 가져다가 Name 속성을 설정합니다.When the Color property changes, the static GetNearestColorName method in the NamedColor class (also included in the DataBindingDemos solution) obtains the closest named color and sets the Name property. Name 속성에는 비공개 set 접근자가 있기 때문에 클래스 외부에서는 설정할 수 없습니다.This Name property has a private set accessor, so it cannot be set from outside the class.

ViewModel이 바인딩 소스로 설정되면 바인딩 인프라는 PropertyChanged 이벤트에 처리기를 연결합니다.When a ViewModel is set as a binding source, the binding infrastructure attaches a handler to the PropertyChanged event. 이런 방식으로 속성에 대한 변경 내용을 바인딩에 알릴 수 있으며 그런 다음, 변경된 값에서 대상 속성을 설정할 수 있습니다.In this way, the binding can be notified of changes to the properties, and can then set the target properties from the changed values.

하지만 대상 속성(또는 대상 속성의 Binding 정의)에 OneTimeBindingMode가 있으면 바인딩 인프라가 PropertyChanged 이벤트에 대한 처리기를 연결할 필요가 없습니다.However, when a target property (or the Binding definition on a target property) has a BindingMode of OneTime, it is not necessary for the binding infrastructure to attach a handler on the PropertyChanged event. 대상 속성은 BindingContext가 변경되는 경우에만 업데이트됩니다. 원본 속성 자체가 변경되는 경우에는 업데이트되지 않습니다.The target property is updated only when the BindingContext changes and not when the source property itself changes.

Simple Color Selector(간단한 색 선택기) XAML 파일은 페이지의 리소스 사전에서 HslColorViewModel을 인스턴스화하고 Color 속성을 초기화합니다.The Simple Color Selector XAML file instantiates the HslColorViewModel in the page's resource dictionary and initializes the Color property. GridBindingContext 속성은 해당 리소스를 참조하는 StaticResource 바인딩 확장으로 설정됩니다.The BindingContext property of the Grid is set to a StaticResource binding extension to reference that resource:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.SimpleColorSelectorPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <local:HslColorViewModel x:Key="viewModel"
                                     Color="MediumTurquoise" />

            <Style TargetType="Slider">
                <Setter Property="VerticalOptions" Value="CenterAndExpand" />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>

    <Grid BindingContext="{StaticResource viewModel}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <BoxView Color="{Binding Color}"
                 Grid.Row="0" />

        <StackLayout Grid.Row="1"
                     Margin="10, 0">

            <Label Text="{Binding Name}"
                   HorizontalTextAlignment="Center" />

            <Slider Value="{Binding Hue}" />

            <Slider Value="{Binding Saturation}" />

            <Slider Value="{Binding Luminosity}" />
        </StackLayout>
    </Grid>
</ContentPage>

BoxView, Label 및 세 가지 Slider뷰는 Grid에서 바인딩 컨텍스트를 상속받습니다.The BoxView, Label, and three Slider views inherit the binding context from the Grid. 이러한 뷰는 모두 ViewModel의 원본 속성을 참조하는 바인딩 대상입니다.These views are all binding targets that reference source properties in the ViewModel. BoxViewColor속성과 LabelText 속성의 경우 데이터 바인딩은 OneWay입니다. 뷰의 속성은 ViewModel의 속성에서 설정됩니다.For the Color property of the BoxView, and the Text property of the Label, the data bindings are OneWay: The properties in the view are set from the properties in the ViewModel.

SliderValue 속성은 TwoWay입니다.The Value property of the Slider, however, is TwoWay. 이렇게 하면 각 Slider을 ViewModel에서 설정하고 각 Slider에서 ViewModel을 설정할 수 있습니다.This allows each Slider to be set from the ViewModel, and also for the ViewModel to be set from each Slider.

프로그램을 처음 실행하는 경우 BoxView, Label 및 세 가지 Slider 요소는 모두 ViewModel을 인스턴스화했을 때 설정된 초기 Color 설정을 기반으로 ViewModel에서 설정됩니다.When the program is first run, the BoxView, Label, and three Slider elements are all set from the ViewModel based on the initial Color property set when the ViewModel was instantiated. 왼쪽의 iOS 스크린샷이 이 경우를 보여줍니다.This is shown in the iOS screenshot at the left:

간단한 색 선택기Simple Color Selector

슬라이더를 조작하면 그에 따라 BoxViewLabel이 Android와 UWP 스크린샷에 설명된 것처럼 업데이트됩니다.As you manipulate the sliders, the BoxView and Label are updated accordingly, as illustrated by the Android and UWP screenshots.

리소스 사전에서 ViewModel을 인스턴스화하는 것이 일반적인 방법 중 하나입니다.Instantiating the ViewModel in the resource dictionary is one common approach. BindingContext 속성에 대한 속성 요소 태그 내에서 ViewModel을 인스턴스화하는 것도 가능합니다.It's also possible to instantiate the ViewModel within property element tags for the BindingContext property. Simple Color Selector(간단한 색 선택기) XAML 파일에서 HslColorViewModel을 리소스 사전에서 제거하고 다음과 같이 GridBindingContext 속성으로 설정합니다.In the Simple Color Selector XAML file, try removing the HslColorViewModel from the resource dictionary and set it to the BindingContext property of the Grid like this:

<Grid>
    <Grid.BindingContext>
        <local:HslColorViewModel Color="MediumTurquoise" />
    </Grid.BindingContext>

    ···

</Grid>

바인딩 컨텍스트는 다양한 방식으로 설정할 수 있습니다.The binding context can be set in a variety of ways. 코드 숨김 파일이 ViewModel을 인스턴스화하고 페이지의 BindingContext 속성으로 설정하는 경우도 있습니다.Sometimes, the code-behind file instantiates the ViewModel and sets it to the BindingContext property of the page. 이 모두가 유효한 방식입니다.These are all valid approaches.

바인딩 모드 재정의Overriding the Binding Mode

대상 속성의 기본 바인딩 모드가 특정 데이터 바인딩에 적합하지 않으면 BindingMode 속성(또는 Binding 태그 확장의 Mode 속성)을 BindingMode 열거형의 멤버 중 하나로 설정하여 재정의할 수 있습니다.If the default binding mode on the target property is not suitable for a particular data binding, it's possible to override it by setting the Mode property of Binding (or the Mode property of the Binding markup extension) to one of the members of the BindingMode enumeration.

단, Mode 속성을 TwoWay로 설정해도 항상 예상대로 작동하지는 않습니다.However, setting the Mode property to TwoWay doesn't always work as you might expect. 예를 들어, 바인딩 정의에 TwoWay가 포함되도록 Alternative XAML Binding(대체 XAML 바인딩) XAML 파일을 수정해 보겠습니다.For example, try modifying the Alternative XAML Binding XAML file to include TwoWay in the binding definition:

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand"
       Scale="{Binding Source={x:Reference slider},
                       Path=Value,
                       Mode=TwoWay}" />

SliderScale 속성의 초기 값인 1로 초기화될 것으로 예상할 수도 있지만 그렇게 되지 않습니다.It might be expected that the Slider would be initialized to the initial value of the Scale property, which is 1, but that doesn't happen. TwoWay 바인딩이 초기화되면 먼저 원본을 통해 대상이 설정됩니다. 즉, Scale 속성이 Slider 기본값인 0으로 설정됩니다.When a TwoWay binding is initialized, the target is set from the source first, which means that the Scale property is set to the Slider default value of 0. TwoWay 바인딩이 Slider에 설정되면 Slider는 원본을 통해 초기 설정됩니다.When the TwoWay binding is set on the Slider, then the Slider is initially set from the source.

Alternative XAML Binding(대체 XAML 바인딩) 샘플에서 바인딩 모드를 OneWayToSource로 설정할 수 있습니다.You can set the binding mode to OneWayToSource in the Alternative XAML Binding sample:

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand"
       Scale="{Binding Source={x:Reference slider},
                       Path=Value,
                       Mode=OneWayToSource}" />

이제 Slider가 1(Scale의 기본값)로 초기화되지만 Slider를 조작해도 Scale 속성에 영향을 주지 않기 때문에 별로 유용하지 않습니다.Now the Slider is initialized to 1 (the default value of Scale) but manipulating the Slider doesn't affect the Scale property, so this is not very useful.

참고

또한 VisualElement 클래스는 VisualElement의 크기를 가로 및 세로 방향으로 다르게 확장할 수 있는 ScaleXScaleY 속성도 정의합니다.The VisualElement class also defines ScaleX and ScaleY properties, which can scale the VisualElement differently in the horizontal and vertical directions.

TwoWay를 사용하여 기본 바인딩 모드를 재정의하는 매우 유용한 애플리케이션은 ListViewSelectedItem 속성과 관련이 있습니다.A very useful application of overriding the default binding mode with TwoWay involves the SelectedItem property of ListView. 기본 바인딩 모드는 OneWayToSource입니다.The default binding mode is OneWayToSource. 데이터 바인딩이 ViewModel의 원본 속성을 참조하도록 SelectedItem 속성에 설정되면 해당 원본 속성은 ListView 선택 항목에서 설정됩니다.When a data binding is set on the SelectedItem property to reference a source property in a ViewModel, then that source property is set from the ListView selection. 하지만 경우에 따라 ListView를 ViewModel에서 초기화해야 하는 경우도 있습니다.However, in some circumstances, you might also want the ListView to be initialized from the ViewModel.

Sample Settings(샘플 설정) 페이지는 이 기술을 보여줍니다.The Sample Settings page demonstrates this technique. 이 페이지는 SampleSettingsViewModel 파일과 같이 ViewModel에 정의되는 경우가 많은 애플리케이션 설정의 간단한 구현을 나타냅니다.This page represents a simple implementation of application settings, which are very often defined in a ViewModel, such as this SampleSettingsViewModel file:

public class SampleSettingsViewModel : INotifyPropertyChanged
{
    string name;
    DateTime birthDate;
    bool codesInCSharp;
    double numberOfCopies;
    NamedColor backgroundNamedColor;

    public event PropertyChangedEventHandler PropertyChanged;

    public SampleSettingsViewModel(IDictionary<string, object> dictionary)
    {
        Name = GetDictionaryEntry<string>(dictionary, "Name");
        BirthDate = GetDictionaryEntry(dictionary, "BirthDate", new DateTime(1980, 1, 1));
        CodesInCSharp = GetDictionaryEntry<bool>(dictionary, "CodesInCSharp");
        NumberOfCopies = GetDictionaryEntry(dictionary, "NumberOfCopies", 1.0);
        BackgroundNamedColor = NamedColor.Find(GetDictionaryEntry(dictionary, "BackgroundNamedColor", "White"));
    }

    public string Name
    {
        set { SetProperty(ref name, value); }
        get { return name; }
    }

    public DateTime BirthDate
    {
        set { SetProperty(ref birthDate, value); }
        get { return birthDate; }
    }

    public bool CodesInCSharp
    {
        set { SetProperty(ref codesInCSharp, value); }
        get { return codesInCSharp; }
    }

    public double NumberOfCopies
    {
        set { SetProperty(ref numberOfCopies, value); }
        get { return numberOfCopies; }
    }

    public NamedColor BackgroundNamedColor
    {
        set
        {
            if (SetProperty(ref backgroundNamedColor, value))
            {
                OnPropertyChanged("BackgroundColor");
            }
        }
        get { return backgroundNamedColor; }
    }

    public Color BackgroundColor
    {
        get { return BackgroundNamedColor?.Color ?? Color.White; }
    }

    public void SaveState(IDictionary<string, object> dictionary)
    {
        dictionary["Name"] = Name;
        dictionary["BirthDate"] = BirthDate;
        dictionary["CodesInCSharp"] = CodesInCSharp;
        dictionary["NumberOfCopies"] = NumberOfCopies;
        dictionary["BackgroundNamedColor"] = BackgroundNamedColor.Name;
    }

    T GetDictionaryEntry<T>(IDictionary<string, object> dictionary, string key, T defaultValue = default(T))
    {
        return dictionary.ContainsKey(key) ? (T)dictionary[key] : defaultValue;
    }

    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

각 애플리케이션 설정은 SaveState라는 메서드로 Xamarin.Forms 속성 사전에 저장되고 이 사전에서 생성자로 로드되는 속성입니다.Each application setting is a property that is saved to the Xamarin.Forms properties dictionary in a method named SaveState and loaded from that dictionary in the constructor. 클래스의 맨 아래에는 ViewModel을 간소화하고 오류를 줄이도록 도와주는 두 가지 메서드가 있습니다.Towards the bottom of the class are two methods that help streamline ViewModels and make them less prone to errors. 맨 아래 OnPropertyChanged 메서드에는 호출 속성으로 설정되는 선택적 매개 변수가 있습니다.The OnPropertyChanged method at the bottom has an optional parameter that is set to the calling property. 이것을 사용하면 속성 이름을 문자열로 지정할 때 맞춤법 오류가 방지됩니다.This avoids spelling errors when specifying the name of the property as a string.

클래스의 SetProperty 메서드는 훨씬 더 많은 작업을 수행합니다. 속성으로 설정되는 값을 필드로 저장되는 값과 비교하고 두 값이 같지 않을 때만 OnPropertyChanged를 호출합니다.The SetProperty method in the class does even more: It compares the value that is being set to the property with the value stored as a field, and only calls OnPropertyChanged when the two values are not equal.

SampleSettingsViewModel 클래스는 배경색에 대한 두 가지 속성을 정의합니다. BackgroundNamedColor 속성은 NamedColor 형식이며, 이것은 DataBindingDemos 솔루션에도 포함되는 클래스입니다.The SampleSettingsViewModel class defines two properties for the background color: The BackgroundNamedColor property is of type NamedColor, which is a class also included in the DataBindingDemos solution. BackgroundColor 속성은 Color 유형이며 NamedColor 개체의 Color 속성에서 가져옵니다.The BackgroundColor property is of type Color, and is obtained from the Color property of the NamedColor object.

NamedColor 클래스는 .NET 리플렉션을 사용하여 Xamarin.Forms Color 구조체의 정적인 공용 필드를 모두 열거하고 정적 All 속성에서 액세스할 수 있는 컬렉션에 이름을 사용하여 저장합니다.The NamedColor class uses .NET reflection to enumerate all the static public fields in the Xamarin.Forms Color structure, and to store them with their names in a collection accessible from the static All property:

public class NamedColor : IEquatable<NamedColor>, IComparable<NamedColor>
{
    // Instance members
    private NamedColor()
    {
    }

    public string Name { private set; get; }

    public string FriendlyName { private set; get; }

    public Color Color { private set; get; }

    public string RgbDisplay { private set; get; }

    public bool Equals(NamedColor other)
    {
        return Name.Equals(other.Name);
    }

    public int CompareTo(NamedColor other)
    {
        return Name.CompareTo(other.Name);
    }

    // Static members
    static NamedColor()
    {
        List<NamedColor> all = new List<NamedColor>();
        StringBuilder stringBuilder = new StringBuilder();

        // Loop through the public static fields of the Color structure.
        foreach (FieldInfo fieldInfo in typeof(Color).GetRuntimeFields())
        {
            if (fieldInfo.IsPublic &&
                fieldInfo.IsStatic &&
                fieldInfo.FieldType == typeof(Color))
            {
                // Convert the name to a friendly name.
                string name = fieldInfo.Name;
                stringBuilder.Clear();
                int index = 0;

                foreach (char ch in name)
                {
                    if (index != 0 && Char.IsUpper(ch))
                    {
                        stringBuilder.Append(' ');
                    }
                    stringBuilder.Append(ch);
                    index++;
                }

                // Instantiate a NamedColor object.
                Color color = (Color)fieldInfo.GetValue(null);

                NamedColor namedColor = new NamedColor
                {
                    Name = name,
                    FriendlyName = stringBuilder.ToString(),
                    Color = color,
                    RgbDisplay = String.Format("{0:X2}-{1:X2}-{2:X2}",
                                                (int)(255 * color.R),
                                                (int)(255 * color.G),
                                                (int)(255 * color.B))
                };

                // Add it to the collection.
                all.Add(namedColor);
            }
        }
        all.TrimExcess();
        all.Sort();
        All = all;
    }

    public static IList<NamedColor> All { private set; get; }

    public static NamedColor Find(string name)
    {
        return ((List<NamedColor>)All).Find(nc => nc.Name == name);
    }

    public static string GetNearestColorName(Color color)
    {
        double shortestDistance = 1000;
        NamedColor closestColor = null;

        foreach (NamedColor namedColor in NamedColor.All)
        {
            double distance = Math.Sqrt(Math.Pow(color.R - namedColor.Color.R, 2) +
                                        Math.Pow(color.G - namedColor.Color.G, 2) +
                                        Math.Pow(color.B - namedColor.Color.B, 2));

            if (distance < shortestDistance)
            {
                shortestDistance = distance;
                closestColor = namedColor;
            }
        }
        return closestColor.Name;
    }
}

DataBindingDemos 프로젝트의 App 클래스는 SampleSettingsViewModel 유형의 Settings라는 속성을 정의합니다.The App class in the DataBindingDemos project defines a property named Settings of type SampleSettingsViewModel. 이 속성은 App 클래스가 인스턴스화될 때 초기화되고 SaveState 메서드는 OnSleep 메서드가 호출될 때 호출됩니다.This property is initialized when the App class is instantiated, and the SaveState method is called when the OnSleep method is called:

public partial class App : Application
{
    public App()
    {
        InitializeComponent();

        Settings = new SampleSettingsViewModel(Current.Properties);

        MainPage = new NavigationPage(new MainPage());
    }

    public SampleSettingsViewModel Settings { private set; get; }

    protected override void OnStart()
    {
        // Handle when your app starts
    }

    protected override void OnSleep()
    {
        // Handle when your app sleeps
        Settings.SaveState(Current.Properties);
    }

    protected override void OnResume()
    {
        // Handle when your app resumes
    }
}

애플리케이션 수명 주기 메서드에 대한 자세한 내용은 앱 수명 주기 문서를 참조하세요.For more information on the application lifecycle methods, see the article App Lifecycle.

그 밖에 거의 모든 것은 SampleSettingsPage.xaml 파일에서 처리됩니다.Almost everything else is handled in the SampleSettingsPage.xaml file. 페이지의 BindingContextBinding 태그 확장을 사용하여 설정됩니다. 바인딩 소스는 프로젝트의 App 클래스 인스턴스인 정적 Application.Current 속성이며 PathSampleSettingsViewModel 개체인 Settings 속성으로 설정됩니다.The BindingContext of the page is set using a Binding markup extension: The binding source is the static Application.Current property, which is the instance of the App class in the project, and the Path is set to the Settings property, which is the SampleSettingsViewModel object:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.SampleSettingsPage"
             Title="Sample Settings"
             BindingContext="{Binding Source={x:Static Application.Current},
                                      Path=Settings}">

    <StackLayout BackgroundColor="{Binding BackgroundColor}"
                 Padding="10"
                 Spacing="10">

        <StackLayout Orientation="Horizontal">
            <Label Text="Name: "
                   VerticalOptions="Center" />

            <Entry Text="{Binding Name}"
                   Placeholder="your name"
                   HorizontalOptions="FillAndExpand"
                   VerticalOptions="Center" />
        </StackLayout>

        <StackLayout Orientation="Horizontal">
            <Label Text="Birth Date: "
                   VerticalOptions="Center" />

            <DatePicker Date="{Binding BirthDate}"
                        HorizontalOptions="FillAndExpand"
                        VerticalOptions="Center" />
        </StackLayout>

        <StackLayout Orientation="Horizontal">
            <Label Text="Do you code in C#? "
                   VerticalOptions="Center" />

            <Switch IsToggled="{Binding CodesInCSharp}"
                    VerticalOptions="Center" />
        </StackLayout>

        <StackLayout Orientation="Horizontal">
            <Label Text="Number of Copies: "
                   VerticalOptions="Center" />

            <Stepper Value="{Binding NumberOfCopies}"
                     VerticalOptions="Center" />

            <Label Text="{Binding NumberOfCopies}"
                   VerticalOptions="Center" />
        </StackLayout>

        <Label Text="Background Color:" />

        <ListView x:Name="colorListView"
                  ItemsSource="{x:Static local:NamedColor.All}"
                  SelectedItem="{Binding BackgroundNamedColor, Mode=TwoWay}"
                  VerticalOptions="FillAndExpand"
                  RowHeight="40">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <BoxView Color="{Binding Color}"
                                     HeightRequest="32"
                                     WidthRequest="32"
                                     VerticalOptions="Center" />

                            <Label Text="{Binding FriendlyName}"
                                   FontSize="24"
                                   VerticalOptions="Center" />
                        </StackLayout>                        
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

페이지의 모든 자식은 바인딩 컨텍스트를 상속받습니다.All the children of the page inherit the binding context. 이 페이지의 다른 바인딩 대부분은 SampleSettingsViewModel의 속성에 지정됩니다.Most of the other bindings on this page are to properties in SampleSettingsViewModel. BackgroundColor 속성은 StackLayoutBackgroundColor 속성을 설정하는 데 사용되고 Entry, DatePicker, Switch, Stepper 속성은 모두 ViewModel의 다른 속성에 바인딩됩니다.The BackgroundColor property is used to set the BackgroundColor property of the StackLayout, and the Entry, DatePicker, Switch, and Stepper properties are all bound to other properties in the ViewModel.

ListViewItemsSource 속성은 정적 NamedColor.All 속성으로 설정됩니다.The ItemsSource property of the ListView is set to the static NamedColor.All property. 이것은 ListView를 모든 NamedColor 인스턴스로 채웁니다.This fills the ListView with all the NamedColor instances. ListView의 각 항목의 경우 해당 항목에 대한 바인딩 컨텍스트는 NamedColor 개체로 설정됩니다.For each item in the ListView, the binding context for the item is set to a NamedColor object. ViewCellBoxViewLabelNamedColor의 속성에 바인딩됩니다.The BoxView and Label in the ViewCell are bound to properties in NamedColor.

ListViewSelectedItem 속성은 NamedColor 유형이며 SampleSettingsViewModelBackgroundNamedColor 속성에 바인딩됩니다.The SelectedItem property of the ListView is of type NamedColor, and is bound to the BackgroundNamedColor property of SampleSettingsViewModel:

SelectedItem="{Binding BackgroundNamedColor, Mode=TwoWay}"

SelectedItem의 기본 바인딩 모드는 OneWayToSource이며, 선택한 항목의 ViewModel 속성을 설정합니다.The default binding mode for SelectedItem is OneWayToSource, which sets the ViewModel property from the selected item. TwoWay 모드는 SelectedItem이 ViewModel에서 초기화될 수 있도록 합니다.The TwoWay mode allows the SelectedItem to be initialized from the ViewModel.

단, SelectedItem이 이렇게 설정되면 선택한 항목을 표시하도록 ListView가 자동으로 스크롤되지 않습니다.However, when the SelectedItem is set in this way, the ListView does not automatically scroll to show the selected item. 코드 숨김 파일에 약간의 코드가 필요합니다.A little code in the code-behind file is necessary:

public partial class SampleSettingsPage : ContentPage
{
    public SampleSettingsPage()
    {
        InitializeComponent();

        if (colorListView.SelectedItem != null)
        {
            colorListView.ScrollTo(colorListView.SelectedItem,
                                   ScrollToPosition.MakeVisible,
                                   false);
        }
    }
}

왼쪽의 iOS 스크린샷은 프로그램이 처음 실행될 때를 보여줍니다.The iOS screenshot at the left shows the program when it's first run. SampleSettingsViewModel의 생성자는 배경색을 흰색으로 초기화하며, 이것이 ListView에 선택된 항목입니다.The constructor in SampleSettingsViewModel initializes the background color to white, and that's what's selected in the ListView:

샘플 설정Sample Settings

다른 스크린샷은 변경된 설정을 보여 줍니다.The other screenshot shows altered settings. 이 페이지로 실험할 때는 프로그램을 절전 상태로 두거나 실행 중인 디바이스나 에뮬레이터에서 종료해야 합니다.When experimenting with this page, remember to put the program to sleep or to terminate it on the device or emulator that it's running. Visual Studio 디버거에서 프로그램을 종료하면 App 클래스의 OnSleep 재정의가 호출되지 않습니다.Terminating the program from the Visual Studio debugger will not cause the OnSleep override in the App class to be called.

다음 문서에서는 LabelText 속성에 설정된 데이터 바인딩의 문자열 서식을 지정하는 방법을 알아봅니다.In the next article you'll see how to specify String Formatting of data bindings that are set on the Text property of Label.