WPF简单数据绑定

 

准备工作

1、首先实现一个简单的页面布局

代码:一个简单的个人信息编辑器布局(该布局代码具有典型意义)

<Window

  x:Class="WpfApplication1.MainWindow"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  Title="WithoutBinding"

  Height="135"

  Width="200">

    <Grid Name="grid">

        <Grid.RowDefinitions>

            <RowDefinition   Height="Auto" />

            <RowDefinition   Height="Auto" />

            <RowDefinition   Height="Auto" />

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="Auto"   />

            <ColumnDefinition Width="*"   />

        </Grid.ColumnDefinitions>

        <TextBlock Grid.Row="0"   Grid.Column="0" Margin="5"   VerticalAlignment="Center">Name:</TextBlock>

        <TextBox Name="nameTextBox"   Grid.Row="0" Grid.Column="1"   Margin="5" />

        <TextBlock   Grid.Row="1" Grid.Column="0"   Margin="5" VerticalAlignment="Center"   >Age:</TextBlock>

        <TextBox Name="ageTextBox"   Grid.Row="1" Grid.Column="1"   Margin="5" />

        <Button Name="birthdayButton"   Grid.Row="2" Grid.Column="1"   Margin="5">Birthday</Button>

    </Grid>

</Window>

 

2、实现一个类,该类必须要实现InotifyPropertyChanged接口

代码:InotifyPropertyChanged接口的标准实现

    public class Person:INotifyPropertyChanged

    {

        //1、该接口就只有一个事件PropertyChanged定义

        public   event PropertyChangedEventHandler   PropertyChanged;

        //4、Notify方法

        protected   void Notify(string   propName)

        {

            if   (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propName));

            }

        }

 

        private   string name;

        private   int age;

 

        public   int Age

        {

            get   { return age; }

            //2、注意这里的实现,只要改变了值,就要触发PropertyChanged事件

            set   {

                if   (age == value) return;

                age = value;

                //3、这里为了使代码更简练,将所有属性的调用PropertyChanged事件

                //部分的代码进行了封装,Notify方法里面

                Notify("Age");

            }

        }

 

        public   string Name

        {

            get   { return name; }

            set   {

                if   (name == value) return;

                name = value;

                Notify("Name");

            }

        }

        public   Person(string name, int   age)

        {

            Name = name;

            Age = age;

        }

    }

 

 

手工进行数据同步

3、下面我们分两个部分来进行实现,首先,我们手动实现数据绑定的过程

代码:手工实现数据绑定

    public partial class MainWindow : Window

{

//0、这里创建一个Person的对象

        private   Person person = new   Person("Tom",   11);

        public   MainWindow()

        {

            InitializeComponent();

            this.nameTextBox.Text   = person.Name;

            this.ageTextBox.Text   = person.Age.ToString();

            //1、跟踪对象变化:该类要为Person实例对象注册PropertyChanged事件

            person.PropertyChanged +=   person_PropertyChanged;

            //这一段添加事件也可写在XAML里面

            //3、跟踪界面变化:要注册TextChanged实现

            nameTextBox.TextChanged +=   nameTextBox_TextChanged;

            ageTextBox.TextChanged +=   ageTextBox_TextChanged;

            birthdayButton.Click +=   birthdayButton_Click;

        }

        //2、在事件处理函数中要将Person类成员中的变化反应到用户界面中,并视情况转换成字符串

        private   void person_PropertyChanged(object sender, PropertyChangedEventArgs   e)

        {

            switch   (e.PropertyName)

            {

                case   "Name":

                    nameTextBox.Text =   person.Name;

                    break;

                case   "Age":

                    ageTextBox.Text =   person.Age.ToString();

                    break;

            }

        }

        private   void birthdayButton_Click(object sender, RoutedEventArgs   e)

        {

            ++person.Age;

            //this.ageTextBox.Text   = person.Age.ToString();

            MessageBox.Show(string.Format("Happy   Birthday,{0},age{1}", person.Name, person.Age), "Birthday");

        }

 

        //4、在处理函数中要将界面的数据赋值给对象数据,并进行必要的类型转换

