2009-05-26 30 views
5

静的メニュー項目と動的メニュー項目の両方が必要なシナリオがあります。静的アイテムは、XAMLで定義され、動的アイテムはビューモデルによって提供されます。それぞれの動的項目はVieModelで表され、CommandViewModelと呼ぶことができます。 CommandViewModelには表示名があり、他のCommandViewModelも含めることができます。動的XAMLメニュー項目と静的XAMLメニュー項目の混在

次のようにメニュー用のDataContextがあるとして使用されますMainViewModel:

public class MainMenuViewModel : INotifyPropertyChanged 
{ 

    private ObservableCollection<CommandViewModel> m_CommandVMList; 


    public MainMenuViewModel() 
    { 
    m_ CommandVMList = new ObservableCollection<CommandViewModel>(); 

    CommandViewModel cmv = new CommandViewModel(); 
    cmv.DisplayName = "Dynamic Menu 1"; 
    m_CommandVMList.Add(cmv); 

    cmv = new CommandViewModel(); 
    cmv.DisplayName = "Dynamic Menu 2"; 
    m_CommandVMList.Add(cmv); 

    cmv = new CommandViewModel(); 
    cmv.DisplayName = "Dynamic Menu 3"; 
    m_CommandVMList.Add(cmv); 

    } 

    public ObservableCollection<CommandViewModel> CommandList 
    { 
    get { return m_CommandVMList; } 
    set 
    { 
     m_CommandVMList = value; 
     OnPropertyChanged("CommandList"); 
    } 
    } 

... ... ...

メニューXAML:

<Grid> 
    <Grid.Resources> 
    <HierarchicalDataTemplate DataType="{x:Type Fwf:CommandViewModel}" ItemsSource="{Binding Path=CommandViewModels}"> 
     <MenuItem Header="{Binding Path=DisplayName}"/> 
    </HierarchicalDataTemplate> 
    </Grid.Resources> 

    <Menu VerticalAlignment="Top" HorizontalAlignment="Stretch"> 
    <MenuItem Header="Static Top Menu Item 1"> 
     <MenuItem Header="Static Menu Item 1"/> 
     <MenuItem Header="Static Menu Item 2"/> 
     <MenuItem Header="Static Menu Item 3"/> 
     <ItemsControl ItemsSource="{Binding Path= CommandList}"/> 
     <MenuItem Header="Static Menu Item 4"/> 
     </MenuItem> 
    </Menu> 
</Grid> 

除くすべて正常に動作します動的なメニューのリストを表現しようとすると、この場合ItemsControlはUIに1つのMenu Iteとして表示されていますより多くのメニュー項目をまとめているので、項目をクリックするとダイナミックメニュー項目のコレクション全体が選択されます。コレクションは、各動的メニュー項目がメニュー項目自体として表示され、この大きなメニュー項目内に表示されるという点で、正しく表されます。私は、メニューが、静的または動的な、それが気にしない各項目のメニュー項目を作成するだけなのはなぜかと思う。各ダイナミックメニュー項目を同じレベルで作成し、親メニュー項目に属するようにする方法はありますか?

答えて

5

"静的な"メニュー項目をXAML側でハードコードするのではなく、VM側でCommandViewModelオブジェクトとしてハードコードします。

ハードコーディングすると柔軟性が失われることはなく、静的メニュー項目をHierarchicalDataTemplateと同期させておくと、今後別の方法でレンダリングする必要があります。

メニューがメニュー項目のコレクションにバインドされるようにバインドを変更する必要があることに注意してください。このhereの例が見つかります。

EDIT:コードサンプル

私はかなり迅速にこれをハックすることができた、とクラス定義のほとんどは(例えばINotifyPropertyChangedの)不完全ですが、それはあなたが何ができるかのアイデアを与える必要があります。 3番目のコマンドにネストしたコマンドをいくつか追加して、Hierarchical DataTemplateが機能することを確認しました。

ここでXAML

<Window 
    x:Class="WPFDynamicMenuItems.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:WPFDynamicMenuItems" 
    Title="Window1" Height="300" Width="600"> 
    <Grid> 
     <Grid.Resources> 
      <HierarchicalDataTemplate DataType="{x:Type local:CommandViewModel}" ItemsSource="{Binding Path=CommandList}"> 
       <ContentPresenter 
        Content="{Binding Path=DisplayName}" 
        RecognizesAccessKey="True" /> 
      </HierarchicalDataTemplate> 
     </Grid.Resources> 
     <ToolBarTray> 
      <ToolBar> 
      <Menu> 
       <Menu.ItemsSource> 
        <CompositeCollection> 
         <MenuItem Header="A"></MenuItem> 
         <MenuItem Header="B"></MenuItem> 
         <MenuItem Header="C"></MenuItem> 

