2020년 7월 3일 금요일

WPF - DataBinding

0. 발전 단계

1단계 데이터들의 동기화 되기 위해서 따로 코딩하는것


ㅈㄱㄴ

2단계 INotifyPropertyChanged 사용

public class Cust : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify(string propName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(thisnew PropertyChangedEventArgs(propName));
        }
    }


    private string name;
    public string Name
    {
        get { return name; }
        set { if (name == valuereturn; name = valueNotify("Name"); }
    }
}

멤버변수의 Set 에 다음과 같은 Notify 함수를 넣는 것은 공식이다.


public MainWindow()
{
    InitializeComponent();
    c1 = GetData();
    c1.PropertyChanged += C1_PropertyChange;

    ViewData(c1);
}

private void C1_PropertyChange(object senderPropertyChangedEventArgs e)
{
    Debug.WriteLine("39: " + e.PropertyName);

    switch(e.PropertyName)
    {
        case "Name":
            txtName.Text = c1.Name;
            break;
        case "Age":
            txtAge.Text = c1.Age.ToString();
            break;
    }
}
INotifyPropertyChanged 를 상속받으면 PropertyChangedEventHandler 를 얻는다.
그 핸들러에 변수가 바뀔 때 필요한 작업들을 넣는다.
그러면 위에서 구현한 Notify 가 Set이 호출될 때 마다 우리가 정의한 작업을 수행할 것이다


3단계 Binding

data 가 아직 binding 되지 않아도 에러가 뜨지 않음에 주의.

이하 Binding 에 대해서 설명함.




1. 데이터 지정

Frame Element 는 DataContext 를 상속함.
여기에 등록된 객체는 같은 메모리를 공유하듯이 쓸 수 있음.
그런데 이러한 DataContext 는 거의 대부분의 Control, Panel 등에 다 있음.

그럼 이중에 어떤 DataContext 를 사용하느냐?


1
    <Grid x:Name="grdMain" DataContext="{StaticResource custList}">
cs

위처럼 Resource 의 x:Key 를 지정해서 위처럼 직점 지정할 수도 있고,
아니면 직접 코드로 대입할 수도 있고,
DataContext Search Tree 을 역주행해서 가장 가까이 있는 애를 사용할 수도 있음.


2. 데이터 바인딩 문법

1
2
3
4
5
<TextBox>
            <TextBox.Text>
                <Binding Path="Name" Source="{StaticResource c3}" Mode="OneWay"/> <!--Field Name-->
            </TextBox.Text>
</TextBox>
cs

Binding 을 따로 뺀 것이 가장 기본적인 형태임.

Path 는 객체의 멤버의 이름 등이 들어가고
Source 는 우리가 어떤 데이터를 쓸지 지정할 수 있음.
Mode 는 읽기, 읽고 쓰기 등의 모드임.

Path 만 필수고 나머지는 기본값이 있음.
Source 는 자기나 부모의 DataContext 를 읽을 거임.


1
<TextBox Text="{Binding Path=Name, Source={StaticResource c3}, Mode=OneWay}"/>
cs

이걸 한줄에 나타내면 위와 같음과 같음.


1
<TextBox Text="{Binding Name}"/>
cs

아무것도 없이 Path 에 해당되는 값만 넣어도 됨.



1
<TextBox  Foreground="{Binding ElementName=txtName, Path=Foreground}"/>
cs


ElementName 을 통해 다른 panel, controll 에 접근 가능.

x:key 로 지정한 이름이 사용됨.




3. Value Converter

public class Int32ToBrush : IValueConverter
{
    public object Convert(object valueType targetType,
        object parameterCultureInfo culture)
    {
        if (targetType != typeof(Brush))
            return null;
 
        int age = Int32.Parse(value.ToString());
 