        private   void nameTextBox_TextChanged(object sender, TextChangedEventArgs   e)

        {

            person.Name = nameTextBox.Text;

        }

 

        private   void ageTextBox_TextChanged(object sender, TextChangedEventArgs   e)

        {

            int   age = 0;

            //这里要注意复习下TryParse的用法

            if   (int.TryParse(ageTextBox.Text, out age))

            {

                person.Age = age;

            }

        }

    }

 

 

开始数据绑定

核心元素:Binding类、Path属性、DataContext属性

4、可见,手工实现数据同步还是比较繁琐的,有更好的方法,那就是数据绑定,好,进入正题

我们把手工进行数据绑定时处理PropertyChanged事件和TextChanged的那些代码全部删除,1、给Window类加上一个命名空间:

xmlns:local="clr-namespace:WpfApplication1"

5、然后添加Binding类的绑定语法

        <TextBlock   Grid.Row="0" Grid.Column="0"   Margin="5" VerticalAlignment="Center">Name:</TextBlock>

        <TextBox Name="nameTextBox"   Grid.Row="0" Grid.Column="1"   Margin="5" Text="{Binding   Path='Name'}" />

        <TextBlock   Grid.Row="1" Grid.Column="0"   Margin="5" VerticalAlignment="Center"   >Age:</TextBlock>

        <TextBox Name="ageTextBox"   Grid.Row="1" Grid.Column="1"   Margin="5" Text="{Binding   Path='Age'}" />

        <Button Name="birthdayButton"   Grid.Row="2" Grid.Column="1"   Margin="5">Birthday</Button>

 

6、再给Grid添加关联数据

grid.DataContext   = person;

现在就可以实现与上面手工的数据同步一样的功效了,是不是很爽啊,这里需要注意的是,数据类实现必须要实现InotifyPropertyChanged接口

一些其他用法和概念:

隐式数据源:当我们设置了DataContext属性时,我们就是使用的隐式数据源,并且查找隐式数据源的工作是迭代进行的

数据岛:我们可以把数据写在XAML文件里,具体如下:

代码:数据岛的示例

    <Window.Resources>

        <local:Person x:Key="Tom" Name="Tom" Age="15"   />

    </Window.Resources>

这里就相当于我们定义了个Person类型的变量,需要注意的是设置x:Key属性,因为资源本质上来说是个哈希表,我们可以通过在XAML里面访问和在后台代码访问,分别形式如下:

XAML:  {StaticResource Tom}   C#:  Person peron = (Person)this.FindResource(“Tom”);

显示数据源:

应用场景:当我们要为每一个Binding对象分别设置数据源时,我们就需要用显式数据源

说白了就是设置其Source属性

代码:显式数据源

       <TextBox Name="nameTextBox"   Grid.Row="0" Grid.Column="1"   Margin="5" Text="{Binding Path=Name,   Source={StaticResource Tom}}"   />

 

 

绑定到其他控件:

应用场景:我们有时候需要将该属性绑定到其他控件的某一属性时,就需要绑定到其他控件。

就是使用Binding类的ElementName属性=该控件的Name属性

代码:绑定到其他控件

        <Button Name="birthdayButton"   Grid.Row="2" Grid.Column="1"   Margin="5" Foreground="{Binding Path=Foreground,ElementName=ageTextBox}">Birthday</Button>

 

 

数值转换:

应用场景:当数据源的数据类型与绑定的属性数据类型不一致时

核心方案:实现IValueConverter接口

代码:两个简单的转换器

    //16位数值转换器

    public class Base16Converter   : IValueConverter

    {

        //用于将绑定源数据转换成目标属性数据类型

        public   object Convert(object   value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            return   ((int)value).ToString("x");

        }

 

        //用于将目标属性数据转换成绑定源数据

        public   object ConvertBack(object   value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            return   int.Parse((string)value,   System.Globalization.NumberStyles.HexNumber);

        }

    }

    //Age转换成背景画刷转换器

    public class AgeToForegroundConverter   : IValueConverter

    {

        public   object Convert(object   value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            if   (targetType != typeof(Brush)) { return null; }

            int   age = int.Parse(value.ToString());

            return   (age > 25 ? Brushes.Red : Brushes.Black);

        }

 

        public   object ConvertBack(object   value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            throw   new NotImplementedException();

        }

    }

 

