【问题标题】:Update Observable Collection When Item Added to Model C# wpf将项目添加到模型 C# wpf 时更新可观察集合
【发布时间】:2016-04-29 22:44:44
【问题描述】:

好的,所以我对此很陌生。我跟着来自 YouTube 的this MVVM tutorial。这是非常好的和简单的。基本上,它设置了一个非常基本的程序,其中包含一个 Model 类、DataAcess 类、3 个视图模型(主窗口、Employee 和 ViewModelBase),最后一个视图具有一个堆栈面板和几个绑定到 FirstName 和 LastName 中的文本框型号。

这一切都按照它的意思工作,我已经经历了很多次,我很确定我理解这一切是如何工作的但我遇到的麻烦是添加新员工 .

在DataAccess类(Employee Repository)中添加Employees如下所示。

    class EmployeeRepository
{
    readonly List<Employee> _employee;

    public EmployeeRepository()
    {
        if (_employee == null)
        {
            _employee = new List<Employee>();
        }

        _employee.Add(Employee.CreateEmployee("Bob", "Jones"));
        _employee.Add(Employee.CreateEmployee("Sarah", "Marshall"));
        _employee.Add(Employee.CreateEmployee("Peter", "Piper"));

    }

    public List<Employee> GetEmployees()
    {
        return new List<Employee>(_employee);
    }


}

在模型中有一个这样的方法调用 CreateEmployee

    public class Employee
{
    public static Employee CreateEmployee(string firstName, string lastName)
    {

        return new Employee { FirstName = firstName, LastName = lastName };

    }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

所以我想我会在 MainWindow 中添加一个按钮,然后在列表中添加另一个名称。跳跃视图会随着项目的更新而更新。只是为了看看它是否会起作用,我只是使用了后面的代码。

我想我可以像在 EmployeeRepository 中一样添加一个新员工,所以我尝试了这个

    readonly List<Employee> _employee = new List<Employee>();

    private void btnAdd_Click(object sender, RoutedEventArgs e)
    {
        _employee.Add(Employee.CreateEmployee("John", "Smith"));
    }

我尝试了很多方法来做到这一点,但无济于事。我已经观看并阅读了许多教程和问题,但我没有尝试过任何工作。

我错过了什么?我最初认为它不起作用,因为我将项目添加到存储库中的列表中,而不是添加到视图模型中的 ObservableCollection。而 AllEmployees ObservableCollection 是 View 的 ItemSource。

    readonly EmployeeRepository _employeeRepository;

    public ObservableCollection<Model.Employee> AllEmployees
    {
        get;
        private set;
    }

    public EmployeeListViewModel(EmployeeRepository currentWindowRepository)
    {
        if (currentWindowRepository == null)
        {
            throw new ArgumentException("currentWindowRepository");
        }
        _employeeRepository = currentWindowRepository;
        this.AllEmployees = new ObservableCollection<Model.Employee>(_employeeRepository.GetEmployees());
    }

但在按钮代码中我尝试实现类似的东西,但没有。

我还可以添加视图 xaml 代码和 MainViewModel 代码,以便您可以根据需要查看它们是如何绑定的。

提前感谢您的帮助!

【问题讨论】:

  • ObservableCollection 开箱即用,而 list 没有。因此,如果您绑定到 observablecollection,然后向其中添加/删除项目,并且如果您的绑定正常,那么它应该可以工作,列表不会。将您的列表更改为 observablecolletion,它将起作用。
  • @adminSoftDK 感谢您的建议,但没有奏效。我了解 ObservableCollection 的工作方式是,当向其中添加项目时,它将更新视图,但存储库类中的列表未绑定到视图。 AllEmployees ObservableCollection 绑定到视图。所以我不明白为什么更改列表会起作用。
  • 附带说明,在任何情况下都不应该在 ViewModel 构造函数中获取数据。获取数据是一个阻塞操作,并且构造函数必须很快。

标签: c# wpf mvvm binding observablecollection


【解决方案1】:

在您的 EmployeelistViewModel 中,您正在创建 ObservableCollection ,并且您认为它会在添加/删除员工时自动重新填充。其次,在您的 GetEmployees 方法中,您正在创建一个新列表。您应该直接使用 obser.coll 代替 List (_employee)。并从您的方法中返回此 ocoll。

【讨论】:

