Создать MenuItems из коллекции

Я пытался создать MenuItems элементов коллекции - и не удалось. Подробно: у меня есть простой класс ClassA, который определяет строковое свойство 'HeadText'. В моей MainViewModel я определил свойство ObservableCollection. Коллекция наполнена 3 предметами. Теперь в XAML я хочу создать MenuItems этих 3 элементов типа ClassA. Я сделал следующее:

<Window.Resources>
    <CompositeCollection x:Key="CollA">
        <ItemsControl ItemsSource="{Binding Path=MItems}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <MenuItem Header="{Binding HeadText}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </CompositeCollection>
</Window.Resources>

<Grid>
    <Menu DockPanel.Dock="Top" ItemsSource="{Binding Source={StaticResource CollA}}"/>
</Grid>

Но все, что я получаю, это пустая строка меню. Любые идеи, как я могу это сделать?

Модель представления и класс ClassA:

public class MainVM
{
    public MainVM() {
        _mItems.Add(new ClassA() { HeadText = "A" });
        _mItems.Add(new ClassA() { HeadText = "B" });
        _mItems.Add(new ClassA() { HeadText = "C" });
    }

    private ObservableCollection<ClassA> _mItems = new ObservableCollection<ClassA>();
    public ObservableCollection<ClassA> MItems{
        get { return _mItems; }
    }
}

public class ClassA
{
    public ClassA() { }
    public String HeadText { get; set; }
}

Заранее спасибо.

Редактировать:

Если я напишу это, это работает:

<Menu DockPanel.Dock="Top" ItemsSource="{Binding MItems}">
    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
            <Setter Property="Header" Value="{Binding HeadText}"/>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>

Но я хочу сделать это по-другому. И мне интересно, почему другой способ не работает.

1 ответ

Решение

Я нашел ответ здесь. Мой XAML сначала выглядел так:

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

    <Window.Resources>
        <CollectionViewSource Source="{Binding Path=MItems}" x:Key="source"/>
    </Window.Resources>

    <StackPanel>
        <Menu DockPanel.Dock="Top">
            <Menu.ItemContainerStyle>
                <Style TargetType="MenuItem">
                    <Setter Property="Header" Value="{Binding HeadText}"/>
                </Style>
            </Menu.ItemContainerStyle>
        <Menu.ItemsSource>
            <CompositeCollection>
                <MenuItem Header="Static 1"/>
                <MenuItem Header="Static 2"/>
                <CollectionContainer Collection="{Binding Source={StaticResource source}}"/>
                <MenuItem Header="Static 3"/>
            </CompositeCollection>
        </Menu.ItemsSource>
    </Menu>
</StackPanel>

И MainVM.cs и ClassA:

public class MainVM
{
    public MainVM() {
        _mItems.Add(new ClassA() { HeadText = "Dyn1" });
        _mItems.Add(new ClassA() { HeadText = "Dyn2" });
        _mItems.Add(new ClassA() { HeadText = "Dyn3" });
    }

    private ObservableCollection<ClassA> _mItems = new ObservableCollection<ClassA>();
    public ObservableCollection<ClassA> MItems{
        get { return _mItems; }
    }
}

public class ClassA
{
    public ClassA() { }

    private string _text = "";
    public String HeadText {
        get { return _text; }
        set { _text = value; }
    }
}

Это прекрасно работает, но вы должны заметить, что Visual Studio выдаст вам ошибки BindingExpression в случае, если вы действительно смешаете статические и динамические элементы меню.

Причина этого заключается в том, что синтаксический анализатор XAML применяет указанный ItemContainerStyle к динамическому элементу AND к статическим элементам меню: он создает новый MenuItem и связывает свойство HeadText со свойством Header вновь созданного MenuItem. Это делается как для динамических пунктов меню, так и для статических. Поскольку в классе MenuItem отсутствует свойство HeadText, отображается ошибка.

Приложение не сбоит, но это не очень хорошее решение. Обходной путь для этого состоит в том, чтобы разделить MenuBar как это:

    <StackPanel>
        <Grid DockPanel.Dock="Top">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Menu Grid.Column="0">
                <MenuItem Header="Static 1"/>
                <MenuItem Header="Static 2"/>
            </Menu>
            <Menu Grid.Column="1">
                <Menu.ItemContainerStyle>
                    <Style TargetType="MenuItem">
                        <Setter Property="Header" Value="{Binding HeadText}"/>
                    </Style>
                </Menu.ItemContainerStyle>
                <Menu.ItemsSource>
                    <CompositeCollection>
                        <CollectionContainer Collection="{Binding Source={StaticResource source}}"/>
                    </CompositeCollection>
                </Menu.ItemsSource>
            </Menu>
            <Menu Grid.Column="2">
                <MenuItem Header="Static 3"/>
            </Menu>
        </Grid>
    </StackPanel>

Может быть, есть более хорошее решение. Дайте мне знать, если это так.

Другие вопросы по тегам