데이터 바인딩 5

BooleanToVisibilityConverter 사용하기

BooleanToVisibilityConverter는 bool과 visibility값을 변환하는 WPF에 제공하는 Convert의 일종이다.
bool의 속성을 컨트롤이 Visibility에 바인딩 할 때 사용한다.

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
</Window.Resources>
<Canvas>
    <CheckBox Canvas.Left="44" Canvas.Top="32" Content="CheckBox" Height="16" Name="checkBox1" Width="110"
              IsChecked="{Binding IsTextBoxVisible, UpdateSourceTrigger=PropertyChanged}"/>
    <TextBox Canvas.Left="40" Canvas.Top="76" Height="19" Name="textBox1" Width="122"
              Visibility="{Binding IsTextBoxVisible, Converter={StaticResource BoolToVisibility}}"/>
</Canvas>

<BooleanToVisibilityConverter x:Key="BoolToVisibility"/>

BoolToVisibility를 BooleanToVisibilityConverter의 키로 설정한다.

IsChecked="{Binding IsTextBoxVisible, UpdateSourceTrigger=PropertyChanged}"

체크 박스를 IsTextBoxVisible에 바인딩 한다. UpdateSourceTrigger의 값을 PropertyChanged로 설정해서 즉시 업데이트 되도록 한다.

Visibility="{Binding IsTextBoxVisible, Converter={StaticResource BoolToVisibility}}"

TextBox를 IsTextBoxVisible에 바인딩 한다. Converter를 BooleanToVisibilityConverter로 설정하여 에디트 박스를 Show, Hide 한다.

 

using System.ComponentModel;

namespace BindingDemo
{
    /// <summary>
    /// MainWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private bool isTextBoxVisible;
        public bool IsTextBoxVisible
        {
            get { return isTextBoxVisible; }
            set
            {
                isTextBoxVisible = value;
                OnPropertyChanged("IsTextBoxVisible");
            }
        }

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }

}

BooleanToVisibilityConverter와 직접적으로 관련된 Show / Hide 코드는 없다.

바인딩 된 속성의 IsTextBoxVisible가 있다.

OnPropertyChanged("IsTextBoxVisible");

바인딩 된 모든 컨트롤이 업데이트 되도록 한다.

다운로드 : BindingDemo_BooleanToVisibilityConverter.zip

 

MultiBinding MultiConverter 사용하기

MultiBinding은 하나의 타겟 속성에 여러개의 소스 속성을 바인딩한다.
여러개의 소스 속성를 하나의 속성 값으로 변환하는 MulticConverter를 사용해보자.

"Last Name"에 성을 "First Name"에 이름을 입력하면 텍스트 블록에 "이름" "성" 순서로 출력하도록 해보자.
그래서, 두 개의 TextBox와 한 개의 TextBlock 컨트롤이 필요하다.

<Window x:Class="BindingDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:BindingDemo"
        Title="BindingDemo" Height="171" Width="230" DataContext="{Binding}">
    <Window.Resources>
        <local:NameConverter x:Key="NameConv"/>
    </Window.Resources>
    <Canvas>
        <TextBox Canvas.Left="81" Canvas.Top="0" Height="20" Name="textBoxLast" Width="115"
                 Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox Canvas.Left="81" Canvas.Top="38" Height="20" Name="textBoxFirst" Width="115"
                 Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Canvas.Left="24" Canvas.Top="100" Height="22" Name="textBlock1" Width="138">
            <TextBlock.Text>
                <MultiBinding Converter="{StaticResource NameConv}">
                        <Binding Path="FirstName"/>
                        <Binding Path="LastName"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
        <Label Canvas.Left="0" Canvas.Top="-4" Content="Last Name" Height="28" Name="label1" Width="67" />
        <Label Canvas.Left="0" Canvas.Top="34" Content="First Name" Height="28" Name="label2" Width="67" />
    </Canvas>
</Window>

xmlns:local="clr-namespace:BindingDemo"

다른 객체를 참고 하기 위한 네임스페이스를 지정한다.

<local:NameConverter x:Key="NameConv"/>

CS 코드에 있는 NameConverter를 자원 등록한다.

Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}"

