按 DataTemplate 中 ViewModel 的 属性 对 ListView 进行分组

Grouping ListView by Property of ViewModel in DataTemplate

我有一个问题,我已经阅读了很多关于 MVVM 分组的文章都没有解决。

我正在编写 WPF 应用程序。以下是 classes 中与我的问题相关的一些例外情况 - 首先是 MainViewModel:

public class MainViewModel
{
    public ObservableCollection<Recipe_OverViewModel> RecipeOverViews {get ; set;}

....[omitted extraneous lines]....

在 MainViewModel 中用作 Recipes 的可观察对象 collection 的 class:

public class Recipe_OverViewModel
{
    public Recipe TargetRecipe { get; set; }

    public override string ToString()
    {
        return TargetRecipe.Parent_Name;
    }

....[omitted extraneous lines]....

和从数据库中获取的Class,即实际的食谱

public partial class Recipe
{
    [Required]
    [StringLength(1000)]
    public string Parent_Name { get; set; }

    [Required]
    [StringLength(60)]
    public string Recipe_Name { get; set; }

    public override string ToString()
    {
        return Parent_Name;
    }
    public int CompareTo(object obj)
    {
        return Parent_Name.CompareTo(((Recipe)obj).Parent_Name);
    }

 ....[omitted extraneous lines]....

每个class都有更多的属性和方法等等,但是这些足以说明我在问什么了。

Recipe_OverViewModel 是显示配方属性的控件 (Recipe_OverView) 的视图模型。在 MainViewModel 中,我有以下 xaml(从较大的文件中提取):

<Window x:Class="RecipeApp.UI.MainWindow"

....[omitted extraneous lines]....

    xmlns:vm="clr-namespace:RecipeApp.UI.ViewModel"
    >
<Window.Resources>
    <DataTemplate DataType="{x:Type vm:Recipe_OverViewModel}" x:Key="Recipe_DT" x:Name="Recipe_DT">
        <control:Recipe_OverView Width="{Binding ActualWidth,ElementName=ListWidth}"/>
    </DataTemplate>
    <CollectionViewSource x:Key="SortedRecipeOverViews" Source="{Binding RecipeOverViews}">
        <CollectionViewSource.SortDescriptions>
            <scm:SortDescription PropertyName="TargetRecipe"/>
        </CollectionViewSource.SortDescriptions>
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="TargetRecipe"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
</Window.Resources>

....[omitted extraneous lines]....

                <ListView ItemsSource="{Binding Source={StaticResource SortedRecipeOverViews}}"
                          ItemTemplate="{StaticResource Recipe_DT}">
                 </ListView>

此列表视图正确显示列表视图中的配方列表,每行包含 Recipe_OverView 控件。但是,我无法使分组正常工作。我想通过与每个 Recipe_OverViewModel 关联的食谱的 Parent_Name 属性 对列表视图进行分组。我的尝试看起来像这样,遵循 Microsoft HowTo:

            <ListView Grid.Row="1" Grid.Column="1"
                     ItemsSource="{Binding Source={StaticResource SortedRecipeOverViews}}"
                     ItemTemplate="{StaticResource Recipe_DT}"
                     >
                <ListView.GroupStyle>
                    <GroupStyle>
                        <GroupStyle.ContainerStyle>
                            <Style TargetType="{x:Type GroupItem}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type GroupItem}">
                                            <Expander IsExpanded="true">
                                                <Expander.Header>
                                                    <TextBlock Text="{Binding Path=ParentName}" />
                                                </Expander.Header>
                                            </Expander>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </GroupStyle.ContainerStyle>
                    </GroupStyle>
                </ListView.GroupStyle>
            </ListView>

但是,我从中得到的只是空的(没有填充 ParentName)headers,因为 collection 中有食谱,并且绑定失败告诉我“ParentName 属性 在 CollectionViewGroupInternal 类型的 object 上找不到。”我得到了扩展按钮,但扩展组中没有任何内容:

我想我明白这意味着绑定正在 Recipe_OverViewModel 中查找 ParentName 属性,但即使我将其作为 属性 添加到 Recipe_OverViewModel 并填充它,我仍然得到这个错误,所以现在我很困惑并有以下问题:

ListView 上的绑定实际上在哪里查找? 我应该如何引导它查看Recipe_OverViewModel.TargetRecipe.ParentName(或者不可能)?

在这件事上我真的很感激帮助,很多文章都采用了如此简单的例子,我不知道如何将其扩展到我的案例中!

ItemsSource 绑定到 SortedRecipeOverViews,后者又绑定到 RecipeOverViews 集合。
此集合的项目类型是 Recipe_OverViewModel.
而且这种类型没有 ParentName 属性.
有一个 Parent_Name 属性,但不是 Recipe_OverViewModel 类型,而是 Recipe 类型。
Recipe_OverViewModel 类型有一个 属性 这种类型。
一般来说,你有一些混乱的类型、它们的名称和它们的属性、绑定路径。
也许您在主题中复制了错误的内容?

根据我自己的猜测,尝试应用这样的绑定:

 <Expander.Header>
     <TextBlock Text="{Binding Path=TargetRecipe.Parent_Name}" />
 </Expander.Header>

Where is the binding on the ListView actually looking?

它寻找 CollectionViewGroupInternal class 的 属性。

这个 class 有一个 Name 属性 那个 returns 你分组的 属性 的值,即 TargetRecipe,和 Items 属性 returns 属于当前组的对象集合。

因此,如果我正确理解您的设置,您可以尝试绑定到组中第一项的 Parent_Name 属性:

<Expander.Header>
    <TextBlock Text="{Binding Items[0].Parent_Name}" />
</Expander.Header>