当项目添加到模型 C# wpf 时更新 Observable 集合
Update Observable Collection When Item Added to Model C# wpf
好的,我对此还很陌生。我跟随了 YouTube 上的 this MVVM tutorial。这非常好而且简单明了。基本上,它设置了一个非常基本的程序,其中包含一个模型 class、DataAcess class、3 个视图模型(Main window、Employee 和 ViewModelBase),最后是一个具有堆栈面板和几个视图的视图绑定到模型中的 FirstName 和 LastName 的文本框。
一切都按预期运作,我已经经历了很多次,我很确定我理解这一切是如何运作的但我遇到的麻烦是增加新员工.
在 DataAccess class(员工信息库)中添加员工,如下所示。
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 是视图的 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 代码,以便您可以根据需要查看它是如何绑定的。
在此先感谢您的帮助!
在您的 EmployeelistViewModel 中,您正在创建 ObservableCollection ,并且您认为它会在 addition/deletion 名员工上自动重新填充。其次,在您的 GetEmployees 方法中,您正在创建一个新列表。您应该直接使用 obser.coll 代替 List (_employee)。 return 这个 ocoll 来自你的方法。
您不能在 "one operation" 中执行此操作。
当您在 UI 中添加一个新的 Employee
时,您首先需要实例化您的 Employee
class 并将其添加到可观察集合中。
如果处于有效状态,则将其保存到存储库中。
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
,这会导致视图中出现内存泄漏。
一个解决方案是将 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,那么您需要使用 binding interceptor for INPC and a collection convention 来制作列表 ObservableCollections。
好的,我对此还很陌生。我跟随了 YouTube 上的 this MVVM tutorial。这非常好而且简单明了。基本上,它设置了一个非常基本的程序,其中包含一个模型 class、DataAcess class、3 个视图模型(Main window、Employee 和 ViewModelBase),最后是一个具有堆栈面板和几个视图的视图绑定到模型中的 FirstName 和 LastName 的文本框。
一切都按预期运作,我已经经历了很多次,我很确定我理解这一切是如何运作的但我遇到的麻烦是增加新员工.
在 DataAccess class(员工信息库)中添加员工,如下所示。
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 是视图的 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 代码,以便您可以根据需要查看它是如何绑定的。
在此先感谢您的帮助!
在您的 EmployeelistViewModel 中,您正在创建 ObservableCollection ,并且您认为它会在 addition/deletion 名员工上自动重新填充。其次,在您的 GetEmployees 方法中,您正在创建一个新列表。您应该直接使用 obser.coll 代替 List (_employee)。 return 这个 ocoll 来自你的方法。
您不能在 "one operation" 中执行此操作。
当您在 UI 中添加一个新的 Employee
时,您首先需要实例化您的 Employee
class 并将其添加到可观察集合中。
如果处于有效状态,则将其保存到存储库中。
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
,这会导致视图中出现内存泄漏。
一个解决方案是将 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,那么您需要使用 binding interceptor for INPC and a collection convention 来制作列表 ObservableCollections。