创建相互依赖(影响)的WPF属性

昨天在网上看到一个网友问如何在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发生了变化, 那么X1X2跟着发生变化,然而X1或者X2发生变化,那么XOffsetX的值也发生变化。

我第一个想到的方法就是使用WPF的数据绑定,使用多重绑定(MultiBinding)和对应的转换器(Converter)来做,因为数据绑定支持双向绑定(Mode.TwoWay),即一个属性变化了,依赖它的其他属性都跟着变化,而依赖属性变化了,原先的属性也会跟着发生变化。

使用数据绑定的方法,其实就是将OffsetXX属性绑定到X1X2上面,数据源是对象自己,然后把XOffsetX里面的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

            // 函数,但是生成的值不会放回到X1X2里面。

            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

}

 

posted @ 2009-09-18 17:00  donjuan  阅读(1816)  评论(1编辑  收藏  举报