【问题标题】:Validating bound ObservableCollection in ViewModel using MVVM Pattern使用 MVVM 模式验证 ViewModel 中绑定的 ObservableCollection
【发布时间】:2023-03-17 16:24:01
【问题描述】:

我是 MVVM 新手,最近才开始遵循 MVVM 模式的第一个项目。我在尝试使用 IDataErrorInfo 接口验证 ObservableCollection 时遇到问题。我的 ObservableCollection 看起来像这样:

ObservableCollection<Magazine> magazineRepository;
    public ObservableCollection<Magazine> MagazineRepository
    {
        get { return magazineRepository; }
        set
        {
            if (value != null)
            {
                bladRepository = value;
                OnPropertyChanged("MagazineRepository");
            }
        }
    }

我的 XAML 是这样的:

<ListBox x:Name="listMagazineRepository"
                 Grid.ColumnSpan="2"
                 ItemsSource="{Binding}" 
                 DataContext="{Binding MagazineRepository}"
                 DisplayMemberPath="Navn" 
                 SelectedItem="{Binding Path=SelectedItem}"/>

        <TextBox x:Name="txtName" Grid.Row="1" Grid.Column="0"
                    Text="{Binding ElementName=listMagazineRepository, Path=SelectedItem.Navn, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
        <TextBox x:Name="txtPrice" Grid.Row="2" Grid.Column="0"
                    Text="{Binding ElementName=listMagazineRepository, Path=SelectedItem.Pris, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />

它只是一个简单的包含对象的列表框,当你选择一个项目时,被选择的对象属性显示在文本框中,然后绑定到列表框对象。

我的问题是,当我像这样设置我的代码时,我可以弄清楚如何验证我的数据的唯一方法是在域模型中,这确实不是一个好习惯,我想验证在 ViewModel 到达那里之前。基本上我想在 ViewModel 中验证 MagazineRepository 中的每个属性,你会怎么做呢?

PS:如果我的问题缺少信息,我是在此板(以及一般编程板)上发帖的新手,请告诉我,我会提供所需的详细信息。

非常感谢。

【问题讨论】:

  • 好吧,我在您发布的代码中没有看到 ViewModel,但典型的方法是让您的 ViewModel 实现 IDataErrorInfo
  • 对不起,我以为是暗示的。集合在我的 ViewModel 中,它确实实现了 IDataErrorInfo,但是当文本框更改集合中的属性时,我需要验证该属性,但我不知道该怎么做。所以有 2 个文本框,每个文本框都绑定到集合中的一个属性,当该属性更改时,我需要对其进行验证,按照我在 IDataErrorInfo 中学习的方式进行验证,我只能验证整个集合。

标签: c# wpf mvvm


【解决方案1】:

如果我理解正确,您想验证 Magazine 对象。如果是这种情况,一种方法是将该类包装在一个视图模型中,我们称之为 MagazineVM,它实现了 IDataErrorInfo 并保持杂志对象的更新。然后将 MagazineVM 列表绑定到视图。举个很简单的例子:

public class MagazineVM : IDataErrorInfo, INotifyPropertyChanged
{
   private Magazine _magazine;

   public int FirstMagazineProperty
   {
      get { return _magazine.FirstMagazineProperty; }
      set { _magazine.FirstMagazineProperty = value; RaisePropertyChanged("FirstMagazineProperty"); }
   }

   //INotifyPropertyChanged implementation

   //IDataErrorInfo implementation
}

