WPF数据绑定(1-简单数据绑定)

小疯最近的项目组工作开发中需要用到WPF,所以最近在学习WPF,小疯对于WPF中的绑定比较迷糊,认为这里需要多注意。然后小疯在这里找到了一篇文章,转过来分享一下:

数据绑定就是将各种数据与用户展现控件进行关联的过程。WPF的数据绑定机制可以以最少的代码方便地处理这样的关联。

简单数据绑定

1 无数据绑定

在实现这样的用户交互的时候:

clip_image002[4]

在没有任何数据绑定机制实现的时候无非采用

this.nameTextBox.Text = person.Name;

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

这样的赋值的方法,而一旦数据改变了,将界面的值写回给person对象。

1.1对象改变

我们现在需要当对象发生改变的时候,UI的现实如何同步跟着改变呢?

实现起来还是比较复杂的,看如下代码就明白了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;//INotifyPropertyChanged

namespace SimpleDataBinding
{
    class Person:INotifyPropertyChanged 
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void Notify(string PropName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this,new PropertyChangedEventArgs(PropName));
            }
        }

        public Person()
        {
            _Age = 0;
            _name = "Null";
        }

        private string _name;
        public string Name
        {
            get
            {  return _name; }
            set
            {
                if (value == _name)
                { return; }
                _name = value;//注意:不能用this.Name来赋值,如果这样形成循环调用,栈溢出
                Notify("Name");
            }
        }

        private int _Age;
        public  int Age
        {
            get
            { return _Age; }
            set
            {
                if (value == _Age) return;
                _Age = value;
                Notify("Age");
            }
        }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace SimpleDataBinding
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class WithoutDataBinding : Window
    {
        private Person _person;

        public WithoutDataBinding()
        {
            InitializeComponent();

            //可以采用如下对象初始化,但本示例为了使用使用属性改变UI改变就先不赋值。
            //_person = new Person
            //{
            //    Name = "zhangying",
            //    Age = 28
            //};
            _person = new Person();

            _person.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
           {
               switch (e.PropertyName)
               {
                   case "Name":
                       this.txt_name.Text = _person.Name;
                       break;
                   case "Age":
                       this.txt_age.Text = _person.Age.ToString();
                       break;

               }
           };

        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            _person.Name = "zhangying";
            _person.Age = 28;
        }

    }
}

1.2 UI改变

当UI的输入值发生变化的时候,如何同步对象的值呢?这需要给他们设置事件代码:

private void txt_name_TextChanged(object sender, TextChangedEventArgs e)

{

_person.Name = this.txt_name.Text;

}

private void txt_age_TextChanged(object sender, TextChangedEventArgs e)

{

int age = 0;

if(int.TryParse(this.txt_age.Text,out age)

{

_person.Age = age ;

}

}

2数据绑定

上面的示例是使用人工的手段将对象属性与UI显示进行同步,而WPF数据绑定则是注册两个属性与数据绑定引擎,来处理同步,合适的数据转换,如下图:

clip_image004[4]

2.1 Bindings

1.使用元素属性绑定:

<TextBox>

<TextBox.Text>

<Binding Path="Age" />

</TextBox.Text>

</TextBox>

2. 简化属性绑定

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

更简化方式:

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

看一个更多特性的绑定方式(属性元素的方式):

<TextBox>

<TextBox.Foreground>

<Binding Path="Age" Mode="OneWay" Source="{StaticResource Tom}"

Converter="{StaticResource ageConverter}" />

</TextBox.Foreground>

</TextBox>

上面的绑定可以采用如下简洁方式:

Foreground="{Binding Path=Age, Mode=OneWay, Source={StaticResource Tom},

Converter={StaticResource ageConverter}}" />

注意上面的引号的用法和不用引号的用法,使用属性元素的方式必须用引号。

下面图表列出绑定到一个对象时候可能用到的属性:

clip_image006[4]

clip_image008[4]

这些属性后面将一一介绍到。

2.2 隐式数据源Implicit Data Source

在WPF中,每个FrameworkElement 和FrameworkContentElement对象都有一个DataContex属性,该属性是Object类型,所以你可以给它指定任何类型的值。当我们定义个对象作为绑定对象,则绑定引擎在逻辑树上寻找该数据库绑定源,如下图所示:

clip_image010[4]

2.3 数据岛Data Islands

看以下代码示例就明白了:

clip_image012[4]

private void btn_Click(object sender, RoutedEventArgs e)

{

Person person = (Person)this.FindResource("zy");

MessageBox.Show(string.Format("I am {0},Age is {1}",person.Name,person.Age.ToString()));

}

2.4 显示数据源Explicit Data Source

显示数据源设置对于多个数据源是很重要的,我们可以通过绑定属性的Source来实现。

clip_image014[4]

注意上面两个文本框设置了不同的数据源的属性。

2.5 绑定到其他控件binding to other controls

如下代码所示:

<Grid>

<Slider Name="slider1" />

<TextBlock Text="{Binding Path=Value,ElementName=slider1}"/>

</Grid>

2.6 值转换 Value Conversation

上面的代码我们发现绑定是将一个数值类型绑定到一个字符串,类型不符合,这究竟是如何做到的呢?原来这是一个号称Value Converter类的功劳,它实现了IValueConverter接口,该接口有两个方法:Convert和ConvertBack。(该接口在System.Windows.Data命名空间,PresentationFramework.dll程序集中),实现 IValueConverter 接口时,最好用ValueConversionAttribute 属性来修饰此实现,以便向开发工具指示转换所涉及的数据类型,如下面的示例所示:(以下示例摘录自MSDN)

看下面的使用方法:

