c# - ObservableCollection<> 不通知视图
问题描述
我一直在寻找一段时间,我只是找不到我做错了什么。
我有一个我在视图中显示的名称列表,在我的视图中我创建了一个 itemsControl,ItemsSource 设置为 ViewModel 中的 observableCollection。目的是概述 nxn 表中的可用名称。用户应该能够使用顶部的搜索框过滤结果。
首先,我使用列表列表进行了尝试,该列表不起作用,因为视图没有根据搜索框中的字符串进行更新。我发现我实际上应该使用 ObservableCollection,因为它实现了 INotifyCollectionChanged。下面我尝试实现一个 ObservableCollection,但是当这个集合发生变化时我无法更新视图。
查看(请参阅 Xaml 部分中的 ItemsControl):
Xaml 资源
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate_Level2">
<Button Content="{Binding}" Height="150" Width="150" Margin="20,20,20,20">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="#2ED99A"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#2EB9D9"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="#333f4a"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_Level1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</UserControl.Resources>
Xaml
<Grid Margin="10,10,10,10" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Left">
<TextBlock Text="{Binding HcfOverviewModel.NSharedModules, StringFormat=Shared Circuits: {0}}"></TextBlock>
<TextBlock Margin="30,0,0,30" Text="{Binding HcfOverviewModel.NPrivateModules, StringFormat=Private Circuits: {0}}"></TextBlock>
</StackPanel>
<Button Grid.Column="1" VerticalAlignment="Top" MaxHeight="20" Content="See Private Circuits"></Button>
</Grid>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Hidden">
<ItemsControl HorizontalAlignment="Center" VerticalAlignment="Top" ItemsSource="{Binding Modules, UpdateSourceTrigger=PropertyChanged}" ItemTemplate="{DynamicResource DataTemplate_Level1}"/>
</ScrollViewer>
</Grid>
视图模型:
class HcfOverviewViewModel:BaseViewModel
{
private HcfOverviewModel _hcfOverviewModel;
private string _pathToSharedModules;
//private List<List<string>> _modules;
private ObservableCollection<ObservableCollection<string>> _modules;
public HcfOverviewViewModel()
{
_pathToSharedModules = @"C:\Users\scamphyn\source\repos\TSD\TSD\TempTestFolder";
_hcfOverviewModel = new HcfOverviewModel();
_hcfOverviewModel.NSharedModules = CheckDirectory.FindModules(_pathToSharedModules).Length;
_hcfOverviewModel.SharedModuleNames = CleanModuleNames(CheckDirectory.FindModules(_pathToSharedModules));
//_modules = CreateGridLayout(_hcfOverviewModel.SharedModuleNames);
_modules = CreateGrid(_hcfOverviewModel.SharedModuleNames);
}
public HcfOverviewModel HcfOverviewModel
{
get => _hcfOverviewModel;
}
public void Filter(string searchString)
{
Console.WriteLine(searchString);
if (searchString == "")
{
//Modules = CreateGridLayout(_hcfOverviewModel.SharedModuleNames);
Modules = CreateGrid(_hcfOverviewModel.SharedModuleNames);
}
else
{
Modules.Clear();
string[] filteredNames = _hcfOverviewModel.SharedModuleNames.Where(n => n.Contains(searchString)).ToArray();
//Modules = CreateGridLayout(filteredNames);
Modules = CreateGrid(filteredNames);
}
}
private string[] CleanModuleNames(string[] listToClean)
{
List<string> moduleNames = new List<string>();
foreach (string s in listToClean)
{
string[] listPath = s.Split('\\');
string name = listPath[listPath.Length - 1];
moduleNames.Add(name);
}
return moduleNames.ToArray();
}
public ObservableCollection<ObservableCollection<string>> CreateGrid(string[] moduleNames)
{
ObservableCollection<ObservableCollection<string>> lsts = new ObservableCollection<ObservableCollection<string>>();
int circuitsPerRow = 4;
int _nRows = moduleNames.Length / circuitsPerRow;
Queue<string> modules = new Queue<string>(moduleNames);
for(int i = 0; i <= _nRows; i++)
{
lsts.Add(new ObservableCollection<string>());
for (int j = 0; j < circuitsPerRow; j++)
{
if (modules.Count != 0)
{
lsts[i].Add(modules.Dequeue());
}
else
{
break;
}
}
}
return lsts;
}
public List<List<string>> CreateGridLayout(string[] moduleNames)
//To Create the grid in HcfOverview
{
List<List<string>> lsts = new List<List<string>>(); // create list of list to store nxn matrix data in.
int circuitsPerRow = 4;
//Determine correct size nxn
int _nRows = moduleNames.Length / circuitsPerRow;
//Create Queue
Queue<string> modules = new Queue<string>(moduleNames);
//Create data for ItemControls
for (int i = 0; i <= _nRows; i++) // number of rows
{
lsts.Add(new List<string>());
for (int j = 0; j < circuitsPerRow; j++) // number of columns
{
if (modules.Count != 0)
{
lsts[i].Add(modules.Dequeue());
}
else {
break;
}
}
}
return lsts;
}
public ObservableCollection<ObservableCollection<string>> Modules
{
get => _modules;
set {
_modules = value;
OnPropertyChanged("Modules");
}
}
}
“_modules”是我的 ObservableCollection 填充在 CreateGrid 方法中。
这是它在窗口中的样子:
当我检测到更新时,搜索框位于“父”视图/视图模型中,我在上面显示的视图模型中调用过滤器方法来更新 ObservableCollection。
如何解决这个问题?
更新:
因为我已经被这个问题困扰了一周,所以我决定创建一个新的“项目”并检查我是否可以让它在一个简化的项目中工作。在这个简化的项目中,我设法添加和删除行。我使用了与以前完全相同的方法。所以我的 observablecollection 的绑定和更新是正确的。我现在想知道这个问题是否在于我如何加载这个 UserControl。显示此数据的 UserControl 是通过另一个 View 加载的。见下面的代码:
<ContentControl Grid.Row="2" Content="{Binding CurrentHcfViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type ViewModels:HcfOverviewViewModel}">
<views:UserControlHcfOverview/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
我的 HcfOverviewViewModel 被加载到存在于不同视图中的用户控件中。这会对 ObeservableCollection 及其在 HcfOverviewViewModel 中的 View 的更新产生影响吗?
解决方案
我发现我实际上应该使用 ObservableCollection,因为它实现了 INotifyCollectionChanged。
由于您进行更新的方式,它没有像您认为的那样工作。当一个人将引用从一个更改ObservableCollection
为另一个ObservableCollection
时,这不会发送INotifyCollectionChanged
消息。只有添加/删除/清除调用会在活动引用的集合上生成该消息。
当您重置参考时,您正在告诉ItemsControl
完全清除自身,然后显示正在清除的内容。
Modules
创建初始集合后,您无需更改它。您的逻辑还存在其他问题,除非有人重写您的自定义控件/逻辑,否则无法完全回答这个问题。
无论如何,您都需要从不更改引用开始,然后以与 parent 相同的模式处理更改内部集合项ObservableCollection
。
请注意,某些控件Null
在设置为新列表时需要设置。不清楚这是否也使问题复杂化,因为您立即更改为较新的参考,而新的参考顶级似乎并没有改变整体ItemsControl
。
推荐阅读
- data-structures - 固定大小的容器,其中物品根据需要存在
- mysql - 我曾尝试重置 MySQL root 密码,尝试了 3 种方法寻找替代方案?我正在使用 Windows 机器的命令行
- json - 如何在swift中使用JSON日期字符串显示过去和未来的日期
- c++ - 将排列分解为具有少于 O(n) 额外内存的循环
- c - C原始套接字函数失败
- xamarin.forms - 一个 ViewModel 如何包含多个 ViewModel xamatin
- sql - 授予名称中包含“-”和“.”的 Teradata Database 的权限
- url - 浏览器中的共享按钮复制旧地址 url
- c - 为什么 gcc-arm-none-eabi 切换速度较慢:GCC7 vs GCC10
- gcc - MSYS2 gcc 未定义引用_assert,我缺少什么库?