.NET MAUI - Binding 모드

2022. 7. 26. 00:00MAUI

반응형

모든 .NET 다중 플랫폼 앱 UI(.NET MAUI) 바인딩 가능 속성에는 바인딩 가능한 속성을 만들 때 설정되고 개체의 BindableProperty 속성에서 사용할 수 있는 기본 바인딩 모드가 있다.

기본 바인딩 모드는 속성이 데이터 바인딩 대상일 때 적용되는 모드를 나타낸다. 

Rotation, Scale  Opacity와 같은 대부분의 속성에 대한 기본 바인딩 모드는 OneWay이다.

이러한 속성이 데이터 바인딩 대상인 경우에는 대상 속성이 원본에서 설정된다.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiApp1.Xaml.BindingMode.MainPage"
             Title="MainPage">
    <StackLayout Padding="10, 0">
        <Label x:Name="label"
               Text="TEXT"
               FontSize="80"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Slider x:Name="slider"
                VerticalOptions="Center"
                Value="{Binding Opacity, Source={x:Reference label}}" />
    </StackLayout>
</ContentPage>

초기에 Slider 은 Label 의 Opacity 속성을 참조 하며 기본값은 1이다. 따라서 1로 초기화 된다. 

 

Slider 에 Value 속성에 대한 기본 바인딩 모드가 Twoway 이기 때문에

Slider 를 조정하면 Label의 Opacity 값도 바뀐다. 

 

 

 

Binding Mode 는 다음 중에서 하나로 재정의 할 수 있다. 

  • TwoWay — 데이터가 소스와 대상 간에 양방향으로 이동
  • OneWay — 소스에서 타겟으로 데이터 이동
  • OneWayToSource — 데이터가 대상에서 소스로 이동
  • OneTime — 데이터가 소스에서 대상으로 한번만 이동 (.NET MAUI 3.0의 새로운 기능)

대부분의 바인딩 가능한 속성에는 OneWay의 기본 바인딩 모드가 있지만 

다음을 포함하여 일부 속성에는 TwoWay의 기본 바인딩 모드가 있다.

TwoWay

  • DatePicker의 날짜 속성
  • Editor, Entry, SearchBar, EntryCell의 Text 속성
  • ListView의 IsRefreshing 속성
  • MultiPage의 SelectedItem 속성
  • Picker의 SelectedIndex 및 SelectedItem 속성
  • Slider와 Stepper의 Value 속성
  • Switch의 IsToggled 속성
  • SwitchCell의 속성에
  • TimePicker의 시간 속성

 

One-way-to-source bindings

ListView의 SelectedItem 속성에는 OneWayToSource의 기본 바인딩 모드가 있다.

 

OneTime

바인딩 컨텍스트가 변경되는 경우에만 업데이트된다.

원본 속성의 변경 사항을 모니터링할 필요가 없기 때문에 바인딩 인프라가 간소화된다.

 

 

ViewModels 및 속성 변경 알림

데이터 바인딩에서 ViewModel 을 사용할때 ViewModel 은 데이터 바이딩 소스에 해당한다.

ViewModel 은 속성 값이 변경될 때 바인딩 인프라가 알림을 받을 수 있도록 하는 알림 메커니즘을 구현한다.

이것은 PropertyChanged 라는 이벤트를 정의 하는 INotifyPropertyChanged 인터페이스 이다.

이 인터페이스는 공용 속성 중 하나가 값을 변경 할 때 이벤트를 발생 시킨다. 

 

다음 예제를 보자

HslColorView.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiApp1.Xaml.BindingMode.HslColorView"
             xmlns:local="clr-namespace:MauiApp1.Xaml.BindingMode"
             Title="ViewModel">
    <ContentPage.BindingContext>
        <local:HslColorViewModel Color="MediumTurquoise" />
    </ContentPage.BindingContext>

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

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <BoxView Color="{Binding Color}"
                 Grid.Row="0" />
        <StackLayout Grid.Row="1">
            <Label Text="{Binding Name}" 
                   HorizontalTextAlignment="Center"  FontSize="Large"/>
            <Slider Value="{Binding Hue}"/>
            <Label Text="{Binding Hue, StringFormat='색조 : {0:F2}'}" FontSize="Large" />
            <Slider Value="{Binding Saturation}"/>
            <Label Text="{Binding Saturation, StringFormat='채도 : {0:F2}'}" FontSize="Large" />
            <Slider Value="{Binding Luminosity}"/>
            <Label Text="{Binding Luminosity, StringFormat='명도 : {0:F2}'}" FontSize="Large" />
        </StackLayout>
    </Grid>
</ContentPage>

