簡體   English   中英

IEnumerable 更改事件

[英]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 時,控件會相應更改。

不,除非IEnumerableINotifyCollectionChanged ,否則不會。

如果您不相信我的話,請在最初呈現控件嘗試在事件處理程序中調用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 的變化做出反應。您的示例之所以有效,是因為尚未構建項目,並且在加載控件之前不會構建(您的代碼全部在構造函數中)。

此示例將向您展示普通ListObservableCollection之間的比較。

假設我們有一個像這樣的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都綁定到一個名為ObservableCollectionObservableCollection

當您將項目添加到 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM