我正遇到ObservableCollection
的一些奇怪行为,该行为与DependencyProperty
一起使用。
我在此处创建了可重现的最小方案:https://github.com/aosyatnik/UWP_ObservableCollection_Issue。
我看到了两个无法解释的问题。
这是我的MainViewModel
:
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace UWP_ObservableCollection
{
public class MainViewModel : BaseViewModel
{
public IList<ItemViewModel> ItemsAsList { get; private set; }
public ObservableCollection<ItemViewModel> ItemsAsObservableCollection { get; private set; }
public IList<ItemViewModel> ItemsRecreatedList { get; private set; }
public MainViewModel()
{
ItemsAsList = new List<ItemViewModel>();
ItemsAsObservableCollection = new ObservableCollection<ItemViewModel>();
ItemsRecreatedList = new List<ItemViewModel>();
}
public void AddNewItem()
{
var newItem = new ItemViewModel();
// First try: add to list and raise property change - doesn't work.
ItemsAsList.Add(newItem);
RaisePropertyChanged(nameof(ItemsAsList));
// Second try: with ObservableCollection - doesn't work?
ItemsAsObservableCollection.Add(newItem);
// Third try: recreate the whole collection - works
ItemsRecreatedList.Add(newItem);
ItemsRecreatedList = new List<ItemViewModel>(ItemsRecreatedList);
RaisePropertyChanged(nameof(ItemsRecreatedList));
}
}
}
也ItemViewModel.cs
:
namespace UWP_ObservableCollection
{
public class ItemViewModel : BaseViewModel
{
private static int Counter;
public string Text { get; private set; }
public ItemViewModel()
{
Counter++;
Text = $"{Counter}";
}
}
}
这里是MainPage.xaml
:
<Page
x:Class="UWP_ObservableCollection.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWP_ObservableCollection"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="Page_Loaded">
<StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Items as List</TextBlock>
<local:MyItemsControl ItemsSource="{Binding ItemsAsList}"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Items as ObservableCollection</TextBlock>
<local:MyItemsControl ItemsSource="{Binding ItemsAsObservableCollection}"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Items recreated list</TextBlock>
<local:MyItemsControl ItemsSource="{Binding ItemsRecreatedList}"/>
</StackPanel>
<Button Click="Button_Click">Add new item</Button>
</StackPanel>
</Page>
MainPage.xaml.cs
:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace UWP_ObservableCollection
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainViewModel MainViewModel
{
get => DataContext as MainViewModel;
}
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender,RoutedEventArgs e)
{
DataContext = new MainViewModel();
}
private void Button_Click(object sender,RoutedEventArgs e)
{
MainViewModel.AddNewItem();
}
}
}
MyItemsControl.xaml
:
<UserControl
x:Class="UWP_ObservableCollection.MyItemsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWP_ObservableCollection"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<ItemsControl ItemsSource="{x:Bind ItemsSource,Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
MyItemsControl.xaml.cs
:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace UWP_ObservableCollection
{
public sealed partial class MyItemsControl : UserControl
{
// This works fine.
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",typeof(IList<ItemViewModel>),typeof(MyItemsControl),new PropertyMetadata(null,ItemsSourcePropertyChanged)
);
public IList<ItemViewModel> ItemsSource
{
get { return (IList<ItemViewModel>)Getvalue(ItemsSourceProperty); }
set { Setvalue(ItemsSourceProperty,value); }
}
// Uncomment this code to see the issue.
/*
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",typeof(IList<BaseViewModel>),ItemsSourcePropertyChanged)
);
public IList<BaseViewModel> ItemsSource
{
get
{
var values = Getvalue(ItemsSourceProperty) as IEnumerable<BaseViewModel>;
if (values is null)
{
return null;
}
return values.ToList();
}
set { Setvalue(ItemsSourceProperty,value); }
}
*/
private static void ItemsSourcePropertyChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Items changed");
}
public MyItemsControl()
{
this.InitializeComponent();
}
}
}
您需要执行以下步骤:
- 构建并运行应用。
- 看到有3个
MyItemsControl
正在使用3个不同的数据源-ItemsAsList
,ItemsAsObservableCollection
和ItemsRecreatedList
。 检查MainViewModel
并发现有3个来源:-
IList<ItemViewModel> ItemsAsList
-
ObservableCollection<ItemViewModel> ItemsAsObservableCollection
-
IList<ItemViewModel> ItemsRecreatedList
-
- 单击“添加新项目”。您应该看到,第2个和第3个集合已更新。检入称为{AddNewItem的
MainViewModel
方法。 它应该将项目添加到每个集合。 第一个问题:为什么将该项添加到第一个集合中,但是即使调用RaisePropertyChanged也不能更新UI? - 停止应用。
- 转到
MyItemsControl.xaml.cs
查找已注释的代码,取消注释并注释先前的代码。这会将IList<ItemViewModel>
更改为IList<BaseViewModel>
。 - 现在重建应用程序并再次运行。尝试再次单击“添加新项目”,然后注意
ObservableCollection
尚未更新。 第二个问题:为什么ObservableCollection不再触发getter?
同样,您可以在repo中找到所有这些内容!
感谢您的帮助,也许我错过了一些东西!我对第二个问题非常感兴趣,不知道为什么它不起作用。希望你能帮助我!