我面临着一些奇怪的行为ObservableCollection
,与DependencyProperty
. 我在这里创建了最小的可重现场景:https ://github.com/aosyatnik/UWP_ObservableCollection_Issue 。
有 2 个问题,我看到但无法解释。
这是我的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>),
typeof(MyItemsControl),
new PropertyMetadata(null, 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
和。检查并发现有 3 个来源:ItemsAsObservableCollection
ItemsRecreatedList
MainViewModel
IList<ItemViewModel> ItemsAsList
ObservableCollection<ItemViewModel> ItemsAsObservableCollection
IList<ItemViewModel> ItemsRecreatedList
- 单击“添加新项目”。您应该看到,第二个和第三个集合已更新。签入
MainViewModel
名为`AddNewItem 的方法。它应该将项目添加到每个集合中。 第一个问题:为什么item被添加到第一个collection中,但是调用了RaisePropertyChanged后UI没有更新? - 停止应用程序。
- 去
MyItemsControl.xaml.cs
查找注释代码,取消注释并注释以前的代码。这IList<ItemViewModel>
变为IList<BaseViewModel>
。 - 现在重建应用程序并再次运行它。尝试再次单击“添加新项目”并注意,
ObservableCollection
未更新。第二个问题:为什么 ObservableCollection 不再触发 getter 了?
同样,您可以在repo中找到所有这些!
感谢您的帮助,也许我错过了一些东西!我对第二个问题很感兴趣,不知道为什么它不起作用。希望,你会帮助我!