Используйте CollectionViewSource с TabControl

Я пытаюсь сгруппировать и отобразить элементы ObservableCollection, просто используя код XAML. Он хорошо работает с использованием простого CollectionViewSource и ListBox[1].

На самом деле, я бы предпочел отображать содержимое группы во вкладке. Google привел меня к следующей статье social.msdn, в которой представлен обходной путь для отображения групп в виде TabControl с использованием кода:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/e073f275-0826-4fca-b9da-e310ccf1713e/wpf-grouping?forum=wpf

Однако, поскольку я использую MVVM и должен полагаться только на xaml, я не могу заставить его работать. На самом деле CollectionViewSource заполняет группы (TabControl показывает правильные заголовки tabItemHeaders), но щелчок по любому из этих TabItem останавливает приложение. Вот что я пробовал:

    <StackPanel x:Key="ModulSelectInputParameterView">
    <StackPanel.Resources>
        <CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="Category"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </StackPanel.Resources>

    <Grid >
        <TabControl ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups, Mode=OneWay}" DataContext="{Binding Source={StaticResource cvs}, Mode=OneWay}">
            <!-- First Level -->
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <ListBox ItemsSource="{Binding Items}">
                         Second Level 
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <Expander Header="{Binding Name}">
                                    <ListBox ItemsSource="{Binding Items}">
                                         The Item of the Collection 
                                        <ListBox.ItemTemplate>
                                            <DataTemplate>
                                                <StackPanel Orientation="Horizontal">
                                                    <TextBlock Text="{Binding Key}"/>
                                                    <TextBlock Text=" - "/>
                                                    <TextBlock Text="{Binding Value.Comment}"/>
                                                </StackPanel>
                                            </DataTemplate>
                                        </ListBox.ItemTemplate>
                                    </ListBox>
                                </Expander>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>

</StackPanel>

[1]: этот кусок xaml работает, как и ожидалось, но использует панель обертки для отображения содержимого групп:

<StackPanel x:Key="ModulSelectInputParameterView">
    <StackPanel.Resources>
        <CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="Category"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </StackPanel.Resources>

    <ListBox ItemsSource="{Binding Source={StaticResource cvs}}" VerticalContentAlignment="Top" ItemContainerStyle="{StaticResource ModulSelectInputParameterListBoxItemContainerStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ListBox.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <Border BorderBrush="DarkGray" BorderThickness="2" Margin="2">
                            <TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Path=Name}" HorizontalAlignment="Center" MinWidth="100"/>
                        </Border>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
                <GroupStyle.Panel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Horizontal" Margin="2"/>
                    </ItemsPanelTemplate>
                </GroupStyle.Panel>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
                        <Setter Property="Control.Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type GroupItem}">
                                    <Border BorderThickness="2" BorderBrush="DarkGray">
                                        <StackPanel>
                                            <ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
                                            <ItemsPresenter Margin="2,0,2,2" />
                                        </StackPanel>
                                    </Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>

        </ListBox.GroupStyle>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True" Orientation="Vertical" VerticalAlignment="Top"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>

    </ListBox>
</StackPanel>

person Bechi    schedule 25.04.2016    source источник


Ответы (2)


Я думаю, что что-то не так с вашей привязкой, ваш код должен работать. Чтобы получить одни и те же элементы в обоих списках, попробуйте связать второй источник элементов списка с первым источником элементов списка следующим образом:

<ListBox ItemsSource="{Binding Items}" Name="ListBox">

                                    <ListBox.ItemTemplate>
                                        <DataTemplate>
                                            <Expander Header="{Binding Key}">
                                                <ListBox ItemsSource="{Binding ItemsSource, ElementName=ListBox}">

                                                    <ListBox.ItemTemplate>
                                                        <DataTemplate>
                                                            <StackPanel Orientation="Horizontal">
                                                                <TextBlock Text="{Binding Key}"/>
                                                                <TextBlock Text=" - "/>
                                                                <TextBlock Text="{Binding Value}"/>
                                                            </StackPanel>
                                                        </DataTemplate>
                                                    </ListBox.ItemTemplate>
                                                </ListBox>
                                            </Expander>
                                        </DataTemplate>
                                    </ListBox.ItemTemplate>
                                </ListBox>
person Community    schedule 25.04.2016
comment
К сожалению, это не решает проблему. Но я получаю следующее сообщение об ошибке, возможно, причина этой ошибки может помочь решить эту проблему: Обнаружен потенциальный цикл в дереве при построении маршрута события. - person Bechi; 25.04.2016
comment
Думаю, вам следует удалить «контекст данных» из вашего TabControl. - person ; 25.04.2016
comment
Действительно, сообщение об ошибке давало подсказку: это был не (ненужный) DataContext. Это проблема с DataTemplates в стилях, которые обновляются с помощью триггеров данных, которые вызывают вышеупомянутый цикл в дереве. Решение Джона работает нормально, и я дам краткое сообщение об ошибке в отдельном ответе. - person Bechi; 25.04.2016

Спасибо Джо, который помог с соответствующей привязкой данных. Однако причина была совершенно в другом, быстрое и грязное решение приведено ниже [1]:

По сути, мне не хватало того, что вышеупомянутый элемент управления вкладками вложен во внешний элемент управления вкладками в моем главном окне. Я не совсем уверен в правильности следующего описания[1], но, на мой взгляд, причина в следующем:

  • Внешний TabControl использует стиль для отображения своего содержимого. Это содержимое применяется к ViewModel, которая содержит упомянутую выше наблюдаемую коллекцию, которая, в свою очередь, должна быть ItemsSource для CollectionViewSource, которая передает внутренний tabControl с группами.
  • Поскольку я определил этот стиль только во внешнем TabControl.Resources и пропустил определение отдельного стиля для внутренней вкладки Control, внутренний tabcontrol наследует внешний стиль и пытается отображать свои данные, используя то же содержимое.
  • Это содержимое снова является другим внутренним tabControl, который вызывает другой внутренний tabControl и так далее.

[1] Определение пустого стиля в inenr tabControl.Resources решило проблему:

<TabControl.Resources>
                <Style TargetType="TabItem">

                </Style>
            </TabControl.Resources>

Я был бы рад, если бы кто-то мог подтвердить эту идею или предоставить несколько ссылок на известные проблемы с общими стилями во вложенных элементах управления.

person Bechi    schedule 25.04.2016