Борьба с HierarchicalDataTemplate в ContextMenu - PullRequest
1 голос
/ 25 мая 2019

Я бы хотел иметь возможность связать ItemsSource из ContextMenu с Collection в моей модели вида, показать Separator s в ContextMenu, и ItemsSource должно бытьиерархический (каждый уровень иерархии будет выглядеть одинаково).

В один из моих других вопросов Мне удалось показать элементы меню и разделители в привязке данных ContextMenu,но сейчас я изо всех сил пытаюсь сделать иерархию ItemsSource.

Сейчас я не знаю, что происходит, может быть, вы можете просветить меня?

Вот мой код снова (упрощенно, чтобы быть кратким, но работает):

MenuItemViewModel.vb

Public Class MenuItemViewModel
    Implements ICommand

    Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

    Public Property IsSeparator As Boolean
    Public Property Caption As String
    Private ReadOnly _subItems As List(Of MenuItemViewModel)

    Public Sub New(createItems As Boolean, level As Byte)
        _subItems = New List(Of MenuItemViewModel)

        If createItems Then
            _subItems.Add(New MenuItemViewModel(level < 4, level + 1) With {.Caption = "SubItem 1"})
            _subItems.Add(New MenuItemViewModel(False, level + 1) With {.IsSeparator = True, .Caption = "SubSep 1"})
            _subItems.Add(New MenuItemViewModel(level < 4, level + 1) With {.Caption = "SubItem 2"})
        End If
    End Sub

    Public ReadOnly Property SubItems As List(Of MenuItemViewModel)
        Get
            Return _subItems
        End Get
    End Property

    Public ReadOnly Property Command As ICommand
        Get
            Return Me
        End Get
    End Property

    Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
        MessageBox.Show(Me.Caption)
    End Sub

    Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
        Return True
    End Function

End Class

Модель представления для каждого элемента меню на каждом уровне имеет Caption для отображения в контекстном менюфлаг IsSeparator, указывающий, является ли он разделителем или функциональным элементом меню, Command, с которым нужно связываться, когда он является функциональным элементом меню, и, конечно, коллекция SubItems, содержащая функциональные элементы меню и разделители вплоть до определенногоуровень иерархии.

MainViewModel.vb

Public Class MainViewModel
    Private ReadOnly _items As List(Of MenuItemViewModel)

    Public Sub New()
        _items = New List(Of MenuItemViewModel)
        _items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 1"})
        _items.Add(New MenuItemViewModel(False, 0) With {.IsSeparator = True, .Caption = "Sep 1"})
        _items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 2"})
        _items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 3"})
        _items.Add(New MenuItemViewModel(False, 0) With {.IsSeparator = True, .Caption = "Sep 2"})
        _items.Add(New MenuItemViewModel(True, 0) With {.Caption = "Item 4"})
    End Sub

    Public ReadOnly Property Items As List(Of MenuItemViewModel)
        Get
            Return _items
        End Get
    End Property

End Class

Модель основного вида имеет только коллекцию Items, содержащую элементы функционального меню и разделители.

MainWindow.xaml

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp3"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance local:MainViewModel, IsDesignTimeCreatable=True}"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <Window.Resources>
        <ControlTemplate x:Key="mist" TargetType="{x:Type MenuItem}">
            <Separator />
        </ControlTemplate>

        <ControlTemplate x:Key="mict" TargetType="{x:Type MenuItem}">
            <MenuItem Header="{Binding Caption}" Command="{Binding Command}" ItemsSource="{Binding SubItems}" />
        </ControlTemplate>

        <Style x:Key="cmics" TargetType="{x:Type MenuItem}">
            <Setter Property="Template" Value="{StaticResource mict}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsSeparator}" Value="True">
                    <Setter Property="Template" Value="{StaticResource mist}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Grid>
        <TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Text="Right click me">
            <TextBox.ContextMenu>
                <ContextMenu ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource cmics}">
                    <ContextMenu.ItemTemplate>
                        <HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}" ItemsSource="{Binding SubItems}" />
                    </ContextMenu.ItemTemplate>
                </ContextMenu>
            </TextBox.ContextMenu>
        </TextBox>
    </Grid>
</Window>

Ресурсы окна содержат два ControlTemplate s "mist" и "mict" и Style "cmics", которые переключаются между двумя ControlTemplate s в зависимости от значения флага IsSeparator.Это прекрасно работает, если ItemsSource не является иерархическим (см. мой другой вопрос ).

Если мои Style "смайлики" присоединены к ItemContainerStyle из *Только 1054 * (как в моем примере кода), тогда это выглядит так:

enter image description here

Первый уровень работает, но другие нет.Это не изменится и при присоединении моих Style "cmics" к ItemContainerStyle из HierarchicalDataTemplate.

Если я только прикреплю свои Style "cmics" к HierarchicalDataTemplate, тоэто выглядит так:

enter image description here

Первый уровень не показывает заголовки и разделители, второй уровень работает, а другие уровни не работают.

Итак, как я могу убедить ContextMenu использовать мои Style "cmics" в качестве ItemContainerStyle для каждого уровня иерархии?

Ответы [ 2 ]

1 голос
/ 27 мая 2019

Я нашел ответ здесь .

Мне пришлось создать пустую модель представления только для разделителей и класса, производного от ItemContainerTemplateSelector, чтобы вернуть DataTemplate, который принадлежитк типу пункта меню («MenuItemViewModel» или «SeparatorViewModel»).

Связанная статья не требует пояснений.

0 голосов
/ 25 мая 2019

Я только что внес некоторые изменения в ваш (TextBox) в части Xaml. Посмотрите на это,

<TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Text="Right click me">
            <TextBox.Resources>
                <HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}" ItemsSource="{Binding SubItems}">
                    <Button Content="{Binding Caption}" Command="{Binding Command}" Background="Red"/>
                </HierarchicalDataTemplate>
            </TextBox.Resources>
            <TextBox.ContextMenu>
                <ContextMenu ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource cmics}"/>
            </TextBox.ContextMenu>
        </TextBox>

По сути, я удалил ваш ItemTemplate ContexttMenu и добавил в TextBox. Ресурсы иерархический шаблон данных.

Внутри шаблона данных я только что добавил переключатель. Вы можете изменить содержание в соответствии с вашими потребностями.

Дайте мне знать, если это решит вашу проблему или вам нужна другая помощь.

...