WPF Binding

时间:2025-04-17 08:50:12

可以结合使用属性名和斜杠来遍历作为集合的属性。例如,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()方法更新绑定。如下:BindingExpression be = (Button.ContentProperty);
();
 

 

还有UpdateSource方法用来更新源。

 


 

绑定到.net属性/对象:

 

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

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

event PropertyChangedEventHandler PropertyChanged
 
实现INotifyPropertyChanged的示例如下:
using ;

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 
{
    add
    {
        this.PropertyChanged = (PropertyChangedEventHandler)(this.PropertyChanged, value);
    }
    remove
    {
        this.PropertyChanged = (PropertyChangedEventHandler)(this.PropertyChanged, value);
    }
}
#endregion
 
看了上面代码着实没看出source值改变了,前台是通过什么机制反映的,正常的情况下公开了一个事件,必须有一个对此事件的实现体,而上面代码并没有实现PropertyChanged的方法。
我猜想是Binding内部获取了这个接口并对PropertyChanged进行了赋值,因为在debug时,这个事件确实被赋值的,而赋值前的Stack是External Code调用的。
 
绑定到集合需实现INotifyCollectionChanged,但是推荐使用ObservableCollection,这个类实现了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"/>
也可以在代码这么写 = 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中预先定义:
<>
<local:CountToBackgroundConverter x:Key="myConverter"/>
</>
 
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:
可以用作返回值,以指示绑定引擎不要执行任何操作。
可用使用[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进行转化,但是可能效率不好)

 


 

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

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

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

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

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

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

 


 

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

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

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

 

 


 

Binding的验证