        if (age < 20return Brushes.Green;
        else if (age <= 40return Brushes.Blue;
        else return Brushes.Gray;
    }
 
    public object ConvertBack(object valueType targetType, 
        object parameterCultureInfo culture)
    {
        return null;
    }
}
IValueConverter 를 상속해 만든 Converter 를 Resource 에 전역변수처럼 둠.

1
<TextBox x:Name="txtName" Foreground="{Binding Path=Age, Converter={StaticResource AgeConverter}}"/>
cs
그러한 컨버터를 지정하면 원하는 데이터를 변경가능함.


4. Validation

1
2
3
4
5
6
7
8
9
<TextBox.Text>
       <Binding Path="Age" 
        Converter="{StaticResource HexConverter}"
        NotifyOnValidationError="True">
                   <Binding.ValidationRules>
                       <ExceptionValidationRule/>
                   </Binding.ValidationRules>
       </Binding>
</TextBox.Text>
cs
포맷 형식이나 범위 등 Binding 값에 제한을 두고 싶을 때 사용함.
위는 Binding 의 ValidationRules 에 바로 넣는 가장 기본적인 적용형태.


public class AgeRangeRule : ValidationRule
{
    public int Min { getset; }
    public int Max { getset; }
    public override ValidationResult Validate(object valueCultureInfo cultureInfo)
    {
        int n;
        if (!int.TryParse((string)valueout n))
            return new ValidationResult(false"invalid for int type");
 
        if (n >= Min && n < Max)
        {
            return new ValidationResult(truenull);
        }
        else return new ValidationResult(false,
            string.Format("invalid ranged({0} ~ {1}) input", Min, Max));
 
    }
}
1
2
3
4
<Binding.ValidationRules>
         <!--<ExceptionValidationRule/>-->
         <local:AgeRangeRule Min="0" Max="125"/>
</Binding.ValidationRules>
cs
규칙은 위처럼 ValidationRule 을 상속한 객체로 커스터마즈 가능함.


1
Validation.AddErrorHandler(this.txtAge, txtAge_Validation_Error_Handler);
cs
private void txtAge_Validation_Error_Handler(object senderValidationErrorEventArgs e)
{
    MessageBox.Show(e.Error.ErrorContent.ToString(), "validation check error");
}
Rule 을 위반할 시에 어떻게 할 지는 위처럼 커스터마이즈 해야한다.
단 NotifyOnValidationError 을 True 로 해야지만 에러처리함수가 발동된다.


1
ToolTip="{Binding ElementName=txtAge, Path=(Validation.Errors)[0].ErrorContent}"
cs
아니면 ToolTip 에 바인딩해서 이런 과정을 생략도 할 수 있다.


5. 기타 속성

1
RelativeSource={RelativeSource Self}
cs
RelativeSource => ElementName 으로 보통은 x:Name 을 이용해 접근하지만, 이게 불가능한 경우 이걸 사용할 수 있다.

1
UpdateSourceTrigger="LostFocus"
cs
UpdateSourceTrigger => Binding 업데이트 하는 시점 처리

1
2
3
4
5
<TextBlock Text="{Binding ElementName=Win, Path=ActualWidth, StringFormat=Window Width:{0:#,###}}"/>
<TextBlock Text="{Binding ElementName=Win, Path=ActualWidth, StringFormat=Window Width:{0:#,##0.00}}"/>
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, StringFormat=Date : {0:yyyy-MM-dd}}"/>
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, StringFormat=Date : {0:HH:mm:ss}}"/>
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, StringFormat={}{0:HH.}}"/>
cs
StringFormat => 위처럼 처리. #은 각 숫자 등의 자리를 표시하는 거임. 밖에도 다양한 지정된 형식이 있음.

이때 처음에 글자를 안적으면 에러가 뜸. 이 때 빈 공간은 {}로 표기함.

1
2
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, ConverterCulture='ko-KR', StringFormat={}{0:D}}"/>
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, ConverterCulture='en-US', StringFormat={}{0:D}}"/>
cs
국가 코드에 따라 날짜 등의 단위나 언어표기 등을 바꿀 수도 있음.





댓글 없음:

댓글 쓰기

List