使用转换器,将转换器声明为资源,就可以在XAML中进行访问了

代码:

    <Window.Resources>

        <local:Person x:Key="Tom" Name="Tom" Age="15"   />

        <local:AgeToForegroundConverter   x:Key="ageConverter" />

        <local:Base16Converter   x:Key="base16Converter" />

    </Window.Resources>

 

        <TextBox ... Text="{Binding Path=Age,   Converter={StaticResource base16Converter}}"   />

 

验证

应用场景:确保用户输入正确格式的数据

核心方案:Binding的ValidationRules属性,Validation.Errors属性

(这里我没有理解的是关于这个Validation静态属性,究竟是属于哪一个类里面的,如果是全局的,那么怎么与当前这个Binding类相关,我只能理解为这个类型可根据当前上下文关系设置作用域)

1、设置验证规则

代码:

        <TextBox Name="ageTextBox" Grid.Row="1" Grid.Column="1" Margin="5" Foreground="{Binding Path=Age, Converter={StaticResource ageConverter}}">

            <TextBox.Text>

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

                      <Binding.ValidationRules>

                             <ExceptionValidationRule />

                      </Binding.ValidationRules>       

                </Binding>

            </TextBox.Text>

        </TextBox>

 

这个时候默认的是在TextBox失去焦点后,WPF会对非法数据以红色进行高亮显示

2、手动捕获ValidationError事件(不推荐)

需要注意的是Binding的NotifyOnValidationError设为True才能引发该事件

代码:

        public   MainWindow()

        {

            InitializeComponent();

            //grid.DataContext   = person;

 

            birthdayButton.Click +=   birthdayButton_Click;

 

            //这个语法有点特殊,因为是静态方法,所以第一个参数要指明是哪个控件对象绑定处理函数

            Validation.AddErrorHandler(ageTextBox,   ageTextBox_ValidationError);

        }

        private   void ageTextBox_ValidationError(object sender, ValidationErrorEventArgs   e)

        {

            //MessageBox.Show((string)e.Error.ErrorContent,"Validation   Error");

            ageTextBox.ToolTip = (string)e.Error.ErrorContent;

 

        }

3、自定义验证规则

代码:创建自定义的验证规则,需要继承ValidatRule类

    public class NumberRangeRule   : ValidationRule

    {

        private   int min;

        public   int Min

        {

            get   { return min; }

            set   { min = value; }

        }

        private   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, "Invalid   number format");

            }

            if   (number < min || number > max)

            {

                return   new ValidationResult(false,

                    string.Format("number is out of range ({0}-{1})",   min, max));

            }

 

            //该句等价于:

            //return   new ValidationResult(true, null);

            return   ValidationResult.ValidResult;

        }

 

使用自定义验证规则

                    <Binding.ValidationRules>

                        <local:NumberRangeRule Min="0" Max="128"   /> 

                    </Binding.ValidationRules>      

4、绑定错误消息

不需要捕获ValidationError的代码,不需要设置NotifyOnValidationError为True,完成一样的功能

代码:

        <TextBox Name="ageTextBox" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"

                 Grid.Row="1" Grid.Column="1" Margin="5" Foreground="{Binding Path=Age, Converter={StaticResource ageConverter}}">

            <TextBox.Text>

                <Binding Path="Age"   UpdateSourceTrigger="PropertyChanged">

                    <Binding.ValidationRules>

                        <local:NumberRangeRule Min="0" Max="128"   /> 

                    </Binding.ValidationRules>      

                </Binding>

            </TextBox.Text>

        </TextBox>

 

上面那段代码使用了相对数据源RelativeSource={RelativeSource Self},等价于ElementName=ageTextBox

5、验证规则触发时机

上面那段代码中的UpdateSourceTrigger="PropertyChanged",可选参数为Defaut、PropertyChanged、LostFocus、Explicit

 

 

posted @ 2011-10-29 11:12  sofootball  阅读(10538)  评论(0)    收藏  举报