WPF 绑定

WPF里分三种Binding:Binding, PriorityBindingMultiBinding,这三种Binding的基类都是BindingBase,而BindingBase又继承于MarkupExtension

Binding

提供对绑定定义的高级别访问,绑定将绑定目标对象(通常为 WPF 元素)的属性与任何数据源(例如数据库、XML 文件或包含数据的任何对象)连接起来。

常见的使用Binding的代码:
C#

Binding binding = new Binding();
// Set source object
binding.Source = treeView;
// Set source property
binding.Path = new PropertyPath("SelectedItem.Header");
// Attach to target property
currentFolder.SetBinding(TextBlock.TextProperty, binding);

XAML:

<TextBlock x:Name=”currentFolder” DockPanel.Dock=”Top”
Text=”{Binding ElementName=treeView, Path=SelectedItem.Header}”
Background=”AliceBlue” FontSize=”16”/>

所有FrameworkElement都包含SetBinding方法:SetBinding(DependencyProperty dp, String path), SetBinding(DependencyProperty dp, BindingBase binding),可以看出,FrameworkElement中的SetBinding只对DependencyProperty有效。

另一种设置Binding的方法是:BindingOperations.SetBinding(currentFolder, TextBlock.TextProperty, binding);
BindingOperations.SetBinding的原型是

public static BindingExpressionBase SetBinding(
	DependencyObject target,
	DependencyProperty dp,
	BindingBase binding
)

第一个参数是DependencyObject,所以我们可以对自定义DependencyObject或者继承自DependencyObject的类进行绑定。当然第二个参数还是DependencyProperty。

清除Binding:
BindingOperations.ClearBinding(currentFolder, TextBlock.TextProperty); //删除currentFolder上的TextBlock.TextProperty绑定
BindingOperations.ClearAllBindings(currentFolder); //删除currentFolder上的所有绑定

直接对dependency property赋值也可以解除binding, 不过只对单向binding有效。


Bingding的源:

有三个属性用来设置源:ElementName(string)、Source(Object) 和 RelativeSource(RelativeSource)。注:这三个只能指定一个,否则异常。
ElementName: 源为一个元素(Element),这里用的是此元素中设置的Name属性。
Source:以object作为源。<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>
RelativeSource: 源相对于绑定目标的位置。
                        源是元素本身:{Binding RelativeSource={RelativeSource Self}}
                        源是Tempalte中元素的Parent:{Binding RelativeSource={RelativeSource TemplatedParent}}
                        源是绑定以collection形式的前一个数据:{Binding RelativeSource={RelativeSource PreviousData}},MSDN上关于PreviousData的说明并不多,这里有一篇文章可以参考
                        以上三项为RelativeSource中的Static值,使用这些值可以减少内存开销
                        源是Ancestor(可能比parent还高):{Binding RelativeSource={RelativeSource FindAncestor,
                                                                     AncestorLevel=n, AncestorType={x:Type desiredType}}}


Path:

