데이터바인딩을 사용하지 않는 경우

UI가 객체의 변화를 추적하는 안정적인 방법은 객체가 속성값이 변경될때마다 이벤트를 발생시키는 것이다.

INotifyPropertyChanged 인터페이스를 구현해야 한다

 

ex.

참조추가 - using System.ComponentModel;

상속 추가 - INotifyPropertyChanged

핸들러 등록

 public event PropertyChangedEventHandler PropertyChanged;

 

이벤트 발생 함수 선언
        protected void Notify(string propName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

 

property의 set함수를 값의 변경 확인후에 이벤트를 발생시키도록 코드 수정

ex

string name;

        public string Name
        {

            get { return this.name; }
            set {
                if (this.name == value) { return; }
                this.name = value;
                Notify("Name");

            }
        }

값변화를 확인해야하는 객체에 이벤트핸들러 추가하기

 person.PropertyChanged += person_PropertyChanged;

그의 이벤트 핸들러.

void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch(e.PropertyName)
            {
                case "Name":
                    this.nameTextBox.Text = person.Name;
                    break;
                case "Age":
                    this.ageTextBox.Text = person.Age.ToString();
                    break;
            }
        }

여기서 이벤트의 이름은 Notify()에서와 동일해야 하며 대소문자를 구분한다.

 

위 예제의 개략적인 진행상황은 다음과 같다

  1. 사용자가 버튼을 클릭하면 click 이벤트가 발생한다
  2. Click 이벤트 핸들러가 Person 객체의 나이를 가져온다
  3. Click 이벤트 핸들러에서 Person 객체의 나이를 증가시킨다.
  4. Person 의 Age 속성에서 PropertyChanged 이벤트를 발생시킨다
  5. PropertyChanged 이벤트가 UI코드내의 이벤트 핸들러로 라우팅된다.
  6. UI 코드가 나이를 표시하는 TextBox 콘트롤의 값을 11에서 12로 변경한다
  7. Button 클릭 이벤트 핸들러가 새로운 나이를 적용한 메시지 박스를 보여준다.

 

 

 

데이터바인딩

 

WPF에서의 데이터 바인딩은 데이터 바인딩 엔진에 두 속성을 등록하고 엔진이 동기화를 담당하도록 필요한 경우 적당한 타입 변환을 수행하도록 하는 작업을 의미한다.

데이터 바인딩 엔진에 동기화를 관리할 두개의 속성을 등록하려면 Binding 객체를 이용하면 된다.

바인딩이 되는 객체(binding source)와 바인딩할 요소의(binding target) 의존속성을 동기화와 타입변환을 통해서 연결한다.

객체는 데이터 원본이고 요소는 UI 컴포넌트인 듯.

ex.1

<Text Box ...>

<TextBox.Text>

<Binding Path="Age"/>

</TextBox.Text>

</TextBox>

객체는age이고 요소는 TextBoxt가 된다. 그리고 바인딩할수 있는 대상은 TextBox의 의존속성뿐이다.

ex.2

<TextBox Text="{Binding Path=Age}"/> || <TextBox Text="{Binding Age}"/>

동일한 의미이다. Path를 생략할수 있기때문에.

ex3. 다양한 기능의 바인딩 예제

 <TextBox>
            <TextBox.Foreground>
                <Binding Path="Age" Mode="OneWay" Source="{StaticResource Jack}"

                             Converter="{StacticResource ageConverter}"/>
            </TextBox.Foreground>

        </TextBox>   

여러 바인딩 특성을 하나로 묶으려면 이름과 값의 쌍을 콤마로 구분하여 나열하게 되며, 공백이나 줄바꿈도 사용할수 있다.

 

바인딩에서 TextBox 콘트롤은 바인딩 대상이고 데이터를 제공하는 객체인 바인딩 원본의 변경사항을 감시하는 역할을 담당한다. 모든 WPF 요소들은 바인딩 대상이 될수 있지만

해당 요소의 의존속성만 바인당할수 있다. 바인딩 원본 객체의 경우에는 모든 CLR 속성과 의존속성을 바인딩할수 있다.-ADO.NET 데이터원본도 가능하다 -

