【问题标题】:Binding to individual elements in a collection绑定到集合中的单个元素
【发布时间】:2011-12-08 22:06:44
【问题描述】:

我对 MVVM 还很陌生,所以请耐心等待。我有一个视图模型类,它的公共属性是这样实现的:

public List<float> Length
{
  get;
  set;
}

在我的视图 XAML 中,我有几个文本框,每个文本框都绑定到此长度列表中的特定元素:

  <TextBox Text="{Binding Length[0], Converter=DimensionConverter}" />
  <TextBox Text="{Binding Length[2], Converter=DimensionConverter}" />
  <TextBox Text="{Binding Length[4], Converter=DimensionConverter}" />

DimensionConverter 是一个 IValueConverter 派生类,它像尺寸一样格式化值(即 480.0 英寸在屏幕上的文本框中变为 40'0"),然后再返回(即,对于一个字符串,采用 35'0" 并产生 420.0英寸为来源)

我的问题:我需要能够验证列表中的每个值,因为它在关联的文本框中发生了更改。对于某些人,我可能需要根据输入的值修改列表中的其他值(即更改 Length[0] 处的浮点数将更改 Length[4] 处的值并更新屏幕)。

有没有办法重新处理属性以允许索引器?或者,我是否需要为列表中的每个项目创建单独的属性(这确实使列表变得不必要)?本质上,由于我已经有了 float 的集合,我希望能够编写 MVVM 代码来验证每个项目的修改。

想法? (并且,提前感谢)

【问题讨论】:

    标签: wpf xaml binding mvvm collections


    【解决方案1】:

    您可以使用ObservableCollection&lt;float&gt; 代替List&lt;float&gt;,并处理CollectionChanged 事件以检测用户何时更改值。

    【讨论】:

    • 据我了解,他无法通过项目索引绑定到特定的列表项,我错过了什么吗?
    • 这不是我理解的。显然 OP 想要验证用户输入,所以他需要检测用户何时更改值
    • 是的,看来我完全误解了这个问题
    • 我已经测试过了,我可以绑定到集合属性中的单个项目。值显示在视图中,当我更改值时,这会反映在集合中。但是,我遇到的问题是,在输入每个项目时需要对每个项目应用验证规则,而不是在它们已经被修改的最后。
    • @user1088638,在这种情况下,您需要使用 ValidationRule(参见此处:msdn.microsoft.com/en-us/library/ms753962.aspx
    【解决方案2】:

    不会是这样的:

        <ItemsControl ItemsSource="{Binding Length}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Mode=TwoWay, Converter=DimensionConverter}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    

    接近你想要的?
    它将显示整个列表,并允许用户修改值,这些值将直接返回到列表中,只要您的 IValueConverter 实现 ConvertBack。

    然后按照 Thomas 所说的进行验证,或者实现 ObservableLinkedList

    你现在做的事情看起来已经很脏了,而且只是几行代码..

    【讨论】:

    • 我遇到的问题是我试图创建的视图的性质。本质上,视图是针对建筑布局的,因此 Length 项是墙壁的长度、各种配置(矩形、L 形、T 形等)。布局显示为图表,放置了 TextBox 控件以输入各个测量值。我已经有一个墙列表,所以我希望在绑定中简单地访问它,而不是为集合中的每个项目创建一个样板方法。
    【解决方案3】:

    如果您可以拥有一个实现 INotifyPropertyChanged 的​​类以在列表长度不变的情况下具有属性,那就太好了。

    【讨论】:

    • 仅供参考:提供 Length 属性的 ViewModel 类确实实现了 INotifyPropertyChanged
    【解决方案4】:

    如果你想用 mvvm 验证你的文本输入,那么创建一个你可以在你的视图模型中使用的模型

    public class FloatClass : INotifyPropertyChanged
    {
      private ICollection parentList;
    
      public FloatClass(float initValue, ICollection pList) {
        parentList = pList;
        this.Value = initValue;
      }
    
      private float value;
    
      public float Value {
        get { return this.value; }
        set {
          if (!Equals(value, this.Value)) {
            this.value = value;
            this.RaiseOnPropertyChanged("Value");
          }
        }
      }
    
      private void RaiseOnPropertyChanged(string propName) {
        var eh = this.PropertyChanged;
        if (eh != null) {
          eh(this, new PropertyChangedEventArgs(propName));
        }
      }
    
      public event PropertyChangedEventHandler PropertyChanged;
    }
    

    在您的视图模型中,您可以像这样使用模型

    public class FloatClassViewmModel : INotifyPropertyChanged
    {
      public FloatClassViewmModel() {
        this.FloatClassCollection = new ObservableCollection<FloatClass>();
        foreach (var floatValue in new[]{0f,1f,2f,3f}) {
          this.FloatClassCollection.Add(new FloatClass(floatValue, this.FloatClassCollection));
        }
      }
    
      private ObservableCollection<FloatClass> floatClassCollection;
    
      public ObservableCollection<FloatClass> FloatClassCollection {
        get { return this.floatClassCollection; }
        set {
          if (!Equals(value, this.FloatClassCollection)) {
            this.floatClassCollection = value;
            this.RaiseOnPropertyChanged("FloatClassCollection");
          }
        }
      }
    
      private void RaiseOnPropertyChanged(string propName) {
        var eh = this.PropertyChanged;
        if (eh != null) {
          eh(this, new PropertyChangedEventArgs(propName));
        }
      }
    
      public event PropertyChangedEventHandler PropertyChanged;
    }
    

    这里是 xaml 示例

    <ItemsControl ItemsSource="{Binding Path=FloatClassViewmModel.FloatClassCollection}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <TextBox Text="{Binding Value, Mode=TwoWay, Converter=DimensionConverter}" />
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    希望对你有帮助

    【讨论】:

    • 我考虑过这种方法。我犹豫了,因为它对我来说似乎“管道很重”,但我对 MVVM 还很陌生,所以这可能是我 C++ 时代的遗留物。
    猜你喜欢
    • 2014-05-03
    • 1970-01-01
    • 2013-08-27
    • 2016-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-29
    相关资源
    最近更新 更多