![](/img/trans.png)
[英]Subscribing to INotifyCollection Changed event through IEnumerable in Custom Control
[英]IEnumerable changed event
我正在使用 C# 在 WinUI 3 中編寫應用程序。
有些控件具有 ItemsSource 屬性。
當我將 IEnumerable 傳遞到 ItemsSource 然后更改 IEnumerable 時,控件會相應更改。
我現在想在我的自定義 UserControl 中實現相同的行為。
我有以下代碼,但它僅適用於實現 INotifyCollectionChanged(例如 ObservableCollection)的 IEnumerables。
WinUI 支持未實現 INotifyCollectionChanged 的 IEnumerable。
我怎樣才能做同樣的事情?
這是我的代碼:
public sealed partial class MyUserControl : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MyUserControl), new PropertyMetadata(null));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
private IEnumerable _oldItemsSource;
public MyUserControl()
{
this.InitializeComponent();
RegisterPropertyChangedCallback(ItemsSourceProperty, OnItemsSourceChanged);
}
private void OnItemsSourceChanged(DependencyObject sender, DependencyProperty prop)
{
if (prop == ItemsSourceProperty)
{
var newValue = (IEnumerable)sender.GetValue(ItemsSourceProperty);
if (_oldItemsSource is INotifyCollectionChanged oldCollection)
{
oldCollection.CollectionChanged -= OnCollectionChanged;
}
if (newValue is INotifyCollectionChanged collection)
{
collection.CollectionChanged += OnCollectionChanged;
}
_oldItemsSource = ItemsSource;
}
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Update the control here
}
}
WinUI 3 允許我使用列表(不實現 INotifyCollectionChanged)作為 ItemsSource。
對列表所做的更改會影響控件。
這是測試頁中的代碼:
public TestPage()
{
this.InitializeComponent();
var list = new List<string> { "Item1", "Item2", "Item3" };
var bar = new BreadcrumbBar(); ;
bar.ItemsSource = list;
this.Content = bar;
list.Add("Item4");
// The BreadcrumbBar now has 4 elements.
}
當我將 IEnumerable 傳遞到 ItemsSource 然后更改 IEnumerable 時,控件會相應更改。
不,除非IEnumerable
是INotifyCollectionChanged
,否則不會。
如果您不相信我的話,請在最初呈現控件后嘗試在事件處理程序中調用list.Add("Item4")
。
例如,此代碼不會將“Item4”添加到BreadcrumbBar
控件:
public sealed partial class TestPage : Page
{
private readonly List<string> list = new List<string> { "Item1", "Item2", "Item3" };
public TestPage()
{
this.InitializeComponent();
breadcrumbBar.ItemsSource = list;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
list.Add("Item4");
}
}
將list
類型更改為ObservableCollection<string>
將使其按預期工作。
因此,從這個意義上說,您的自定義控件並不比任何內置控件差。 源集合必須以某種方式通知視圖。
WinUI 3 允許我使用列表(不實現 INotifyCollectionChanged)作為 ItemsSource。 對列表所做的更改會影響控件。
它肯定不會對“啞”collections 的變化做出反應。您的示例之所以有效,是因為尚未構建項目,並且在加載控件之前不會構建(您的代碼全部在構造函數中)。
此示例將向您展示普通List
和ObservableCollection
之間的比較。
假設我們有一個像這樣的UserControl
。
TestUserControl.xaml
<UserControl
x:Class="ItemsSourceTest.TestUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:ItemsSourceTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<ListView ItemsSource="{x:Bind ItemsSource, Mode=OneWay}" />
</Grid>
</UserControl>
TestUserControl.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
using System.Collections;
using Windows.Foundation.Collections;
namespace ItemsSourceTest;
public sealed partial class TestUserControl : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
nameof(ItemsSource),
typeof(IEnumerable),
typeof(TestUserControl),
new PropertyMetadata(default, (d, e) => (d as TestUserControl)?.UpdateItemsSource()));
public TestUserControl()
{
this.InitializeComponent();
}
// You don't need CollectionViewSource or CollectionView
// in order to populate the ListView. This is just to show
// you how you can get events when the collection changes.
public CollectionViewSource? CollectionViewSource { get; set; }
public ICollectionView? CollectionView { get; set; }
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
private void UpdateItemsSource()
{
CollectionViewSource = new CollectionViewSource()
{
Source = ItemsSource,
};
if (CollectionView is not null)
{
CollectionView.VectorChanged -= CollectionView_VectorChanged;
}
CollectionView = CollectionViewSource.View;
CollectionView.VectorChanged += CollectionView_VectorChanged;
}
private void CollectionView_VectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs @event)
{
// Your can do your work for collection changes here...
}
}
一個MainPage
一個ListView
和一個TestUserControl
都綁定到一個名為NonObservableItems的普通List
,另一組ListView
和一個TestUserControl
都綁定到一個名為ObservableCollection
的ObservableCollection 。
當您將項目添加到 collections 時,您會看到只有綁定到ObservableItems的控件才會被填充。
主頁.xaml
<Page
x:Class="ItemsSourceTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:ItemsSourceTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid RowDefinitions="Auto,*">
<Button
Grid.Row="0"
Click="Button_Click"
Content="Add item" />
<Grid
Grid.Row="1"
ColumnDefinitions="*,*"
RowDefinitions="Auto,*,*">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Text="Non-Observable Items" />
<ListView
Grid.Row="1"
Grid.Column="0"
ItemsSource="{x:Bind NonObservableItems, Mode=OneWay}" />
<local:TestUserControl
Grid.Row="2"
Grid.Column="0"
ItemsSource="{x:Bind NonObservableItems, Mode=OneWay}" />
<TextBlock
Grid.Row="0"
Grid.Column="1"
Text="Observable Items" />
<ListView
Grid.Row="1"
Grid.Column="1"
ItemsSource="{x:Bind ObservableItems, Mode=OneWay}" />
<local:TestUserControl
Grid.Row="2"
Grid.Column="1"
ItemsSource="{x:Bind ObservableItems, Mode=OneWay}" />
</Grid>
</Grid>
</Page>
MainPage.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace ItemsSourceTest;
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
AddItem();
}
public List<string> NonObservableItems { get; set; } = new();
public ObservableCollection<string> ObservableItems { get; set; } = new();
private int Counter { get; set; } = 0;
private void AddItem()
{
NonObservableItems.Add(Counter.ToString());
ObservableItems.Add(Counter.ToString());
Counter++;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
AddItem();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.