    【解决方案2】:

    你不能在“一次操作”中做到这一点。

    当您在 UI 中添加新的 Employee 时,您首先需要实例化您的 Employee 类并将其添加到可观察集合中。

    如果处于有效状态,则将其持久化到存储库中。

    private ICommand addEmployeeCommand;
    public ICommand AddEmployeeCommand { get { return addEmployeeCommand; } }
    
    public ObservableCollection<Employee> Employees { get; protected set; }
    
    private void AddEmployee() 
    {
        // Get the user input that's bound to the viewmodels properties
        var employee = Employee.Create(FirstName, LastName);
    
        // add it to the observable collection
        // Note: directly using model in your ViewModel for binding is a pretty bad idea, you should use ViewModels for your Employees too, like: 
        // Employee.Add(new EmployeeViewModel(employee));
        Employees.Add(employee);
    
        // add it to the repository
        this.employeeRepository.AddOrUpdate(employee);
    }
    
    // in constructor
    this.addEmployeeCommand = new DelegateCommand(AddEmployee, CanExecuteAddEmployee);
    

    如上所述,避免在 ViewModel 绑定中直接使用您的模型,它有几个缺点,例如您现在的视图取决于您的视图模型。模型中的每一个变化都需要反映在视图中,这超出了视图模型的目的,即解耦视图、视图模型和模型。

    另一个缺点是,通常您的模型没有实现INotifyPropertyChanged,这会导致视图中的内存泄漏。

    【讨论】:

    • 谢谢!我错过了将员工添加到 ObservableCollection。我试图添加一个员工,然后调用 ObservableCollection 的一个新实例,认为 ObservableCollection 将从 3 个名称变为 4 个,因此应该更新我的视图。在看到您的答案并考虑了一段时间后,我意识到创建更大列表的新实例与添加项目不同。
    【解决方案3】:

    对此的一种解决方案是将 INPC 添加到您的模型中,然后让您的视图模型观察他们的模型并相应地更新自己,例如:

    public class MyListType
    {
        // some data
    }
    
    public class MyModel
    {
        public IList<MyListType> MyListItems { get; set; }
    
        public MyModel()
        {
            this.MyListItems = new ObservableCollection<MyListType>();
        }
    }
    
    public class MyListTypeViewModel : ViewModelBase
    {
        public MyListType Model {get; set;}
    
        // INPC properties go here
    }
    
    public class MyViewModel
    {
        public IList<MyListTypeViewModel> MyListItemViewModels { get; set; }
    
        public MyViewModel(MyModel model)
        {
            (model.MyListItems as INotifyCollectionChanged).CollectionChanged += OnListChanged;
            // todo: create initial view models for any items already in MyListItems
        }
    
        private void OnListChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // create any new elements
            if (e.NewItems != null)
                foreach (MyListType item in e.NewItems)
                    this.MyListItemViewModels.Add(new MyListTypeViewModel{Model = item});
    
            // remove any new elements
            if (e.OldItems != null)
                foreach (MyListType item in e.OldItems)
                    this.MyListItemViewModels.Remove(
                        this.MyListItemViewModels.First(x => x.Model == item)
                    );
        }
    

    现在您的视图模型列表将自动与您的模型列表保持同步。这种方法的主要问题是您的模型通常源自您的 ORM(数据库)代码,因此您需要使用在创建时用于注入 INPC 的任何框架,例如如果您使用 NHibernate,那么您需要为 INPC 使用 binding interceptor,并为创建 ObservableCollections 列表使用 collection convention

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-20
      • 2011-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多