textBoxLast 텍스트박스를 LastName 소스에 바인딩 한다.

Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}"

textBoxFirst 텍스트박스를 Firstname 소스에 바인딩 한다.

            <TextBlock.Text>
                <MultiBinding Converter="{StaticResource NameConv}">
                        <Binding Path="FirstName"/>
                        <Binding Path="LastName"/>
                </MultiBinding>
            </TextBlock.Text>

FirstName, LastName 멀티소스를 텍스트블록 하나의 대상에서 사용하고 있다.
두 개이 소스는 NameConverter에 의해 변환된다.

Converter를 사용하지 않고 문자열 배열할 수 있는 방법은 StringFormat을 이용하는 방법도 있다.
StringFormat="{0} {1}"와 같이 "와 {사이에 공백이 없으면 다음과 같이 에러가 발생한다.

error MC3044: MarkupExtension 식의 닫는 '}' 뒤에 텍스트 ' {1}'을(를) 사용할 수 없습니다

     <TextBlock.Text>
                 <MultiBinding StringFormat=" {0} {1}">
                         <Binding Path="FirstName"/>
                         <Binding Path="LastName"/>
                 </MultiBinding>
     </TextBlock.Text>

Binding 순서에 따라 {0} = FirstName, {1} = LastName으로 바인딩 소스가 지정된다.

   <Binding Path="LastName"/>
   <Binding Path="FirstName"/>

위 처럼 배열하면 {0}=LastName, {1}=FirstName으로 바인딩 소스가 지정된다.

 

using System.ComponentModel;

namespace BindingDemo
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string firstName;
        public string FirstName
        {
            get { return firstName; }
            set
            {
                firstName = value;
                OnPropertyChanged("FirstName");
            }
        }

        private string lastName;
        public string LastName
        {
            get { return lastName; }
            set
            {
                lastName = value;
                OnPropertyChanged("LastName");
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }

    public class NameConverter : IMultiValueConverter
    {
        public object Convert(object[] value, Type type, object parameter, System.Globalization.CultureInfo culture)
        {
            string first = value[0] as string;
            string last = value[1] as string;
            return string.Format("{0} {1}", first, last);
        }

        public object[] ConvertBack(object value, Type[] type, object parameter, System.Globalization.CultureInfo culture)
        {
            string name = value as string;
            string[] items = name.Split(' ');
            return new string[] { items[1], items[0] };
        }
    }
}

FirstName, LastName 속성에서 OnPropertyChanged 실행하여 모든 컨트롤이 업데이트 되도록 한다.

public object Convert(object[] value, Type type, object parameter, System.Globalization.CultureInfo culture)
{
    string first = value[0] as string;
    string last = value[1] as string;
    return string.Format("{0} {1}", first, last);
}

멀티 소스를 그림과 같이 value 배열에 의해서 값을 추출한다.

여기서 주의 할 사항이 있다.

string first = (string)value[0]; 과 같이 캐스팅 하면 다음과 같이 에러가 발생한다.
as string에 의해서 값을 변환 하도록 한다.

실행 화면이다.

다운로드 : BindingDemo_MultiBinding.zip

 

컨트롤 Style 사용

반복되는 컨트롤의 공통적인 속성 설정을 Style로 묶어 처리하면 편리하다.
Style을 사용할 때 바인딩을 어떻게 하는지 알아본다.

Style 없이 XAML 정의

한 개의 체크 박스와 5개의 버튼을 배치한다.
체크 박스의 ON / OFF에 따라 버튼이 활성화, 비활성화 된다.
버튼은 체크박스의 IsChecked 속성을 바딩한다.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width = "Auto"/>
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <StackPanel>
        <CheckBox Content = "Enabled" x:Name = "ButtonEnabled"/>
        <Button Content = "Button1" Width = "70" Height = "23" Margin = "5"
            IsEnabled = "{Binding IsChecked, ElementName = ButtonEnabled}"/>
        <Button Content = "Button2" Width = "70" Height = "23" Margin = "5"
            IsEnabled = "{Binding IsChecked, ElementName = ButtonEnabled}"/>
        <Button Content = "Button3" Width = "70" Height = "23" Margin = "5"
            IsEnabled = "{Binding IsChecked, ElementName = ButtonEnabled}"/>
        <Button Content = "Button4" Width = "70" Height = "23" Margin = "5"
            IsEnabled = "{Binding IsChecked, ElementName = ButtonEnabled}"/>
        <Button Content = "Button5" Width = "70" Height ="23" Margin ="5 "
            IsEnabled = "{Binding IsChecked, ElementName = ButtonEnabled}"/>
    </StackPanel>