Binding中的Path是 PropertyPath对象。

    • 在最简单的情况下,Path 属性值是要用于绑定的源对象的属性名称,如 Path=PropertyName。
    • 通过类似于 C# 中使用的语法,可以指定属性的子属性。例如,子句 Path=ShoppingCart.Order 将绑定设置为对象的子属性 Order 或属性 ShoppingCart。
    • 若要绑定到附加属性,请将附加属性用括号括起。例如,若要绑定到附加属性 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=.}”。


      BindingExpression

      Binding 类是高级别类。BindingExpression 类是基础对象,用于保持绑定源与绑定目标之间的连接。Binding 中包含可在多个 BindingExpression 对象之间共享的所有信息。也就是说,可以把一个Binding对象绑定对n个元素上,而针对这n个元素,分别有相应的n个BindingExpresion对象。
      Binding可以直接绑定普通的.net实例,比如int值。但是如果后台改变int值了,前台不能显示改变后的值,这时可以调用UpdateTarget()方法更新绑定。如下:BindingExpressionbe = button.GetBindingExpression(Button.ContentProperty);
      be.UpdateTarget();

      还有UpdateSource方法用来更新源。


      绑定到.net属性/对象:

      上面提到Binding绑到普通的.net属性,如果source变化了,UI上是不会显示的,除了用BindingExpression每次显式更新Target外,还可以使用如下技术:

      绑定到单个对象需实现INotifyPropertyChanged接口,这个接口只有一个成员:

      event PropertyChangedEventHandler PropertyChanged
       
      实现INotifyPropertyChanged的示例如下:
      using System.ComponentModel;
      
      namespace SDKSample
      {
        // This class implements INotifyPropertyChanged
        // to support one-way and two-way bindings
        // (such that the UI element updates when the source
        // has been changed dynamically)
        public class Person : INotifyPropertyChanged
        {
            private string name;
            // Declare the event
            public event PropertyChangedEventHandler PropertyChanged;
      
            public Person()
            {
            }
      
            public Person(string value)
            {
                this.name = value;
            }
      
            public string PersonName
            {
                get { return name; }
                set
                {
                    name = value;
                    // Call OnPropertyChanged whenever the property is updated
                    OnPropertyChanged("PersonName");
                }
            }
      
            // Create the OnPropertyChanged method to raise the event
            protected void OnPropertyChanged(string name)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(name));
                }
            }
        }
      }
      或者显式实现INotifyPropertyChanged:
      
      #region INotifyPropertyChanged Members
      event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
      {
          add
          {
              this.PropertyChanged = (PropertyChangedEventHandler)Delegate.Combine(this.PropertyChanged, value);
          }
          remove
          {
              this.PropertyChanged = (PropertyChangedEventHandler)Delegate.Remove(this.PropertyChanged, value);
          }
      }
      #endregion
       
      看了上面代码着实没看出source值改变了,前台是通过什么机制反映的,正常的情况下公开了一个事件,必须有一个对此事件的实现体,而上面代码并没有实现PropertyChanged的方法。
      我猜想是Binding内部获取了这个接口并对PropertyChanged进行了赋值,因为在debug时,这个事件确实被赋值的,而赋值前的Stack是External Code调用的。
       
      绑定到集合需实现INotifyCollectionChanged,但是推荐使用ObservableCollection<T>,这个类实现了INotifyCollectionChanged和INotifyPropertyChanged。
       
      附:当绑定到普通的.net属性时,WPF使用反射取得source的值,当对象实现ICustomTypeDescriptor时,WPF使用这个接口取得值,性能上会有所提升。

      DataContext:

      DataContext在共享资源时最有用。

      <StackPanel x:Name="parent" DataContext="{StaticResource photos}">
      <Label x:Name="numItemsLabel"
      Content="{Binding Path=Count}"
      DockPanel.Dock="Bottom"/>
      也可以在代码这么写parent.DataContext = photos;
       

      Value Converters:

      IValueConverter可以在绑定时加入自己的逻辑,很好。

      public class RawCountToDescriptionConverter : IValueConverter
      {
      	public object Convert(object value, Type targetType, object parameter,
      		CultureInfo culture)
      	{
      		// Let Parse throw an exception if the input is bad
      		int num = int.Parse(value.ToString());
      		return num + (num == 1 ? " item" : " items");
      	}
      
      	public object ConvertBack(object value, Type targetType, object parameter,
      		CultureInfo culture)
      	{
      		throw new NotSupportedException();
      	}
      }
      IValueConverter就两个方法需要自己实现,Convert和ConvertBack,一个转过来,一个转过去。
      XAML代码如下
      <Label Background="{Binding Path=Count, Converter={StaticResource myConverter},
      Source={StaticResource photos}}"/>
      这里的myConverter是个resource,需要在xaml中预先定义:
      <Window.Resources>
      <local:CountToBackgroundConverter x:Key="myConverter"/>
      </Window.Resources>
       
      Path对应的Count值会作为第一个参数value传给Convert方法。
       
      注意,返回的值一定要是绑定时对应的值,比如绑定时需要绑到Geometry类上,那么Convert返回的也必须是Geometry类。
       
      Convert方法还带有一个parameter参数,可以在xaml中这么使用
      <Label Background="{Binding Path=Count, Converter={StaticResource myConverter},
      ConverterParameter=Yellow, Source={StaticResource photos}}"/>

      ConverterParameter是object类型。
      C#代码中就可以得到parameter的值了。

      TIP:
      可以用Binding.DoNothing作返回值,以指示绑定引擎不要执行任何操作。
      可用使用[ValueConversion(typeof(DateTime), typeof(String))]来标识Converter要转化和返回的值类型,第一个参数是soure,第二个参数是target。这样在编译时,如果类型不匹配的话,编译器会抛出异常:error CS0592: Attribute ‘ValueConversion‘ is not valid on this declaration type. It is only valid on ‘class‘ declarations.

      .net自带一些converter,比如常用的BooleanToVisibilityConverter,可以根据checkbox是否勾上来隐藏其他控件。

      在collection中使用converter:使用DateTemplate,在其中使用Converter。(也可以使用Converter对整个collection进行转化,但是可能效率不好)


      Binding.Mode

      指示源和目标间数据流的方向。

      OneWay 源更新时,目标也更新
      TwoWay 源更新时目标也更新,或者目标更新时同时更新源
      OneTime 仅当应用程序启动时或 DataContext 进行更改时更新目标属性。绑一次就不更维护更新,目标相当于源的一次性镜像
      OneWayToSource 目标更新时更新源,和OneWay相反

      大部分WPF自带的控件的dependency property默认的是OneWay,像TextBox.Text默认的是TwoWay。
      值得注意的事,只读属性只能设置成OneWay,不能是TwoWay,否则运行时异常。

      对于 OneWay 或 TwoWay 绑定,对源的动态更改不会自动传播到目标。必须在源对象上实现 INotifyPropertyChanged 接口。 
      对于 TwoWay 绑定,对目标的更改不会自动传播到源,除非绑定目标是 Text 属性。在这种情况下,更新仅在 TextBox 失去焦点时发生。 
      对于 OneTime 和 OneWay 绑定,对 SetValue 的调用会自动更改目标值并删除绑定。

      再次提醒,源要实现了INotifyPropertyChanged 接口才能把改变反映到目标上。

      OneWayToSource 用于多个目标更改一个源的情况,可以想像成多人录入。或者用来实现源和目标倒置的情况。


      Binding.UpdateSourceTrigger

      指示使用TwoWay或OneWayToSource时,目标在什么情况下更新源。有三个枚举值

      PropertyChanged:目标属性改变时更新源
      LostFocus:失去焦点时更新源
      Explicit:只有在显式调用BindingExpression.UpdateSource方法时才更新源。BindingExpression可以通过BindingOperations.GetBindingExpression或FrameworkElement.GetBindingExpression方法获得

      Binding类中提供了SourceUpdated和TargetUpdated事件,可以用来记些log,不过必须相应的NotifyOnSourceUpdated或NotifyOnTargetUpdated设置成true才会激发事件。

