【问题标题】:How can I get the CollectionView that is defined in XAML如何获取 XAML 中定义的 CollectionView
【发布时间】:2012-06-18 06:49:20
【问题描述】:

我想绑定到 XAML 中的 ObservableCollection 并在那里应用分组。原则上,这很好用。

<UserControl.Resources>
    <CollectionViewSource x:Key="cvs" Source="{Binding Path=TestTemplates}">
        <CollectionViewSource.SortDescriptions>
            <scm:SortDescription PropertyName="Title"/>
        </CollectionViewSource.SortDescriptions>
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="TestCategory"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
</UserControl.Resources>

然后数据绑定表达式变成ItemsSource="{Binding Source={StaticResource ResourceKey=cvs}}"而不是ItemsSource="{Binding Path=TestTemplates}"

起初,一切看起来都很酷,直到我想从视图模型中刷新 UI。问题是,CollectionViewSource.GetDefaultView(TestTemplates) 返回的视图与应用分组的 XAML 中的视图不同。因此,我无法设置选择或对其进行任何有用的操作。

我可以通过将列表再次直接绑定到视图模型的属性并在代码隐藏中设置分组来修复它。但我对这个解决方案不太满意。

private void UserControlLoaded(object sender, RoutedEventArgs e)
{
    IEnumerable source = TemplateList.ItemsSource;
    var cvs = (CollectionView)CollectionViewSource.GetDefaultView(source);
    if (cvs != null)
    {
        cvs.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
        cvs.GroupDescriptions.Add(new PropertyGroupDescription("TestCategory"));
    }
}

我想,原因已经是given by John Skeet here

尽管如此,我希望应该有一种方法来获得正确的视图。我错了吗?

【问题讨论】:

  • 你做错了。虚拟机不应该知道视图。如果要更新视图,请确保它绑定到的属性是 ObservableCollection 或者您的代码在修改集合时显式引发 NotifyPropertyChanged。
  • @PanagiotisKanavos:列表视图中的内容实际上ObservableCollection 中,并且UI 中的项目确实会在属性更改时更新。但是分组不尊重这一点。一个已知的解决方法是强制更新,即CollectionViewSource.GetDefaultView(…).Refresh
  • 在 .NET 4.5 中,这将通过 ICollectionViewLiveShaping 来解决。

标签: c# wpf xaml code-behind collectionviewsource


【解决方案1】:

找到了一种方法,基于J. Lennon's 的答案。如果我通过我的命令传递了可以访问资源的东西,那么我可以在那里查找CollectionViewSource

在 XAML 中(CollectionViewResource 同上):

<Button Command="{Binding Command}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}">Do it!</Button>

在虚拟机代码中:

private void Execute(object parm)
{
    var fe = (FrameworkElement)parm;
    var cvs = (CollectionViewSource)fe.FindResource("cvs");
    cvs.View.Refresh();
}

Execute 是分配给RelayCommand 的那个。

这会回答这个问题,但我不太喜欢它。意见?

【讨论】:

  • 因为这比J. Lennon 的基础更完整(和工作),我标记了这个(但他得到了赏金)。
【解决方案2】:

你不能这样做吗?

var _viewSource = this.FindResource("cvs") as CollectionViewSource;

如果数据已连接,我认为会有更新的视图。

【讨论】:

  • 这也是同事提出来的。问题是在 VM 中,我无法访问任何 FrameworkElementFindResource(单独的程序集,只有 Vs 参考 VM)。
  • 当您的答案最接近时,您将获得赏金。明智地使用它。 ;-)
【解决方案3】:

我倾向于只从 VM 中公开集合视图,而不是让视图定义它:

public ICollection<Employee> Employees
{
    get { ... }
}

public ICollectionView EmployeesView
{
    get { ... }
}

这样,您的虚拟机就可以完全控制向视图显示的内容。例如,它可以更改排序顺序以响应某些用户操作。

【讨论】:

  • 这是一个很好的方法,但它不能回答问题。我还是想知道。
  • “刷新视图”是什么意思,为什么要这样做?如果你的VM暴露的底层集合实现了INotifyCollectionChanged(比如ObservableCollection&lt;T&gt;),那么你的视图创建的集合视图应该是最新的,这样就省去了“刷新视图”的麻烦。
  • 我应该符合上述条件。只要您只是添加/删除项目,集合视图就应该保持排序。如果您正在修改排序所依据的基础属性,这就是您需要“刷新”的正当理由之一。如果您真的希望视图创建 CV 而不是 VM,那么每当视图刷新时,您都必须在 VM 中引发一个事件。恕我直言,让 VM 处理这一切会更干净,因为它是业务逻辑。
  • 是的,基础集合是ObservableCollection&lt;T&gt;。但是不,它没有完全更新。排序列表aaa, bbb, eee 在属性更改时自动变为aaa, zzz, eee。要得到度假村,你必须告诉它。所以是的,这就是你提到的正当理由。
  • +1 表示您的第二条评论,这是另一种方式。尽管如此,我无法相信“正确”的简历真的无法访问。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 2022-11-30
  • 2020-07-05
  • 2020-11-23
  • 2010-12-31
  • 1970-01-01
相关资源
最近更新 更多