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

问题描述:

我的目标是通过两个不同的文本框更新单个Size属性。特别是每个组件都连接到单个文本框(在下面的XAML中突出显示)。具体这行:{绑定路径= Dimensions.Width,...}将单个属性的单个组件绑定到WPF中的两个单独控件

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> 

我的问题是所设定的功能不叫当我改变文本框中的文本时。我想是因为我没有提供一个Size对象,但最好是一个double对象,最坏的情况是一个字符串。我在那里有一个转换器来将字符串转换为double,但它似乎并不是个窍门。我的感觉是Multibindin*生我的解决方案,但是我看到的所有例子都是将两个字符串合并到第三个控件中。我对使用附加值进行控制并不感兴趣,而是更新我的“复杂”属性的组件。

我明白我可以制作两个“sub”属性,Height和Width,然后一次更新一个Dimension的属性,但似乎应该存在一个更优雅的解决方案。

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

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

public class MySizeClass : Size, INotifyPropertyChanged 
+0

我认为你的答案是一个合理的解决方案。你原来的帖子中的代码肯定有一些问题:1.你只能设置宽度,而不是高度。 2.您的转换器如何将宽度转换为大小而不知道其高度?你如何坚持以前尺寸对象的高度? –

+0

@BillZhang转换器实际上只是将字符串更改为Double,而不是大小。要转换为尺寸,我需要以某种方式获取高度参数。我不清楚,那实际上是可能的。除非我的转换器做了一些特殊的地方,它只更新两个组件之一。例如:return(new Size(input,this.Height)),但这并不是特别优雅,可能更糟糕...... 编辑:将OP中的代码调整为更统一。 – mwjohnson

+0

这将是显而易见的。由于没有隐式转换器将Size转换为double,因此您的Size属性绝对不会被调用。由于View Model是View的一个特殊模型,因此您应该考虑View如何方便地使用它的Model。 –

如果您在此处使用的Size类型是由WPF提供的类型,即System.Windows.Size,那么问题在于它不是类。这是一种价值类型(即struct)。尽管它确实为WidthHeight提供了引导程序,但它可能不应该是因为它们没有达到你所期望的。

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能够绑定到字段,你可以做你想做的。但事实并非如此,所以你不能。

+0

非常感谢,非常感谢。我常常被*社区的专业知识和知识所吸引。你提到,“... System.Windows.Size,...不是一个类,它是一个值类型(即一个结构体)。”如果我要用两个类型为double的变量(Height和Width)创建一个新的Class MySize,然后使用该类型定义我的Dimensions属性,此方法是否可行?即'公共MySize维度'因此使用类定义我的属性,而不是内置的结构。 – mwjohnson

+1

是的,这应该工作。 –

这里是组件绑定解决方案,它令人愉快地工作得很好。

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所支持的东西。