0. 발전 단계
ㅈㄱㄴ
2단계 INotifyPropertyChanged 사용
public class Cust : INotifyPropertyChanged{
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
private string name;
public string Name
{
get { return name; }
set { if (name == value) return; name = value; Notify("Name"); }
}
}
멤버변수의 Set 에 다음과 같은 Notify 함수를 넣는 것은 공식이다.
public MainWindow(){
InitializeComponent();
c1 = GetData();
c1.PropertyChanged += C1_PropertyChange;
ViewData(c1);
}
private void C1_PropertyChange(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine("39: " + e.PropertyName);
switch(e.PropertyName)
{
case "Name":
txtName.Text = c1.Name;
break;
case "Age":
txtAge.Text = c1.Age.ToString();
break;
}
}
그 핸들러에 변수가 바뀔 때 필요한 작업들을 넣는다.
그러면 위에서 구현한 Notify 가 Set이 호출될 때 마다 우리가 정의한 작업을 수행할 것이다
3단계 Binding
data 가 아직 binding 되지 않아도 에러가 뜨지 않음에 주의.
이하 Binding 에 대해서 설명함.
Frame Element 는 DataContext 를 상속함.
여기에 등록된 객체는 같은 메모리를 공유하듯이 쓸 수 있음.
그런데 이러한 DataContext 는 거의 대부분의 Control, Panel 등에 다 있음.
그럼 이중에 어떤 DataContext 를 사용하느냐?
위처럼 Resource 의 x:Key 를 지정해서 위처럼 직점 지정할 수도 있고,
아니면 직접 코드로 대입할 수도 있고,
DataContext Search Tree 을 역주행해서 가장 가까이 있는 애를 사용할 수도 있음.
포맷 형식이나 범위 등 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 value, Type targetType, object parameter, CultureInfo culture) { if (targetType != typeof(Brush)) return null; int age = Int32.Parse(value.ToString()); if (age < 20) return Brushes.Green; else if (age <= 40) return Brushes.Blue; else return Brushes.Gray; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo 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 의 ValidationRules 에 바로 넣는 가장 기본적인 적용형태.
public class AgeRangeRule : ValidationRule { public int Min { get; set; } public int Max { get; set; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { int n; if (!int.TryParse((string)value, out n)) return new ValidationResult(false, "invalid for int type"); if (n >= Min && n < Max) { return new ValidationResult(true, null); } else return new ValidationResult(false, string.Format("invalid ranged({0} ~ {1}) input", Min, Max)); } }
private void txtAge_Validation_Error_Handler(object sender, ValidationErrorEventArgs 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 업데이트 하는 시점 처리
12345 <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 => 위처럼 처리. #은 각 숫자 등의 자리를 표시하는 거임. 밖에도 다양한 지정된 형식이 있음.
이때 처음에 글자를 안적으면 에러가 뜸. 이 때 빈 공간은 {}로 표기함.
12 <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
국가 코드에 따라 날짜 등의 단위나 언어표기 등을 바꿀 수도 있음.
댓글 없음:
댓글 쓰기