16

メニュー項目を ObservableCollection にバインドすると、MenuItem の「内部」領域のみがクリック可能になります。

代替テキスト http://tanguay.info/web/external/mvvmMenuItems.png

私のビューには、次のメニューがあります。

<Menu>
    <MenuItem 
        Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>

次に、このDataTemplateでバインドします。

<DataTemplate x:Key="MainMenuTemplate">
    <MenuItem
        Header="{Binding Title}" 
        Command="{Binding DataContext.SwitchPageCommand,
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}" 
        Background="Red"
        CommandParameter="{Binding IdCode}"/>
</DataTemplate>

ObservableCollection ManageMenuPageItemViewModelsの各 ViewModel にはプロパティTitleIdCodeがあるため、上記のコードは一見すると問題なく動作します。

ただし、問題は、DataTemplate のMenuItemが実際には別の MenuItem内にある ( 2 回バインドされているかのように) ため、上記の DataTemplate with Background="Red" では、各メニュー項目内に赤いボックスがあり、この領域のみがあることです。メニュー項目領域自体ではなく、クリックできます (たとえば、ユーザーがチェックマークのある領域、または内側のクリック可能領域の右または左をクリックした場合、何も起こりません。色は非常に紛らわしいです。)

各MenuItem内の領域全体がクリック可能になるように、MenuItemsをViewModelsのObservableCollectionにバインドする正しい方法は何ですか?

アップデート:

そのため、以下のアドバイスに基づいて次の変更を加えたところ、次のようになりました。

代替テキスト http://tanguay.info/web/external/mvvmMenuItemsYellow.png

DataTemplate 内に TextBlock しかありませんが、「MenuItem 全体に色を付ける」ことはできませんが、TextBlock だけです。

<DataTemplate x:Key="MainMenuTemplate">
    <TextBlock Text="{Binding Title}"/>
</DataTemplate>

CommandバインディングをMenu.ItemContainerStyleに入れましたが、今は起動しません:

<Menu DockPanel.Dock="Top">
    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Background" Value="Yellow"/>
            <Setter Property="Command" Value="{Binding DataContext.SwitchPageCommand,
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"/>
            <Setter Property="CommandParameter" Value="{Binding IdCode}"/>
        </Style>
    </Menu.ItemContainerStyle>
    <MenuItem 
        Header="MVVM" ItemsSource="{Binding MvvmMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
    <MenuItem 
        Header="Application" ItemsSource="{Binding ApplicationMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
    <MenuItem 
        Header="Manage" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>
4

4 に答える 4

37

MenuItemsでMVVMを使用するのは非常に難しいことがわかりました。私のアプリケーションの残りの部分では、DataTemplatesを使用してビューとViewModelをペアリングしていますが、説明した理由から、メニューでは機能しないようです。これが私が最終的にそれを解決した方法です。私の見解は次のようになります。

<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=(local:MainViewModel.MainMenu)}">
    <Menu.ItemContainerStyle>
        <Style>
            <Setter Property="MenuItem.Header" Value="{Binding Path=(contracts:IMenuItem.Header)}"/>
            <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=(contracts:IMenuItem.Items)}"/>
            <Setter Property="MenuItem.Icon" Value="{Binding Path=(contracts:IMenuItem.Icon)}"/>
            <Setter Property="MenuItem.IsCheckable" Value="{Binding Path=(contracts:IMenuItem.IsCheckable)}"/>
            <Setter Property="MenuItem.IsChecked" Value="{Binding Path=(contracts:IMenuItem.IsChecked)}"/>
            <Setter Property="MenuItem.Command" Value="{Binding}"/>
            <Setter Property="MenuItem.Visibility" Value="{Binding Path=(contracts:IMenuItem.Visible), 
                Converter={StaticResource BooleanToVisibilityConverter}}"/>
            <Setter Property="MenuItem.ToolTip" Value="{Binding Path=(contracts:IMenuItem.ToolTip)}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=(contracts:IMenuItem.IsSeparator)}" Value="true">
                    <Setter Property="MenuItem.Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type MenuItem}">
                                <Separator Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>
