.NET MAUI - Binding Value Converters

2022. 7. 28. 00:00MAUI

반응형

.NET 다중 플랫폼 앱 UI(.NET MAUI) 데이터 바인딩은 일반적으로 원본 속성에서 대상 속성으로 데이터를 전송하고 경우에 따라 대상 속성에서 원본 속성으로 데이터를 전송한다. 이 전송은 소스 및 대상 속성이 동일한 유형이거나 암시적 변환을 통해 한 유형을 다른 유형으로 변환할 수 있는 경우에 간단하다. 그렇지 않은 경우 형식 변환이 발생한다.

데이터 바인딩의 StringFormat 속성을 사용하여 모든 유형을 문자열로 변환할 수 있다. 

다른 유형의 변환의 경우 IValueConverter 인터페이스를 구현하는 클래스에 일부 특수 코드를 작성해야 한다. 

 

소스 속성이 int 유형이지만 대상 속성이 bool인 데이터 바인딩을 정의하려고 한다고 가정하자. 

정수 소스가 0과 같을 때 이 데이터 바인딩이 false 값을 생성하고 그렇지 않으면 true를 생성한다.

 

public class IntToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value != 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 1 : 0;
    }
}

그리고 이 내용을 Button 을 enable 하거나 disable 하는 다음 XAML Page 에 적용해보자

<?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.ValueConverter.EnableButtonPage"
             xmlns:local ="clr-namespace:MauiApp1.Xaml.ValueConverter"
             Title="EnableButtonPage">
    <ContentPage.Resources>
        <local:IntToBoolConverter x:Key="intToBool" />
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="enter search term"
               VerticalOptions="Center" />
        <Button Text="Search"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry1},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
        <Entry x:Name="entry2"
               Text=""
               Placeholder="enter destination"
               VerticalOptions="Center" />
        <Button Text="Submit"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry2},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
    </StackLayout>
</ContentPage>

실행화면

 

Convert 는 int 형식을 bool 형식으로 변환하고

ConvertBack 는 bool 형식을 int 로 변환한다. 

 

Convert 는 Entry 에 입력 값의 길이가 source 이고 Button 의 IsEnabled 가 target 이 된다. 

ConvertBack 은 반대로 생각하면 쉽다. 

 

또한 Value Converter 는 속성과 매개변수가 있을 수 있다. 

다음 Converter 는 소스의 bool 을 대상에 T 형태로 변환한다. 

public class BoolToObjectConverter<T> : IValueConverter
{
    public T TrueObject { get; set; }
    public T FalseObject { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? TrueObject : FalseObject;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((T)value).Equals(TrueObject);
    }
}

다음 예는 이 변환기를 사용하여 Switch 보기의 값을 표시하는 방법을 보여준다.

여기에서 각 값 변환기는 Binding.Converter 속성 요소 태그 간에 인스턴스화된다.

x:TypeArguments는 generic argument 를 나타내며 TrueObject 및 FalseObject는 모두 해당 유형의 개체로 설정된다.

<?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.ValueConverter.SwitchIndicatorsPage"
             xmlns:local="clr-namespace:MauiApp1.Xaml.ValueConverter"
             Title="SwitchIndicatorsPage">
    <ContentPage.Resources>
        <Style TargetType="Label">
            <Setter Property="FontSize" Value="18" />
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>

        <Style TargetType="Switch">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Subscribe?" />
            <Switch x:Name="switch1" />
            <Label>
                <Label.Text>
                    <Binding Source="{x:Reference switch1}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Of course!"
                                                         FalseObject="No way!" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Allow popups?" />
            <Switch x:Name="switch2" />
            <Label>
                <Label.Text>
                    <Binding Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Yes"
                                                         FalseObject="No" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
                <Label.TextColor>
                    <Binding Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Color"
                                                         TrueObject="Green"
                                                         FalseObject="Red" />
                        </Binding.Converter>
                    </Binding>
                </Label.TextColor>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Learn more?" />
            <Switch x:Name="switch3" />
            <Label FontSize="18"
                   VerticalOptions="Center">
                <Label.Style>
                    <Binding Source="{x:Reference switch3}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Style">
                                <local:BoolToObjectConverter.TrueObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Indubitably!" />
                                        <Setter Property="FontAttributes" Value="Italic, Bold" />
                                        <Setter Property="TextColor" Value="Green" />
                                    </Style>
                                </local:BoolToObjectConverter.TrueObject>

                                <local:BoolToObjectConverter.FalseObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Maybe later" />
                                        <Setter Property="FontAttributes" Value="None" />
                                        <Setter Property="TextColor" Value="Red" />
                                    </Style>
                                </local:BoolToObjectConverter.FalseObject>
                            </local:BoolToObjectConverter>
                        </Binding.Converter>
                    </Binding>
                </Label.Style>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

이 예에서 세 개의 Switch 및 Label 쌍 중 마지막에서 generic argument가 Style로 설정되고 TrueObject 및 FalseObject 값에 대해 전체 Style 개체가 제공된다. 이들은 리소스 사전에 설정된 Label의 암시적 스타일을 재정의하므로 해당 스타일의 속성이 명시적으로 Label에 할당된다. 스위치를 토글하면 해당 레이블에 변경 사항이 반영된다.

 