时间: 2024-08-27 20:21:06

WPF 绑定的相关文章

WPF绑定的ListBox获取ListBoxItem及GoToState应用

现公司项目中需要制作一个扇形菜单,菜单项是用ListBox重写Style实现的,其数据是绑定的.菜单的每一项都有Normal,MouseOver和Selected三种状态,这三种状态当然可以通过鼠标移动和点击控制,但现在要通过代码来改变控件外观实现三种状态切换,该如何处理呢?   1.WPF绑定的ListBox获取ListBoxItem WPF中如果ListBox的ItemSource为绑定的,则ListBox.Items为绑定的数据源,而非ListBoxItem.如果直接通过如下代码会发现无法

WPF绑定之索引器值变化通知

背景 在某些应用中,需要在界面上绑定到索引器,并在值发生变化时实时更新. 解决方案 只要将包含索引器的类实现INotifyPropertyChanged接口,并在索引值更改时引发PropertyChanged事件,并将属性名称设置为Item[]即可.示例代码如下: public class NotifyDictionary : INotifyPropertyChanged { private readonly Dictionary<string, string> _dictionary = n

WPF绑定

WPF绑定使用的源属性必须是依赖项属性,这是因为依赖项属性具有内置的更改通知支持,元素绑定表达式使用了Xaml扩展标记,WPF绑定一个控件是使用Binding.ElementName,绑定非控件对象时使用Source,RelativeSource,DataContext属性(WPF特有,而非XAML),只能绑定对象的共有字段.下边是部分Binding 属性名,完整列表参考 :http://msdn.microsoft.com/zh-cn/library/vstudio/ms750413.aspx

WPF快速入门系列(4)——深入解析WPF绑定

一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信息来设置目标对象的属性.目标属性总是依赖属性.然而,源对象可以是任何内容,可以是一个WPF元素.或ADO.NET数据对象或自定义的数据对象等.下面详细介绍了WPF绑定中的相关知识点. 二.绑定元素对象 2.1 如何实现绑定元素对象 这里首先介绍绑定最简单的情况——绑定元素对象,即数据源是一个WPF元

[WPF]绑定到界面的数组不支持调度线程以外对其更改的办法

[原]WPF编程经常遇到一个问题: 某个数组己绑定到主界面某控件中,然后在后台程序中需要对数组增(减)数据,然后程序就会报错, 程序提示:该类型的CollectionView 不支持从调度程序线程以外的线程对其SourceCollection进行的更改. 如下图所示: 既然不能这样操作,就得想一个办法来解决,现在先把把出现错误的程序全部列出来,然后再来根据解决办法进行修改, 本测试程序先建一个学生类: using System; using System.Collections.Generic;

WPF 绑定密码

原文:WPF 绑定密码 我们发现我们无法绑定密码框的密码,PasswordBox 的 Password 不能绑定. 我们想做 MVVM ,我们需要绑定密码,不能使用前台 xaml.cs 监听 密码改变得到密码的值,传到 ViewModel . 本文提供一个简单方法来绑定 WPF 的 PasswordBox 的 Password .这种方法不仅在 WPF 可以使用,在 UWP 也可以使用.关于 UWP 绑定密码,可以在我博客 win10 uwp 绑定密码 查看. 我在网上找的很多大神给出的可以解决

WPF绑定BitMapImage

先说下图片文件存在服务器.wpf常用绑定图片地址没办法用.忽然想到,convert能否转字节数据?实验了下可以. 图片绑定字节数组. convert代码 public class PictureConvert : IValueConverter { /// <summary> /// 转换图片 /// </summary> /// <param name="value"></param> /// <param name="

WPF绑定Radiobutton到enum

WPF中经常会需要用到多个Radiobutton选择项,并且需要将选中的选项映射到某个枚举值enum中. 这种情形的解决办法如下: 1) 需要创建一个UserControl,举个例子代码如下: <UserControl x:Class="ShangfeiXApp.XModeUserControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="ht

WPF绑定到集合

什么是集合视图? 集合视图是位于绑定源集合顶部的一层,您可以通过它使用排序.筛选和分组查询来导航和显示源集合,而无需更改基础源集合本身.集合视图还维护着一个指向集合中的当前项的指针.如果源集合实现了 INotifyCollectionChanged 接口,则CollectionChanged 事件引发的更改将传播到视图. 如果将控件直接绑定到集合,则 WPF 会绑定到其默认视图.此默认视图由直接到同一集合的所有绑定共享,因此一个绑定控件或代码对默认视图所做的更改(如排序或对当前项指针的更改)会反