NamedColor.cs 
(참조 : https://icodebroker.tistory.com/11584)

using System.Reflection;
using System.Text;

namespace MauiApp1.Xaml.BindingMode;
public class NamedColor : IEquatable<NamedColor>, IComparable<NamedColor>
{

    #region 이름있는 색상 리스트 - NamedColorList

    /// <summary>
    /// 이름있는 색상 리스트
    /// </summary>
    public static IList<NamedColor> NamedColorList { private set; get; }

    #endregion

    #region 명칭 - Name

    /// <summary>
    /// 명칭
    /// </summary>
    public string Name { private set; get; }

    #endregion
    #region 친숙한 명칭 - FriendlyName

    /// <summary>
    /// 친숙한 명칭
    /// </summary>
    public string FriendlyName { private set; get; }

    #endregion
    #region 색상 - Color

    /// <summary>
    /// 색상
    /// </summary>
    public Color Color { private set; get; }

    #endregion
    #region RGB 디스플레이 - RGBDisplay

    /// <summary>
    /// RGB 디스플레이
    /// </summary>
    public string RGBDisplay { private set; get; }

    #endregion

    #region 생성자 - NamedColor()

    /// <summary>
    /// 생성자
    /// </summary>
    static NamedColor()
    {
        List<NamedColor> namedColorList = new List<NamedColor>();

        StringBuilder stringBuilder = new StringBuilder();

        foreach (FieldInfo fieldInfo in typeof(Colors).GetRuntimeFields())
        {
            if (fieldInfo.IsPublic && fieldInfo.IsStatic && fieldInfo.FieldType == typeof(Color))
            {
                string name = fieldInfo.Name;

                stringBuilder.Clear();

                int index = 0;

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

                    stringBuilder.Append(character);

                    index++;
                }

                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.Red),
                        (int)(255 * color.Green),
                        (int)(255 * color.Blue)
                    )
                };

                namedColorList.Add(namedColor);
            }
        }

        namedColorList.TrimExcess();

        namedColorList.Sort();

        NamedColorList = namedColorList;
    }

    #endregion

   
    #region 생성자 - NamedColor()

    /// <summary>
    /// 생성자
    /// </summary>
    private NamedColor()
    {
    }

    #endregion

    
    #region 찾기 - Find(name)

    /// <summary>
    /// 찾기
    /// </summary>
    /// <param name="name">명칭</param>
    /// <returns>이름있는 색상</returns>
    public static NamedColor Find(string name)
    {
        return ((List<NamedColor>)NamedColorList).Find(nc => nc.Name == name);
    }

    #endregion
    #region 최근접 색상명 구하기 - GetNearestColorName(color)

    /// <summary>
    /// 최근접 색상명 구하기
    /// </summary>
    /// <param name="color">색상</param>
    /// <returns>최근접 색상명</returns>
    public static string GetNearestColorName(Color color)
    {
        double shortestDistance = 1000;

        NamedColor closestColor = null;

        foreach (NamedColor namedColor in NamedColor.NamedColorList)
        {
            double distance = Math.Sqrt
            (
                Math.Pow(color.Red - namedColor.Color.Red, 2) +
                Math.Pow(color.Green - namedColor.Color.Green, 2) +
                Math.Pow(color.Blue - namedColor.Color.Blue, 2)
            );

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

        return closestColor.Name;
    }

    #endregion

  
    #region 동일 여부 구하기 - Equals(other)

    /// <summary>
    /// 동일 여부 구하기
    /// </summary>
    /// <param name="other">다른 이름있는 색상</param>
    /// <returns>동일 여부</returns>
    public bool Equals(NamedColor other)
    {
        return Name.Equals(other.Name);
    }

    #endregion
    #region 비교하기 - CompareTo(other)

    /// <summary>
    /// 비교하기
    /// </summary>
    /// <param name="other">다른 이름있는 색상</param>
    /// <returns>비교 결과</returns>
    public int CompareTo(NamedColor other)
    {
        return Name.CompareTo(other.Name);
    }

    #endregion
}

 

HslColorViewModel.cs

using System.ComponentModel;

namespace MauiApp1.Xaml.BindingMode;
public class HslColorViewModel : INotifyPropertyChanged
{
    Color color;
    string name;
    float hue;
    float saturation;
    float luminosity;

    public event PropertyChangedEventHandler PropertyChanged;

    public float Hue
    {
        get
        {
            return hue;
        }
        set
        {
            if (hue != value)
            {
                Color = Color.FromHsla(value, saturation, luminosity);
            }
        }
    }

    public float Saturation
    {
        get
        {
            return saturation;
        }
        set
        {
            if (saturation != value)
            {
                Color = Color.FromHsla(hue, value, luminosity);
            }
        }
    }

    public float Luminosity
    {
        get
        {
            return luminosity;
        }
        set
        {
            if (luminosity != value)
            {
                Color = Color.FromHsla(hue, saturation, value);
            }
        }
    }

    public Color Color
    {
        get
        {
            return color;
        }
        set
        {
            if (color != value)
            {
                color = value;
                hue = color.GetHue();
                saturation = color.GetSaturation();
                luminosity = color.GetLuminosity();
                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);
            }
        }
    }

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

내용을 보면 HslColorViewModel.cs (이하 뷰모델) 에 각 Xaml Binding Propery 와 mapping 되는 속성을 선언한다.

그리고 해당 속성이 set 될때 PropertyChanged 를 통해 속성의 변경을 Update 하라고 XAML 에 알린다. 

XAML 은 해당 속성에 맞는 {Binding xxxx} 를 update 시켜 화면을 갱신한다. 

 

실행

 

 

 

관련영상

https://youtu.be/JX3_9P6BtTw

 

 

반응형

'MAUI' 카테고리의 다른 글

.NET MAUI - Binding Value Converters  (0) 2022.07.28
.NET MAUI - Binding 경로 (Path)  (0) 2022.07.27
.NET MAUI - Binding 기본  (0) 2022.07.25
.NET MAUI - XAML stack navigation  (0) 2022.07.22
NET MAUI - XAML tab navigation  (0) 2022.07.21