Value Converter 를 이용해 Convert 시에 Parameter 를 전달 할 수 있다. 

이것을 Converter parameters 라고 한다. 

using MauiApp1.Xaml.BindingMode;
using System.ComponentModel;

namespace MauiApp1.Xaml.ValueConverter;
public class RgbColorViewModel : INotifyPropertyChanged
{
    Color color;
    string name;

    public event PropertyChangedEventHandler PropertyChanged;

    public float Red
    {
        get { return color.Red; }
        set
        {
            if (color.Red != value)
            {
                Color = new Color(value, color.Green, color.Blue);
            }
        }
    }

    public float Green
    {
        get { return color.Green; }
        set
        {
            if (color.Green != value)
            {
                Color = new Color(color.Red, value, color.Blue);
            }
        }
    }

    public float Blue
    {
        get { return color.Blue; }
        set
        {
            if (color.Blue != value)
            {
                Color = new Color(color.Red, color.Green, value);
            }
        }
    }

    public Color Color
    {
        get { return color; }
        set
        {
            if (color != value)
            {
                color = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Red"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Green"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Blue"));
                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"));
            }
        }
    }
}

NamedColor 은 이전 강의 Bining Mode 에 있습니다. 

R , G, B 값의 범위는 0과 1 사이 일 수 있다. 그러나 우리는 16진수 값에 더 익숙 하다.

그러므로 16진수 값으로 표시하기 위해 255 를 곱해서 정수로 변환한 다음

StringFormat 의 "X2" 를 이용하여 형식을 지정해 주면 된다. 

값 변환기를 최대한 일반화하려면 ConverterParameter 속성을 사용하여 곱셈 인수를 지정할 수 있다.

즉, Convert 및 ConvertBack 메서드가 매개 변수 인수로 입력된다. 

using System.Globalization;

namespace MauiApp1.Xaml.ValueConverter;
public class FloatToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)Math.Round((float)value * GetParameter(parameter));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value / GetParameter(parameter);
    }

    double GetParameter(object parameter)
    {
        if (parameter is float)
            return (float)parameter;
        else if (parameter is int)
            return (int)parameter;
        else if (parameter is string)
            return float.Parse((string)parameter);

        return 1;
    }
}

XAML 에서는 다음과 같이 사용할 수 있다. 

<Label Text="{Binding Red,
                      Converter={StaticResource doubleToInt},
                      ConverterParameter=255,
                      StringFormat='Red = {0:X2}'}" />

255는 숫자처럼 보이지만 ConverterParameter는 Object 유형이므로 XAML 파서는 255를 문자열로 처리한다.

이러한 이유로 값 변환기에는 매개변수가 float, int 또는 string 유형인 경우를 처리하는

별도의 GetParameter 메서드가 포함되어 있다.

<?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.ValueConverter.RgbColorSelectorPage"
             xmlns:local="clr-namespace:MauiApp1.Xaml.ValueConverter"
             Title="RgbColorSelectorPage">
    <ContentPage.BindingContext>
        <local:RgbColorViewModel Color="Gray" />
    </ContentPage.BindingContext>
    <ContentPage.Resources>
        <Style TargetType="Slider">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>

        <Style TargetType="Label">
            <Setter Property="HorizontalTextAlignment" Value="Center" />
        </Style>

        <local:FloatToIntConverter x:Key="floatToInt" />
    </ContentPage.Resources>

    <StackLayout Margin="20">
        <BoxView Color="{Binding Color}"
                 HeightRequest="100"
                 WidthRequest="100"
                 HorizontalOptions="Center" />
        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />
            <Slider Value="{Binding Red}" />
            <Label Text="{Binding Red,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Red = {0:X2}'}" />
            <Slider Value="{Binding Green}" />
            <Label Text="{Binding Green,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Green = {0:X2}'}" />
            <Slider Value="{Binding Blue}" />
            <Label>
                <Label.Text>
                    <Binding Path="Blue"
                             StringFormat="Blue = {0:X2}"
                             Converter="{StaticResource floatToInt}">
                        <Binding.ConverterParameter>
                            <x:Single>255</x:Single>
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

Red 및 Green 속성 값은 Binding 태그 확장과 함께 표시된다. 

그러나 Blue 속성은 Binding 클래스를 인스턴스화하여 

명시적 float 값을 ConverterParameter 속성으로 설정할 수 있는 방법을 보여준다.

 

 

실행

 

 

 

관련영상

https://youtu.be/Pta94u9Rzx0

 

 

 

반응형

'MAUI' 카테고리의 다른 글

.NET MAUI - Binding Command  (0) 2022.08.01
.NET MAUI - Binding Relative bindings  (0) 2022.07.29
.NET MAUI - Binding 경로 (Path)  (0) 2022.07.27
.NET MAUI - Binding 모드  (0) 2022.07.26
.NET MAUI - Binding 기본  (0) 2022.07.25