         <CollectionContainer x:Name="dynamicMenuItems"> 
         </CollectionContainer> 

         <MenuItem Header="D"></MenuItem> 

        </CompositeCollection> 
       </Menu.ItemsSource> 

      </Menu> 
       </ToolBar> 
     </ToolBarTray> 
    </Grid> 
</Window> 

だと、ここでコードビハインドです:

using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Windows; 

namespace WPFDynamicMenuItems 
{ 
    /// <summary> 
    /// Interaction logic for Window1.xaml 
    /// </summary> 
    public partial class Window1 : Window 
    { 
     private MainMenuViewModel _mainMenuVM = new MainMenuViewModel(); 

     public Window1() 
     { 
      InitializeComponent(); 

      this.dynamicMenuItems.Collection = this._mainMenuVM.CommandList; 
     } 
    } 


    public class MainMenuViewModel : INotifyPropertyChanged 
    { 
     private ObservableCollection<CommandViewModel> m_CommandVMList; 

     public MainMenuViewModel() 
     { 
      m_CommandVMList = new ObservableCollection<CommandViewModel>(); 
      CommandViewModel cmv = new CommandViewModel(); 
      cmv.DisplayName = "Dynamic Menu 1"; 
      m_CommandVMList.Add(cmv); 
      cmv = new CommandViewModel(); 
      cmv.DisplayName = "Dynamic Menu 2"; 
      m_CommandVMList.Add(cmv); 
      cmv = new CommandViewModel(); 
      cmv.DisplayName = "Dynamic Menu 3"; 
      m_CommandVMList.Add(cmv); 

      CommandViewModel nestedCMV = new CommandViewModel(); 
      nestedCMV.DisplayName = "Nested Menu 1"; 
      cmv.CommandList.Add(nestedCMV); 

      nestedCMV = new CommandViewModel(); 
      nestedCMV.DisplayName = "Nested Menu 2"; 
      cmv.CommandList.Add(nestedCMV); 
     } 
     public ObservableCollection<CommandViewModel> CommandList 
     { 
      get { return m_CommandVMList; } 
      set { m_CommandVMList = value; OnPropertyChanged("CommandList"); } 
     } 

     protected void OnPropertyChanged(string propertyName) 
     { 
      // Hook up event... 
     } 

     #region INotifyPropertyChanged Members 

     public event PropertyChangedEventHandler PropertyChanged; 

     #endregion 
    } 

    public class CommandViewModel : INotifyPropertyChanged 
    { 
     private ObservableCollection<CommandViewModel> m_CommandVMList; 

     public CommandViewModel() 
     { 
      this.m_CommandVMList = new ObservableCollection<CommandViewModel>(); 
     } 

     public string DisplayName { get; set; } 

     public ObservableCollection<CommandViewModel> CommandList 
     { 
      get { return m_CommandVMList; } 
      set { m_CommandVMList = value; OnPropertyChanged("CommandList"); } 
     } 

     protected void OnPropertyChanged(string propertyName) 
     { 
      // Hook up event... 
     } 

     #region INotifyPropertyChanged Members 

     public event PropertyChangedEventHandler PropertyChanged; 

     #endregion 
    } 
} 
+0

うん理由は主にXAMLはかなりあるということで、私はそれを行うことがしたいが、残念ながら、私はできません私の手からアプリケーション開発者が作成/管理しています。私のモデルを使用して、メニューからコードを引き出す必要があるものをメニューに追加するだけです。 –

+0

それは残念です。 CompositeCollection(http://msdn.microsoft.com/en-us/library/system.windows.data.compositecollection.aspx)を利用するためにXAMLを変更する方法はありますか? これは理想的ではありませんが、動的なアイテムを注入することができます。私が見ることができる唯一の欠点は、結果として得られるMenuItemがどのようにソートされるかです。しかし、前に来たMenuItemsを1つのコレクションとして扱い、後で別のものとして扱うMenuItemsを扱うことができます。 – micahtan

+0

Micahtan - 多分あなたのポストまでCompositeCollectionクラスを使って解決策を書くべきでしょうか?それは本当に正しい方法だと思われるからです。ありがとう、私は同じ問題を抱えてきた。 – arconaut

関連する問題