</DockPanel>

お気づきの方もいらっしゃると思いますが、MenuItemのViewModelであるIMenuItemというインターフェイスを定義しました。そのためのコードは次のとおりです。

public interface IMenuItem : ICommand
{
    string Header { get; }
    IEnumerable<IMenuItem> Items { get; }
    object Icon { get; }
    bool IsCheckable { get; }
    bool IsChecked { get; set; }
    bool Visible { get; }
    bool IsSeparator { get; }
    string ToolTip { get; }
}

IMenuItemがIEnumerableItemsを定義していることに注意してください。これは、サブメニューを取得する方法です。また、IsSeparatorは、メニューでセパレーターを定義する方法です(もう1つの難しい小さなトリック)。IsSeparatorがtrueの場合、xamlでDataTriggerを使用してスタイルを既存のセパレータースタイルに変更する方法を確認できます。MainViewModelが(ビューがバインドする)MainMenuプロパティを定義する方法は次のとおりです。

public IEnumerable<IMenuItem> MainMenu { get; set; }

これはうまくいくようです。MainMenuにObservableCollectionを使用できると思います。私は実際にMEFを使用してパーツからメニューを構成していますが、その後はアイテム自体が静的になります(各メニューアイテムのプロパティは静的ではありませんが)。また、IMenuItemを実装し、さまざまな部分のメニュー項目をインスタンス化するためのヘルパークラスであるAbstractMenuItemクラスを使用します。

アップデート:

あなたの色の問題に関して、このスレッドは役に立ちますか?

于 2009-07-01T12:55:49.443 に答える
14

に を入れないMenuItemでくださいDataTemplate。はの内容DataTemplate定義します。代わりに、経由で無関係なプロパティを指定します。MenuItemMenuItemItemContainerStyle

<Menu>
    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Header" Value="{Binding Title}"/>
            ...
        </Style>
    </Menu.ItemContainerStyle>
    <MenuItem 
        Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>

sも見てくださいHierarchicalDataTemplate

于 2009-07-01T08:26:32.577 に答える
2

これが私のメニューのやり方です。正確には必要なものではないかもしれませんが、かなり近いと思います。

  <Style x:Key="SubmenuItemStyle" TargetType="MenuItem">
    <Setter Property="Header" Value="{Binding MenuName}"></Setter>
    <Setter Property="Command" Value="{Binding Path=MenuCommand}"/>
    <Setter Property="ItemsSource" Value="{Binding SubmenuItems}"></Setter>
  </Style>

  <DataTemplate DataType="{x:Type systemVM:TopMenuViewModel}" >
    <Menu>
      <MenuItem Header="{Binding MenuName}"         
                    ItemsSource="{Binding SubmenuItems}" 
                    ItemContainerStyle="{DynamicResource SubmenuItemStyle}" />
    </Menu>
  </DataTemplate>

    <Menu DockPanel.Dock="Top" ItemsSource="{Binding Menus}" />

TopMenuViewModelは、メニューバーに表示されるメニューのコレクションです。それぞれに、表示されるMenuNameと、ItemsSourceとして設定したSubMenuItemsというコレクションが含まれています。

SumMenuItemStyleスタイルを使用してSubMenuItemsの表示方法を制御します。各SubMenuItemには、独自のMenuNameプロパティ、タイプICommandのCommandプロパティ、および場合によってはSubMenuItemの別のコレクションがあります。

その結果、すべてのメニュー情報をデータベースに保存し、実行時に表示されるメニューを動的に切り替えることができます。メニュー項目領域全体がクリック可能で、正しく表示されます。

お役に立てれば。

于 2009-07-01T12:33:13.170 に答える
2

DataTemplate を TextBlock (または、アイコンと TextBlock を持つスタック パネル) にするだけです。

于 2009-07-02T03:16:52.090 に答える