我正在尝试使用WPF和MVVM。 我的ViewModel包含一个自定义集合(IEnumerable),它必须在UI中呈现。我添加了一些代码来向 Model 添加一个新实体,我想这将在UI中呈现。但这不会发生。
[1] 我的模式错了吗? [2] 为什么新的实体没有反映在View中,虽然我提出了INPC?
请在下面找到我的代码
Classes.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _06_MVVMTest___Add_to_Model_Reflects_in_View
{
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Salary { get; set; }
public Employee(int id, string name,int age, int salary)
{
this.Id = id;
this.Name = name;
this.Age = age;
this.Salary = salary;
}
}
public class EmployeeCollection : IEnumerable
{
List<Employee> employees = new List<Employee>();
public EmployeeCollection()
{
employees.Add(new Employee(100, "Alice", 23, 300));
employees.Add(new Employee(100, "Bob", 22, 400));
employees.Add(new Employee(100, "Trude", 21, 200));
}
public IEnumerator GetEnumerator()
{
foreach (Employee emp in employees)
{
if (emp==null)
{
break;
}
yield return emp;
}
}
public void AddNewEmployee()//For Test
{
employees.Add(new Employee(200, "Dave", 21, 2000));
}
}
public class EmployeeViewModel:INotifyPropertyChanged
{
EmployeeCollection employees=new EmployeeCollection();
public EmployeeCollection Employees
{
get { return employees; }
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyOnEmployeeCollectionChanged()
{
if (PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs("Employees"));
}
}
}
MainWindow.xaml.cs
sing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace _06_MVVMTest___Add_to_Model_Reflects_in_View
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
EmployeeViewModel vm =(EmployeeViewModel) this.DataContext;
vm.Employees.AddNewEmployee();
vm.NotifyOnEmployeeCollectionChanged();
}
}
}
MainWindow.xaml
<Window x:Class="_06_MVVMTest___Add_to_Model_Reflects_in_View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:_06_MVVMTest___Add_to_Model_Reflects_in_View"
Title="Employee Window" Height="350" Width="525">
<Window.DataContext>
<local:EmployeeViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListView Height="300" Grid.Row="0" Grid.Column="0" BorderBrush="Blue" ItemsSource="{Binding Employees}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="LightCoral" Margin="10,10,10,10">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Height="300" Grid.Row="0" Grid.Column="1" BorderBrush="Blue" ItemsSource="{Binding Employees}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="LightCoral" Margin="10,10,10,10">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Grid.Row="1" Height="40" Content="add new employee" Click="Button_Click" />
</Grid>
</Window>
答案 0 :(得分:1)
WPF处理属性更改的方式以及通知更改的方式存在问题。 WPF会检查您的值是否仍然相同; EmployeeCollection实际上与旧的相同(它仍然是同一个实例,它不会在集合中查看)。
一种解决方案是将EmployeeCollection
的实现更改为:
public class EmployeeCollection : IEnumerable, INotifyCollectionChanged
{
private ObservableCollection<Employee> employees = new ObservableCollection<Employee>();
public EmployeeCollection()
{
employees.Add(new Employee(100, "Alice", 23, 300));
employees.Add(new Employee(100, "Bob", 22, 400));
employees.Add(new Employee(100, "Trude", 21, 200));
}
public IEnumerator GetEnumerator()
{
return employees.GetEnumerator();
}
public void AddNewEmployee() //For Test
{
employees.Add(new Employee(200, "Dave", 21, 2000));
}
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add { employees.CollectionChanged += value; }
remove { employees.CollectionChanged -= value; }
}
}
这引入了INotifyCollectionChanged
接口,并且无需在主窗口中调用vm.NotifyOnEmployeeCollectionChanged()
,因为ObservableCollection<T>
底层的EmployeeCollection
将告诉WPF它已更改。
如果您现在不想了解这些概念,请快速解决:
public void NotifyOnEmployeeCollectionChanged()
{
var current = this.employees;
this.employees = null;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Employees"));
this.employees = current;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Employees"));
}
答案 1 :(得分:1)
您的自定义收藏集应该实现INotifyCollectionChanged
。每次添加项目时,都会触发CollectionChanged
事件。
public class EmployeeCollection : IEnumerable, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void AddNewEmployee() //For Test
{
employees.Add(new Employee(200, "Dave", 21, 2000));
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
}