WPF双向绑定到属性的属性以替换父属性

时间:2017-01-03 08:22:57

标签: c# wpf data-binding dependency-properties inotifypropertychanged

我有一个使用DependencyProperties(或INotifyPropertyChanged)的ViewModel,它具有非常简单的复合类型的属性,如System.Windows.Point。 简单的复合类型不使用DependencyProperties或INotifyPropertyChanged,并且它打算保持这种状态(它不受我的控制)。

我现在要做的是创建与Point的X和Y属性绑定的双向数据,但是当其中一个被更改时,我希望替换整个Point类,而不是只更新构件。

代码示例仅用于说明:

<Window ...>
    <StackPanel>
        <TextBox Text="{Binding TestPoint.X, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, StringFormat=\{0:F\}}"/>
        <TextBox Text="{Binding TestPoint.Y, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, StringFormat=\{0:F\}}"/>
        <!-- make following label update based on textbox changes above -->
        <Label Content="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}"/>
    </StackPanel>
</Window>

代码隐藏:

public partial class MainWindow : Window
{
    public Point TestPoint
    {
        get { return (Point)GetValue(TestPointProperty); }
        set { SetValue(TestPointProperty, value); }
    }
    public static readonly DependencyProperty TestPointProperty = DependencyProperty.Register("TestPoint", typeof(Point), typeof(MainWindow), new PropertyMetadata(new Point(1.0, 1.0)));
}

我想的是将TextBoxes直接绑定到TestPoint属性并使用IValueConverter仅过滤掉特定成员,但是转换方法存在问题,因为修改X时Y值不再存在值。

我觉得必须有一个非常简单的解决方案,我没有得到。

修改

上面的代码只是一个简化的例子,实际应用程序比这更复杂。复合类型有大约7个成员,并且通常在应用程序中使用,因此将其拆分为单个成员并不感觉正确。另外,我想依赖依赖属性的OnChanged事件来调用其他更新,所以我真的需要替换整个类。

3 个答案:

答案 0 :(得分:7)

为什么不使用访问者?

public partial class MainWindow : Window
{
    public Point TestPoint
    {
        get { return (Point)GetValue(TestPointProperty); }
        set { SetValue(TestPointProperty, value); }
    }

    public double TestPointX
    {
        get { return this.TestPoint.X; }
        set
        { 
            SetValue(TestPointProperty, new Point(value, this.TestPointY);
        }
    }

    public double TestPointY
    {
        get { return this.TestPoint.Y; }
        set
        { 
            SetValue(TestPointProperty, new Point(this.TestPointX, value);
        }
    }

    public static readonly DependencyProperty TestPointProperty = DependencyProperty.Register("TestPoint", typeof(Point), typeof(MainWindow), new PropertyMetadata(new Point(1.0, 1.0)));
}

在你的XAML中:

<Window ...>
<StackPanel>
        <TextBox Text="{Binding TestPointX, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, StringFormat=\{0:F\}}"/>
        <TextBox Text="{Binding TestPointY, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, StringFormat=\{0:F\}}"/>
        <Label Content="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}"/>
    </StackPanel>
</Window>

答案 1 :(得分:0)

我认为在这种情况下,引入两个DependencyProperties会更容易,一个用于X值,一个用于Y值并简单地绑定到它们。 在每个DependencyProperty的PropertyMetadata中注册一个方法,s。如果仍然需要,可以在每个值更改上替换Point对象。

您还可以使用适当的单向IMultiValueConverter将标签绑定到两个属性的MultiBinding。

答案 2 :(得分:0)

正如我在评论中所说,你可以这样试试。当我们将转换器添加到特定控件的资源时,相同的实例将用于子控件。

  <Grid>
    <Grid.Resources />
    <StackPanel>
        <StackPanel>
            <StackPanel.Resources>
                <local:PointConverter x:Key="PointConverter" />
            </StackPanel.Resources>
            <TextBox Text="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={StaticResource PointConverter}, ConverterParameter=x}" />
            <TextBox Text="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={StaticResource PointConverter}, ConverterParameter=y}" />
            <!--  make following label update based on textbox changes above  -->
            <Label Content="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" />
        </StackPanel>
        <StackPanel>
            <StackPanel.Resources>
                <local:PointConverter x:Key="PointConverter" />
            </StackPanel.Resources>
            <TextBox Text="{Binding TestPoint2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={StaticResource PointConverter}, ConverterParameter=x}" />
            <TextBox Text="{Binding TestPoint2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={StaticResource PointConverter}, ConverterParameter=y}" />
            <!--  make following label update based on textbox changes above  -->
            <Label Content="{Binding TestPoint2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" />
        </StackPanel>
    </StackPanel>
</Grid>

和代码背后,

    public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }


    public Point TestPoint
    {
        get
        {
            return (Point)GetValue(TestPointProperty);
        }
        set
        {
            SetValue(TestPointProperty, value);
        }
    }

    // Using a DependencyProperty as the backing store for TestPoint.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TestPointProperty =
        DependencyProperty.Register("TestPoint", typeof(Point), typeof(MainWindow), new PropertyMetadata(new Point(1.0, 1.0)));
     public Point TestPoint2
    {
        get
        {
            return (Point)GetValue(TestPoint2Property);
        }
        set
        {
            SetValue(TestPoint2Property, value);
        }
    }

    // Using a DependencyProperty as the backing store for TestPoint.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TestPoint2Property =
        DependencyProperty.Register("TestPoint2", typeof(Point), typeof(MainWindow), new PropertyMetadata(new Point(1.0, 1.0)));


}

public class PointConverter : IValueConverter
{
    double knownX = 0.0;
    double knownY = 0.0;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
         if (parameter.ToString() == "x")
        {
            knownX = ((Point)value).X;
            return ((Point)value).X;
        }
        else
        {
            knownY = ((Point)value).Y;
            return ((Point)value).Y;
        }

    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {

         Point p = new Point();
        if (parameter.ToString() == "x")
        {
            p.Y = knownY;
            p.X = double.Parse(value.ToString());
        }
        else
        {
            p.X = knownX;
            p.Y = double.Parse(value.ToString());
        }
        return p;
    }
}

注意:我没有添加任何空检查。

相关问题