</Grid>

<CheckBox Content = "Enabled" x:Name = "ButtonEnabled"/>

ButtonEnabled"라는 이름의 체크 박스를 만든다.

IsEnabled = "{Binding IsChecked, ElementName = ButtonEnabled}"

"ButtonEnabled" 컨트롤의 IsChecked 속성을 바인딩 한다.

 
Style 사용하기

스타일을 사용해 버튼의 설정을 처리 해 보자.

<Window.Resources>
    <Style x:Key="ButtonStyle" TargetType="Button">
        <Setter Property="Width" Value="70"/>
        <Setter Property="Height" Value="23"/>
        <Setter Property="Margin" Value="5"/>
        <Setter Property="IsEnabled" Value="{Binding IsChecked, ElementName=ButtonEnabled}"/>
    </Style>
</Window.Resources>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <StackPanel>
        <CheckBox Content="Enabled" x:Name="ButtonEnabled"/>
        <Button Content="Button1" Style="{StaticResource ButtonStyle}"/>
        <Button Content="Button2" Style="{StaticResource ButtonStyle}"/>
        <Button Content="Button3" Style="{StaticResource ButtonStyle}"/>
        <Button Content="Button4" Style="{StaticResource ButtonStyle}"/>
        <Button Content="Button5" Style="{StaticResource ButtonStyle}"/>
    </StackPanel>
</Grid>

 

Width = "70"
Height = "23"
Margin = "5"
IsEnabled = "{Binding IsChecked, ElementName = ButtonEnabled}"

위의 공통 된 속성들을 Style로 변환 하면 다음과 같다.

<Setter Property="Width" Value="70"/>
<Setter Property="Height" Value="23"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="IsEnabled" Value="{Binding IsChecked, ElementName=ButtonEnabled}"/>

Setter  Property = "속성이름" Value= "속성값"으로 변환한다.

<Button Content="Button1" Style="{StaticResource ButtonStyle}"/>

버튼의 스타일이름 ButtonStyle을 설정한다.

 

Key 생략하기

스타일에 키를 생략하고 버튼 컨트롤에 Style 지정을 생략하면 동일한 타겟이면 선언한 스타일이 설정된다.

<Window.Resources>
    <Style TargetType = "Button">
        <Setter Property = "Width" Value = "70"/>
        <Setter Property = "Height" Value = "23"/>
        <Setter Property = "Margin" Value = "5"/>
        <Setter Property = "IsEnabled" Value = "{Binding IsChecked, ElementName = ButtonEnabled}"/>
    </Style>
</Window.Resources>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width = "Auto "/>
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height = "Auto"/>
    </Grid.RowDefinitions>
    <StackPanel Grid.RowSpan = "2">
        <CheckBox Content = "Enabled" x:Name = "ButtonEnabled"/>
        <Button Content = "Button1"/>
        <Button Content = "Button2"/>
        <Button Content = "Button3"/>
        <Button Content = "Button4"/>
        <Button Content = "Button5"/>
    </StackPanel>
    <StackPanel Grid.Row = "1" Grid.Column = "1" Orientation = "Horizontal" HorizontalAlignment = "Right">
        <Button Content = "OK"/>
        <Button Content = "Cancel"/>
    </StackPanel>
</Grid>

하단에 추가한 OK, Cancel 버튼도 같은 스타일이 적용된다.

키 생략 전 : <Style x:Key="ButtonStyle" TargetType="Button">

이전과 달리 Key 생략

키 생략 후 : <Style TargetType = "Button">

 

키 생략 전: <Button Content="Button1" Style="{StaticResource ButtonStyle}"/>

이전과 달리 Style 생략

키 생략 후 : <Button Content = "Button1"/>

 

참고 ) http://cswpf.seesaa.net/index-8.html