일반적으로 바인딩 될 원본 데이터는 데이터 콘텍스트에서 가져온다.

 

암시적 데이터원본

다른 특별한 명령이 없는 경우 바인딩은 데이터원본을 찾기 위해 data context를 탐색한다. WPF의 모든  FrameworkElement객체와 FrameworkContentElement 객체는 data context

속성을 가지고 있다. DataContext 속성은 object 타입을 사용하기떄문에 모든 객체를 사용할수 있다. 바인딩객체는 바인딩 원본으로 사용되는 객체를 탐색할때 자신이 정의된 위치부터

객체 트리를 탐색하여 DataContext 속성이 지정된 객체를 찾는다.

 

ex.<Window ...>

<Grid Name = "grid">

//...

<TextBlock ..>Name</TextBlock>

<TextBox Text="{Binding Path=Name}"/>

<TextBlock.... >Age </TextBlock>

<TextBox Text="{Binding Path=Age}"/>

</Grid>

</Window>

DataContext 탐색순서

  1. 처음 자신이 속한 TextBox의 DataContext 속성을 탐색한다
  2. TextBox가 속한 Grid의 DataContext 속성을 탐색한다
  3. TextBox가 속한 Grid가 속한 Window의 DataContext 속성을 탐색한다

 

INotifyPropertyChanged 인터페이스를 구현하고 바인딩을 걸면 된다.  이 인터페이스가 구현되어 있어야 객체의 변경사항을 감시할수 있다.

그리고 속한 객체의 DataContext에 바인딩 소스를 걸어주면 된다.

바인딩을 하면 이벤트핸들러처리, UI 초기화 등을 구현할 필요가 없다!!!

 

data island - MS IE에서 디스플레이 되는 HTML 페이지내에 존재하는 XML Data Source Object(DSO)이다. XML DSO는 InternetExplorer Version4에서 구현된 ActiveX콘트롤이다. XML DSO는 외부의 XML 파일에서 컨텐츠를 추출하여 HTML페이지로 추출하는 방식을 제공한다.

ex XAML 코드에서 사용자 정의 타입의 인스턴스 생성하기

<Window x:Class="WPF_EX_03.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WPF_EX_03"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <local:Person x:Key="John" Name="동현" Age="21"/>
       
    </Window.Resources>

//...

</Window>

ex.XAML에서 선언된 객체를 이용한 데이터 바인딩

//..

 <Canvas Name="canvas" DataContext="{StaticResource John}"> //john은 리소스의 키값. 키값은 대소문자 구분함.

//...

ex XAML에서 바인딩된 객체 사용하기

  Person person = (Person)this.FindResource("John");// resource에서 찾은 다음에 형변환.
            ++person.Age;

 

값의 변환

바인딩 소스 속성은 Int32이고 Foreground 속성은 Brush 타입이라면 그 사이에 적절한 형 변환이 있어야 한다. 값변환기는 ConvertConvertBack 두개의 메소드를 정의하는

IValueConverter 인터페이스를 구현한 객체다. Convert 메소드는 원본 데이터를 대상 U 요소의 데이터로 변활할때(예제에서는 Int32를 Brush타입으로 변환)호출된다.

ConvertBack 메소드는 UI의 데이터를 원본 데이터로 변환할때 호출된다.

ex.xaml.cs

[ValueConversion(/*sourceType*/ typeof(int), /*targetType*/ typeof(Brush))]
    public class AgeToForegroundConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureinfo)
        {
            if (targetType != typeof(Brush))
            {
                return null;
            }
            int age = int.Parse(value.ToString());
            return (age > 25 ? Brushes.Red : Brushes.Blue);

        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureinfo)
        {
            throw new NotImplementedException();
        }

    }

 