clip_image016[4]

使用方法,先指定数据源

clip_image018[4]

clip_image020[4]

2.7 Editable Value Conversion

2.8 Validation

一个验证规则就是在一条目标数据更新来源数据的时候的一段数据验证代码,这段代码一般是继承ValidationRule类并覆盖Validate方法,一个内置的规则称为ExceptionValidationRule

如下代码我们可以看到使用最基本的验证的方式:

clip_image022[4]

当我们运行程序的时候

clip_image024[4]

红色边框显示输入框数据格式错误。但是我们发现这样仍然不够友好,因为没有消息提示到底为什么错误。

为了显示错误,需要使用绑定的事件来侦听ValidationError,代码示例很容易说明这点:

clip_image026[4]

这样还不行,还必须如下图设置:

clip_image028[4]

运行结果大家肯定很明白了,当然这也许还不是我们需要的,我们可以更多地控制行为,这就要求我们自定义验证规则。下面我们以一个实例来讲解自定义验证规则。

首先我们通过继承ValidationRule类然后覆写validate方法,还是让代码说明一切吧:

clip_image030[4]

clip_image032[4]

在实际项目中,可能大家对错误提示采用对话框不是很喜欢,最好的方式是WindowsForm的验证方式Tooltip,但有一点需要注意ValidateRule没有类似ValidateSucess事件让我们清除错误的时候产生的Tooltip,以至于整个Tooltip的显示很奇怪。

2.9绑定路径句法Binding Path Syntax

以下列出MSDN上的描述:

使用 Path 属性可以指定您要绑定到的源值:

· 在最简单的情况下,Path 属性值是要用于绑定的源对象的属性名,如 Path=PropertyName。

· 在 C# 中可以通过类似语法指定属性的子属性。 例如,子句 Path=ShoppingCart.Order 设置与对象或属性 ShoppingCart 的 Order 子属性的绑定。

· 若要绑定到附加属性,应在附加属性周围放置圆括号。 例如,若要绑定到附加属性 DockPanel..::.Dock,则语法是 Path=(DockPanel.Dock)。

· 可以在要应用索引器的属性名后面的方括号内指定属性的索引器。 例如,子句 Path=ShoppingCart[0] 将绑定设置为与属性的内部索引处理文本字符串“0”的方式对应的索引。 还支持嵌套的索引器。

· 可以在 Path 子句中混合索引器和子属性;例如,Path=ShoppingCart.ShippingInfo[MailingAddress,Street].

· 在索引器内部,您可以有多个由逗号 (,) 分隔的索引器参数。 可以使用圆括号指定每个参数的类型。 例如,您可以有 Path="[(sys:Int32)42,(sys:Int32)24]",其中 sys 映射到 System 命名空间。

· 如果源为集合视图,则可以用斜杠 (/) 指定当前项。 例如,子句 Path=/ 用于设置到视图中当前项的绑定。 如果源为集合,则此语法指定默认集合视图的当前项。

· 可以结合使用属性名和斜杠来遍历作为集合的属性。 例如,Path=/Offices/ManagerName 指定源集合的当前项,该源集合包含也作为集合的 Offices 属性。 其当前项是一个包含 ManagerName 属性的对象。

· 也可以使用句点 (.) 路径绑定到当前源。例如,Text=”{Binding}” 等效于 Text=”{Binding Path=.}”。

转义机制

· 在索引器 ([ ]) 内部,插入符号 (^) 用于对下一个字符进行转义。

· 如果您在 XAML 中设置 Path,则还需要使用 XAML 实体对 XAML 分析程序专用的某些字符进行转义:

· 使用 &amp; 对字符“&”进行转义。

· 使用 &gt; 对结束标记“>”进行转义。

· 此外,如果您使用标记扩展语法描述属性中的整个绑定,则需要使用反斜杠 \ 对 WPF 标记扩展分析程序专用的字符进行转义:

· 反斜杠 \ 本身是转义字符。

· 等号 (=) 将属性名与属性值隔开。

· 逗号 (,) 用于分隔属性。

· 右大括号 (}) 是标记扩展的结尾。

Binding the ToolTip property to the validation error message

<TextBox

Name="ageTextBox" ...

ToolTip="{Binding

ElementName=ageTextBox,

Path=(Validation.Errors)[0].ErrorContent}">

<TextBox.Text>

<Binding Path="Age">

<!-- No need for NotifyOnValidationError="true" -->

<Binding.ValidationRules>

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

</Binding.ValidationRules>

</Binding>

</TextBox.Text>

</TextBox>

2.10相对数据源Relative Source

还是代码来说明更易理解

<TextBox ...

ToolTip="{Binding RelativeSource={RelativeSource Self},

Path=(Validation.Errors)[0].ErrorContent}">

在上面实例中我们采用 ElementName=txtDate这样的方式指定的,这里使用self来表示引用该标签所在的元素生成的对象,当然我们还可以使用类似于parent指向上一级,关于Relative resource在后续章节会继续讨论。

2.11更新源数据触发器Update Source Trigger

在前面我们基本上是在目标控件的失去焦点的时候发生相关验证并更新数据源的,而实际上更新数据源的机制有多种类型,如下:

namespace System.Windows.Data

{

     public enum UpdateSourceTrigger

     {

          Default = 0, // updates "naturally" based on the target control

          PropertyChanged = 1, // updates the source immediately

          LostFocus = 2, // updates the source when focus changes

          Explicit = 3, // must call BindingExpression.UpdateSource()

      }

}

而在XAML里面必须如下声明:

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

猜你喜欢

转载自my.oschina.net/yexiaofeng/blog/1826330