【讨论】:

    【解决方案2】:

    首先,正如 Dtex 所说,您应该使用 MagazineViewModel 类而不是 Magazine 类。例如

    public class MagazineViewModel : INotifyPropertyChanged, IDataErrorInfo
    {
      private string navn;
      private string pris;
      private string error;
    
      public string Navn
      {
        get { return navn; }
        set
        {
          if (navn != value)
          {
            navn = value;
            RaisePropertyChanged("Navn");
          }
        }
      }
      public string Pris
      {
        get { return pris; }
        set
        {
          if (pris != value)
          {
            pris = value;
            RaisePropertyChanged("Pris");
          }
        }
      }
      public string Error
      {
        get { return error; }
        set
        {
          if (error != value)
          {
            error = value;
            RaisePropertyChanged("Error");
          }
        }
      }
    
      public event PropertyChangedEventHandler PropertyChanged;
    
      public string this[string columnName]
      {
        get
        {
          var result = string.Empty;
    
          switch (columnName)
          {
            case "Pris":
              if (string.IsNullOrWhiteSpace(Pris))
              {
                result =  "Pris is required";
              }
              break;
            case "Navn":
              if (string.IsNullOrWhiteSpace(Navn))
              {
               result =  "Navn is required";
              }
              break;
          }
    
          return result;
    
        }
      }
    
      private void RaisePropertyChanged(string PropertyName)
      {
        var e = PropertyChanged;
        if (e != null)
        {
          e(this, new PropertyChangedEventArgs(PropertyName));
        }
      }
    
    }
    

    需要注意的重要属性是“public string this[string columnName]”。 ColumnName 将是您的绑定属性之一,您可以在此处进行验证。

    接下来要考虑的是您的 MainViewModel(您的 DataContext)。例如

    public class MainViewModel : INotifyPropertyChanged
    {
      //Use a readonly observable collection. If you need to reset it use the .Clear() method
      private readonly ObservableCollection<MagazineViewModel> magazines = new ObservableCollection<MagazineViewModel>();
    
      private MagazineViewModel selectedItem;
    
      //Keep the item being edited separate to the selected item
      private MagazineViewModel itemToEdit;
    
      public ObservableCollection<MagazineViewModel> Magazines { get { return magazines; } }
      public MagazineViewModel SelectedItem
      {
        get { return selectedItem; }
        set
        {
          if (selectedItem != value)
          {
            selectedItem = value;
            RaisePropertyChanged("SelectedItem");
            //When the selected item changes. Copy it to the ItemToEdit
            //This keeps the the copy you are editing separate meaning that invalid data isn't committed back to your original view model
            //You will have to copy the changes back to your original view model at some stage)
            ItemToEdit = Copy(SelectedItem);
          }
        }
      }
      public MagazineViewModel ItemToEdit
      {
        get { return itemToEdit; }
        set
        {
          if (itemToEdit != value)
          {
            itemToEdit = value;
            RaisePropertyChanged("ItemToEdit");
          }
        }
      }
    
      public event PropertyChangedEventHandler PropertyChanged;
    
      public MainViewModel()
      {
        //Ctor...
      }
    
      //Create a copy of a MagazineViewModel
      private MagazineViewModel Copy(MagazineViewModel ToCopy)
      {
        var vm = new MagazineViewModel();
        vm.Navn = ToCopy.Navn;
        vm.Pris = ToCopy.Pris;
        return vm;
      }
    
      private void RaisePropertyChanged(string PropertyName)
      {
        //...
      }
    }
    

    这里唯一缺少的是如何将更改复制回原始视图模型。您可以在所选项目更改之前执行此操作(如果 ItemToEdit 有效),或者有一个仅在 ItemToEdit 有效时启用的提交按钮。如果您可以让原始视图模型进入无效状态,则无需担心复制。

    最后是 XAML

    显示错误提示的隐式样式

    <Style
      TargetType="{x:Type TextBox}">
      <Setter
        Property="ToolTip"
        Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
    </Style>
    

    还有控件和绑定

    <ListBox
      ItemsSource="{Binding Magazines}"
      DisplayMemberPath="Navn"
      SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" />
    <TextBox
      Margin="5"
      x:Name="txtName"
      Grid.Row="1"
      Grid.Column="0"
      Text="{Binding ItemToEdit.Navn, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
    <TextBox
      Margin="5"
      x:Name="txtPrice"
      Grid.Row="2"
      Grid.Column="0"
      Text="{Binding ItemToEdit.Pris, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
    

    文本框绑定到 ItemToEdit。 ItemToEdit 将是 SelectedItem 的同步副本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-14
      • 2017-06-14
      • 1970-01-01
      • 1970-01-01
      • 2013-09-18
      • 1970-01-01
      • 2013-06-14
      • 2016-08-28
      相关资源
      最近更新 更多