.xaml 값변환기를 이용한 바인딩

 <Window.Resources>
        <local:Person x:Key="John" Name="동현" Age="21"/>
        <local:AgeToForegroundConverter x:Key="ageConverter"/>
    </Window.Resources>
    <Canvas Name="canvas" DataContext="{StaticResource John}">
        <TextBlock Width="68" Height="20" Canvas.Left="10" Canvas.Top="17">Name : </TextBlock>
        <TextBox Text="{Binding Path=Name}" Height="19.78" Canvas.Left="76" Canvas.Top="14" Width="162"></TextBox>
        <TextBlock Height="14" Canvas.Left="10" Canvas.Top="43" Width="29.827">Age : </TextBlock>
        <TextBox Text="{Binding Path=Age}" Foreground="{Binding Path=Age,Converter={StaticResource ageConverter}}"  Name="ageTextBox"
                 Height="19.78" Canvas.Left="76" Canvas.Top="40" Width="162"></TextBox>
        <Button Name="birthdayBtn" Foreground="{Binding Path=Foreground, ElementName=ageTextBox}"
                Height="22" Canvas.Left="76" Canvas.Top="72" Width="162">생일</Button>
    </Canvas>

 

일반적인 경우 값변환기를 구현할필요가 없다 .Net1.0부터 지원하는 타입변환기를 사용할수 있기때문이다. 이런 식의 값변환기는 특정한 상황을 가지는 응용프로그램에서 유용하다.

 

유효성검사

유효성 검사코드(validation code)는 실제로는 System.Windows.Control.ValidationRule 클래스로부터 파생되는 클래스의 인스턴스이며, Validate 메소드를 오버라이딩하여 구현할수 있다.

ex. .xaml

<TextBox>

<TextBox.Text>

<Binding Path="Age" NotifyOnValidationError="True">

<Binding.ValidationRules>

<ExceptionValidationRule/>

</Binding.ValidationRule>

</Binding>

</TextBox.Text>

</TextBox>

 

.xaml.cs

 Validation.AddErrorHandler(this.ageTextBox, ageTextBox_ValidationError);   //애러이벤트 핸들러 등록
           
     
        void ageTextBox_ValidationError(object sender, ValidationErrorEventArgs e)
        {
            int age = 20;
            this.ageTextBox.Text = age.ToString();
            MessageBox.Show((string)e.Error.ErrorContent, "유효성 검사님 오류");
        }

 

사용자 정의 유효성 검사 규칙.

ValidationRule 클래스를 상속한 후 Validate메소드를 오버라이드해서 사용한다.

ex

 public class NumberRangeRule : ValidationRule
    {
        int min;
        public int Min
        {
            get { return min; }
            set { min = value; }
        }

        int max;
        public int Max
        {
            get { return max; }
            set { max = value; }
        }

        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            int number;
            if (!int.TryParse((string)value, out number))
            {
                return new ValidationResult(false, "잘못된 숫자입니다");
            }

            if (!(number >= min && number <= max))
            {
                return new ValidationResult(false, string.Format("유효한 범위는 {0}-{1}입니다", min, max));
            }

            return ValidationResult.ValidResult;
        }

    }

 <TextBox.Text>
                <Binding Path="Age" NotifyOnValidationError="True">
                    <Binding.ValidationRules>
                        <local:NumberRangeRule Min="0" Max="25"/>
                    </Binding.ValidationRules>                   
                </Binding>
            </TextBox.Text>

 

요약

데이터 바인딩은 두 값 사이의 동기화를 유지하는 것이다. 두 값 중 하나인 바인딩 대상은 의존속성이며 대체로 UI요소에서제공된다. 다른 하나는 바인딩 원본으로 CLR속성이며

XPath표현식의 결과이거나 의존속성 혹은 ADO.NET 이 제공하는 것처럼 런타임 이전에 어떤 데이터를 가지게 되는지 알수 없는 동적 속성들이 사용될수 있다.

기본적으로 대상이나 원본이 변경되면 다른 값도 업데이트되지만 여러 바인딩 모드를 이용하여(ex. One-Way, One-TIme) 이 동작을 제어할수 있다.

데이터가 변뎡될 때 변환기를 사용할수 있다면 타입변환이 발생하며 여러분이 변환이나 유효성 검사 과정에 대한 전반적인 제어가 가능해서 어떤 방법을 지정했다 하더라도 데이터의

범위를 제한하고 데이터 형식을 변환하거나 풍선도움말에 오류를 자동으로 보여줄수 있다.

 

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 9. 18. 14:42