将单个属性的各个组件绑定到WPF中的两个独立控件

本文关键字:两个 控件 独立 属性 单个 组件 绑定 WPF | 更新日期: 2023-09-27 18:25:45

我的目标是用两个不同的文本框更新单个Size属性。具体来说,每个组件都连接到一个文本框,该文本框在下面的XAML中突出显示。特别是一行:{Binding Path=Dimensions.Width,……}

private Size _dimension;
    public Size Dimensions
    {
        get { return _dimension; }
        set
        {
            _dimension = value;
            OnPropertyChanged("Dimensions");
        }
    }

我的XAML如下:

<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
   <Label Content="Width" />
   <TextBox Width="50" Text="{Binding Path=Dimensions.Width, 
            Converter={StaticResource myStringToDoubleConverter},
            UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
   <Label Content="Height" />
   <TextBox Width="50" Text="{Binding Path=Dimensions.Height,
            Converter={StaticResource myStringToDoubleConverter},
            UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</StackPanel>

我遇到的问题是,当我更改文本框中的文本时,set函数不会被调用。我想原因是我没有提供Size对象,但最多提供一个double,最坏提供一个字符串。我有一个转换器,可以将字符串转换为双精度,但这似乎不是诀窍。我的感觉是多绑定产生了我的解决方案,然而,我看到的所有例子都是将两个字符串组合成第三个控件。我对制作带有附加值的控件不感兴趣,而是更新"复杂"属性的组件。

我知道我可以制作两个"子"属性,一个Height和Width,然后一次更新一个Dimension属性,但似乎应该有一个更优雅的解决方案。

为什么我的文本框不能正确地绑定到Dimension属性的单个组件并相应地更新维度?

构造一个实现INotifyPropertyChanged的包装类似乎太过分了。

public class MySizeClass : Size, INotifyPropertyChanged

将单个属性的各个组件绑定到WPF中的两个独立控件

如果您在这里使用的Size类型是WPF提供的类型,即System.Windows.Size,那么问题是它不是一个类。它是一种值类型(即struct)。尽管它确实为WidthHeight提供了setter,但它可能不应该这样做,因为它们没有达到您的预期。

C#也不允许您修改Dimensions.Width——如果您编写以下内容:

src.Dimensions.Width = 123;

其中src是包含您在"组件绑定解决方案"中显示的属性定义的类型的实例,您将得到以下错误:

Cannot modify the return value of 'WpfApplication1.Source.Dimensions' because it is not a variable

C#在这里拒绝是因为同样的基本问题阻止了它在WPF中的工作:这是因为Size是一个值类型。任何检索Dimensions属性的尝试都将返回该值的全新副本。

当然,C#代码实际上是的缩写

Size dimensions = src.Dimensions;
dimensions.Width = 123;

C#实际上会让你写这个,但如果你尝试一下,你会发现它为什么会阻止你写较短的版本:src.Dimensions.Width仍然会返回旧值。(此代码所做的是获取src.Dimensions的副本,将该副本存储在名为dimensions的局部变量中,然后修改该局部变量,使原始变量保持不变。)

基本上,没有办法获得对Size实例的引用,即Dimensions属性返回的引用——您只能获得该值的副本。更新它的唯一方法是在它的位置写一个全新的Size。这就是为什么不能单独修改WidthHeight的原因。

试图用转换器解决这个问题是行不通的,因为问题不在于你试图写入目标的值。问题是,您想要写入不可访问的目标,即值类型的特定属性。只有当目标属性是对象的属性而不是值时,绑定才能写入目标属性。

(原则上,微软本可以让WPF检测到这一点,让它读取Dimensions属性,修改它获得的值的副本,然后将修改后的值写回Dimensions。尽管在这种情况下这很方便,但与数据绑定通常处理属性更新的方式相比,这也将是一个重大变化。我认为他们最好不要尝试,因为它可以很容易地掩盖其他问题。)

在C#中,当您有一个包含Size值的局部变量或字段时,您可以写入单个属性,因为该变量或字段表示该值的特定实例。字段、局部变量、refout自变量和数组元素是特殊的,因为实际上可以使用它们原位保存的值。但在浏览属性时不能这样做。同样的原因是,如果定义了List<Size>,就不能编写myList[0].Width = 123;,即使这对数组来说很好。

在任何情况下,可变值类型都是有问题的,原因有很多,而不仅仅是这些。他们通常不受欢迎,不幸的是,WPF只运送了一些。

如果WPF能够绑定到字段,那么您就可以随心所欲了。但事实并非如此,所以你不能。

这是组件绑定解决方案,它确实非常好用。

    private Double m_Width;
    public Double Width
    {
        get { return (Dimensions.Width); }
        set
        {
            m_Width = value;
            Dimensions= new Size(m_Width, m_Height);
        }
    }
    private Double m_Height;
    public Double Height
    {
        get { return (Dimensions.Height); }
        set
        {
            m_Height = value;
            Dimensions= new Size(m_Width, m_Height);
        }
    }
    private Size _dimension;
    public Size Dimensions
    {
        get { return _dimension; }
        set
        {
            _dimension = value;
            OnPropertyChanged("Dimensions");
        }
    }

似乎直接绑定到Dimensions.Width是WPF支持的东西。