昨天在网上看到一个网友问如何在WPF里面实现相互依赖的属性,例如下面一个类:
using System;
public class RtdField
{
#region SizeX
public Double X1
{
get { return x1; }
set
{
x1 = value;
OnPropertyChanged("X1");
OnPropertyChanged("X");
OnPropertyChanged("OffsetX");
}
}
private Double x1;
public Double X2
{
get { return x2; }
set
{
x2 = value;
OnPropertyChanged("X2");
OnPropertyChanged("X");
OnPropertyChanged("OffsetX");
}
}
private Double x2;
public double X
{
get { return x1 + x2; }
set
{
double hx = value / 2.0;
double ox = OffsetX;
x1 = hx - ox;
x2 = hx + ox;
OnPropertyChanged("X1");
OnPropertyChanged("X2");
OnPropertyChanged("X");
}
}
public double OffsetX
{
get { return (x2 - x1) / 2.0; }
set
{
double hx = X / 2.0;
double ox = value;
x1 = hx - ox;
x2 = hx + ox;
OnPropertyChanged("X1");
OnPropertyChanged("X2");
OnPropertyChanged("OffsetX");
}
}
#endregion
}
需要实现,如果OffsetX发生了变化, 那么X1和X2跟着发生变化,然而X1或者X2发生变化,那么X和OffsetX的值也发生变化。
我第一个想到的方法就是使用WPF的数据绑定,使用多重绑定(MultiBinding)和对应的转换器(Converter)来做,因为数据绑定支持双向绑定(Mode.TwoWay),即一个属性变化了,依赖它的其他属性都跟着变化,而依赖属性变化了,原先的属性也会跟着发生变化。
使用数据绑定的方法,其实就是将OffsetX和X属性绑定到X1和X2上面,数据源是对象自己,然后把X和OffsetX里面的Set函数移到对应的值转换器(ValueConverter)里面就好了。
代码:
public partial class Window1 : Window { public Window1() { InitializeComponent(); TestClass tc = new TestClass(); tc.X1 = 1; tc.X = 2.4; } }
public class TestClass : DependencyObject { public double X1 { get { return (double)GetValue(X1Property); } set { SetValue(X1Property, value); } }
public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof(double), typeof(TestClass), new UIPropertyMetadata(0.0));
public double X2 { get { return (double)GetValue(X2Property); } set { SetValue(X2Property, value); } }
public static readonly DependencyProperty X2Property = DependencyProperty.Register("X2", typeof(double), typeof(TestClass), new UIPropertyMetadata(0.0));
public double X { get { return (double)GetValue(XProperty); } set { SetValue(XProperty, value); } }
public static readonly DependencyProperty XProperty = DependencyProperty.Register("X", typeof(double), typeof(TestClass), new UIPropertyMetadata(0.0));
public double OffsetX { get { return (double)GetValue(OffsetXProperty); } set { SetValue(OffsetXProperty, value); } }
public static readonly DependencyProperty OffsetXProperty = DependencyProperty.Register("OffsetX", typeof(double), typeof(TestClass), new UIPropertyMetadata(0.0));
public TestClass() { MultiBinding binding = new MultiBinding(); binding.Bindings.Add(new Binding("X1") { RelativeSource = RelativeSource.Self, // 下面这个模式一定要设置,否则 // X属性更改了,虽然会调用X1X2ToXConverter.ConvertBack // 函数,但是生成的值不会放回到X1和X2里面。 Mode = BindingMode.TwoWay }); binding.Bindings.Add(new Binding("X2") { RelativeSource = RelativeSource.Self, Mode = BindingMode.TwoWay }); binding.Converter = new X1X2ToXConverter(); // 下面这个模式也要设置,否则X属性更改不会 // 让WPF自动调用X1X2ToXConverter.ConvertBack函数 binding.Mode = BindingMode.TwoWay; binding.ConverterParameter = this; BindingOperations.SetBinding(this, XProperty, binding);
binding = new MultiBinding(); binding.Bindings.Add(new Binding("X1") { RelativeSource = RelativeSource.Self, Mode = BindingMode.TwoWay }); binding.Bindings.Add(new Binding("X2") { RelativeSource = RelativeSource.Self, Mode = BindingMode.TwoWay }); binding.Converter = new X1X2ToOffsetXConverter(); binding.Mode = BindingMode.TwoWay; binding.ConverterParameter = this; BindingOperations.SetBinding(this, OffsetXProperty, binding); } }
public class X1X2ToXConverter : IMultiValueConverter { #region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return (double)values[0] + (double)values[1]; }
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { double hx = (double)value / 2.0; var tc = parameter as TestClass; double ox = tc.OffsetX; var result = new object[] { hx - ox, hx + ox};
return result; }
#endregion }
public class X1X2ToOffsetXConverter : IMultiValueConverter { #region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return ((double)values[1] - (double)values[0]) / 2.0; }
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { double hx = ((TestClass)value).X / 2.0; double ox = (double)value; var result = new object[] { hx - ox, hx + ox};
return result; }
#endregion } |