通过 INotifyPropertyChanged 更新 ListView 的 ItemsSource
Updating ListView's ItemsSource via INotifyPropertyChanged
在回答 时,我尝试了解一件事。我有一个 ListView,它 ItemsSource 绑定到我页面的 属性。该页面实现了 INotifyPropertyChanged,设置了 DataContext,一切正常,我可以在屏幕上看到我的列表。但是,如果我更改其中一个元素内的某些内容并在整个集合上引发 属性 changed,则更改在屏幕上不可见,调用 getter,因此绑定有效,但没有任何变化。
为什么会这样?是否有任何内部 ListView 的 优化正在进行?
XAML代码:
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{Binding Elements}" Height="400">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontSize="16"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Modify item" Click="Button_Click"/>
</StackPanel>
后面的代码:
public class MyItem
{
public string Name { get; set; }
public MyItem(string name) { this.Name = name; }
}
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private List<MyItem> elements = new List<MyItem> { new MyItem("Standard"), new MyItem("Standard"), new MyItem("Standard") };
public List<MyItem> Elements { get { Debug.WriteLine("Collection getter"); return elements; } }
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
elements[1].Name = "Modified one";
RaiseProperty(nameof(Elements));
}
}
注意: 我知道如何通过在 MyItem 中实现 InotifyPropertyChanged class。我也知道,当某个项目内部发生变化时,重新加载整个集合是一种不好的模式。我只是想确定一些事情。
其他情况 - 似乎绑定创建了源对象的引用副本,这很清楚。但有趣的是,当我绑定到字符串数组时:
public string[] Elements { get; } = new string[] { "Standard", "Standard", "Standard" };
private void Button_Click(object sender, RoutedEventArgs e)
{
Elements[1] = "Modified one";
RaiseProperty(nameof(Elements));
}
XAML 中的修改:<TextBlock Text="{Binding}" FontSize="16"/>
。
然后调用 RaiseProperty(nameof(Elements));
更新视图,即使不修改集合。那么为什么绑定在这种情况下以不同的方式工作呢?它似乎只是 returns 数组而不检查更改。
是的,getter 将被调用,因为您正在调用 RaiseProperty(nameof(Elements))
。但不会更新任何内容,因为 属性 Elements
未更改。它仍然是完全相同的对象。改变的是它的底层对象(elements[1]
)的属性 Name
.
这就是为什么在这种情况下 InotifyPropertyChanged
需要在 MyItem
级别实施。
如果您将 Elements
的类型更改为 ObservableCollection<MyItem>
。然后,当您 add/remove 项目 to/from 它时, UI 将得到更新。但是,如果基础 MyItem
的 属性 像您上面所做的那样更改,UI 将保持不变。
在回答
为什么会这样?是否有任何内部 ListView 的 优化正在进行?
XAML代码:
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{Binding Elements}" Height="400">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontSize="16"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Modify item" Click="Button_Click"/>
</StackPanel>
后面的代码:
public class MyItem
{
public string Name { get; set; }
public MyItem(string name) { this.Name = name; }
}
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private List<MyItem> elements = new List<MyItem> { new MyItem("Standard"), new MyItem("Standard"), new MyItem("Standard") };
public List<MyItem> Elements { get { Debug.WriteLine("Collection getter"); return elements; } }
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
elements[1].Name = "Modified one";
RaiseProperty(nameof(Elements));
}
}
注意: 我知道如何通过在 MyItem 中实现 InotifyPropertyChanged class。我也知道,当某个项目内部发生变化时,重新加载整个集合是一种不好的模式。我只是想确定一些事情。
其他情况 - 似乎绑定创建了源对象的引用副本,这很清楚。但有趣的是,当我绑定到字符串数组时:
public string[] Elements { get; } = new string[] { "Standard", "Standard", "Standard" };
private void Button_Click(object sender, RoutedEventArgs e)
{
Elements[1] = "Modified one";
RaiseProperty(nameof(Elements));
}
XAML 中的修改:<TextBlock Text="{Binding}" FontSize="16"/>
。
然后调用 RaiseProperty(nameof(Elements));
更新视图,即使不修改集合。那么为什么绑定在这种情况下以不同的方式工作呢?它似乎只是 returns 数组而不检查更改。
是的,getter 将被调用,因为您正在调用 RaiseProperty(nameof(Elements))
。但不会更新任何内容,因为 属性 Elements
未更改。它仍然是完全相同的对象。改变的是它的底层对象(elements[1]
)的属性 Name
.
这就是为什么在这种情况下 InotifyPropertyChanged
需要在 MyItem
级别实施。
如果您将 Elements
的类型更改为 ObservableCollection<MyItem>
。然后,当您 add/remove 项目 to/from 它时, UI 将得到更新。但是,如果基础 MyItem
的 属性 像您上面所做的那样更改,UI 将保持不变。