微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ObservableCollection<> 不通知视图 Xaml 资源XML视图模型:

如何解决ObservableCollection<> 不通知视图 Xaml 资源XML视图模型:

我已经搜索了一段时间,但找不到我做错了什么。

我有一个在视图中显示名称列表,在我的视图中我创建了一个 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">
            <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>

XML

<Grid Margin="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,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 方法中。

这是窗口中的样子:

enter image description here

当我检测到更新时,搜索框位于“父”视图/视图模型中,我在上面显示的视图模型中调用过滤器方法来更新 ObservableCollection。

如何解决这个问题?

更新:

因为我已经被这个问题困扰了一周,所以我决定创建一个新的“项目”并检查我是否可以让它在一个简化的项目中工作。 在这个简化的项目中,我设法添加删除了行。我使用了与之前使用的完全相同的方法。所以我的 observablecollection 的绑定和更新是正确的。我现在想知道这个问题是否与我加载这个 UserControl 的方式有关。显示此数据的 UserControl 是通过另一个视图加载的。见下面的代码

<ContentControl Grid.Row="2" Content="{Binding CurrentHcfviewmodel}">
        <ContentControl.Resources>
            <DataTemplate DataType="{x:Type viewmodels:HcfOverviewviewmodel}">
                <views:UserControlHcfOverview/>
            </DataTemplate>
        </ContentControl.Resources>
    </ContentControl>

我的 HcfOverviewviewmodel 被加载到存在于不同视图中的用户控件中。这是否会影响 HcfOverviewviewmodel 中 ObeservableCollection 及其视图的更新?

解决方法

我发现我实际上应该使用 ObservableCollection,因为它实现了 INotifyCollectionChanged。

由于您进行更新的方式,它并没有像您认为的那样工作。当将 reference 从一个 ObservableCollection 更改为另一个 ObservableCollection 时,不会发送 INotifyCollectionChanged 消息。只有添加/删除/清除调用会在 活动 引用的集合上生成该消息。

当您重置引用时,您是在告诉 ItemsControl 完全清除自身,然后显示正在清除的内容。

在创建初始 Modules 集合后,您无需更改它。您的逻辑还存在其他问题,除非有人重写您的自定义控件/逻辑,否则无法完全回答这个问题。

无论您是否需要从不更改引用开始,然后以与父 ObservableCollection 相同的模式处理不断变化的内部集合项。


请注意,某些控件在设置为新列表时需要设置 Null。目前尚不清楚这是否也会使问题更加复杂,因为您会立即更改为更新的参考,而新的参考顶级似乎并没有改变整个 ItemsControl

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。