2009-09-08 103 views
14

我是WPF和MVVM的新手。我正在與一個LoB應用程序團隊合作。我們希望有一個動態的Menu控件,該控件根據登錄的用戶配置文件創建菜單。在以前的開發場景(即ASP.NET)中,我們使用它來遍歷描述集合的數據並動態生成MenuItem。在MVVM中,我會如何做到這一點?我可以從描述菜單元素的ViewModel中分離出XAML視圖嗎?MVVM動態菜單用戶界面與ViewModel綁定

解決方案:

隨着從評論員我們能夠與從視圖模型的數據動態地綁定Menu輸入。這article也很有幫助。

XAML:

<HierarchicalDataTemplate DataType="{x:Type self:Menu}" ItemsSource="{Binding Path=Children, UpdateSourceTrigger=PropertyChanged}"> 
    <ContentPresenter Content="{Binding Path=MenuText}" RecognizesAccessKey="True"/> 
</HierarchicalDataTemplate> 

[...] 

<Menu Height="21" Margin="0" Name="mainMenu" VerticalAlignment="Top" HorizontalAlignment="Stretch" 
     ItemsSource="{Binding Path=MenuItems, UpdateSourceTrigger=PropertyChanged}" ItemContainerStyle="{StaticResource TopMenuItems}"> 
    <Menu.Background> 
     <ImageBrush ImageSource="/Wpf.Modules;component/Images/MenuBg.jpg" /> 
    </Menu.Background> 
</Menu> 

Menu數據類:

​​
+0

在Google上花費了一些時間之後,我發現HierarchicalDataTemplate可以在動態菜單創建中有所幫助,但是可以將「關注」與MVVM模式分開。我還沒有任何代碼示例:( – Raj 2009-09-08 07:01:00

回答

14

嘗試是這樣的:

public class MenuItemViewModel 
{ 
    public MenuItemViewModel() 
    { 
     this.MenuItems = new List<MenuItemViewModel>(); 
    } 

    public string Text { get; set; } 

    public IList<MenuItemViewModel> MenuItems { get; private set; } 
} 

假設你的DataContext有一個名爲的MenuItems屬性,它是名單MenuItemViewModel。這樣的事情應該工作,然後:

<Window x:Class="WpfApplication1.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:self="clr-namespace:WpfApplication1" 
     Title="Window1" Height="300" Width="300"> 
    <Window.Resources> 
     <HierarchicalDataTemplate DataType="{x:Type self:MenuItemViewModel}" 
            ItemsSource="{Binding Path=MenuItems}"> 
      <ContentPresenter Content="{Binding Path=Text}" /> 
     </HierarchicalDataTemplate> 
    </Window.Resources> 
    <DockPanel> 
     <Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MenuItems}" /> 
     <Grid /> 
    </DockPanel> 
</Window> 
+0

嗨,我可以將我的類綁定到菜單,但我怎麼能分層結構他們?每個菜單項都有一個parentId,那些null parentId是根菜單元素,我想在HierarchicalDataTemplate上是否還有其他資源? 謝謝 – Raj 2009-09-16 19:40:38

+0

而且我怎樣才能將樣式從XAML分配給MenuItem,因爲它們沒有在XAML中明確定義? – Raj 2009-09-17 03:44:50

+0

樣式到MenuItem可以使用ItemContainerStyle屬性 – Raj 2009-09-17 03:56:09

4

我知道這是一箇舊的帖子,但我需要這加上如何綁定命令。

至於古格對如何綁定命令的問題: VMMenuItems在我看來是模型類類型的屬性

ObservableCollection<Menu> 

和菜單是上面定義的類。 MenuItem的Command屬性綁定到Menu類的Command屬性。 在我看來模型類

Menu.Command = _fou 

其中

private ICommand _fou; 

的XAML

<ListView.ContextMenu> 
    <ContextMenu ItemsSource="{Binding Path=VMMenuItems}"> 
      <ContextMenu.ItemContainerStyle> 
       <Style TargetType="{x:Type MenuItem}">          
         <Setter Property="Command" Value="{Binding Command}"/> 
        </Style> 
      </ContextMenu.ItemContainerStyle> 
     </ContextMenu>      
</ListView.ContextMenu> 
13

這應該讓你你要去哪裏

<UserControl x:Class="WindowsUI.Views.Default.MenuView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:ViewModels="clr-namespace:WindowsUI.ViewModels" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300"> 
<UserControl.Resources> 
    <Style TargetType="{x:Type MenuItem}"> 
     <Setter Property="Header" Value="{Binding Path=DisplayName}"/> 
     <Setter Property="Command" Value="{Binding Path=Command}"/> 
    </Style> 
    <HierarchicalDataTemplate 
     DataType="{x:Type ViewModels:MenuItemViewModel}" 
     ItemsSource="{Binding Path=Items}"> 
    </HierarchicalDataTemplate> 
</UserControl.Resources> 
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=Items}"/> 

請注意,在我的示例中,我的菜單Item有一個ICommand屬性,稱爲Command。

1

如果你想知道如何做分隔符,它真的很容易。

下面的代碼是我的ViewModel的一部分。由於XAML使用反射,我需要做的就是返回'object',其可以是MenuItemViewModelSeparator或(如果出於某種奇怪的原因,我需要)實際MenuItem

我使用yield來動態生成項目,因爲它似乎對我來說讀起來更好。即使我正在使用yield - 如果項目更改,我仍然需要像往常一樣爲"ContextMenu"籌集一個PropertyChanged事件,但我不會不必要地生成該列表直到需要爲止。

public IEnumerable<object> ContextMenu 
    { 
     get 
     { 
      // ToArray() needed or else they get garbage collected 
      return GetContextMenu().ToArray(); 
     } 
    } 

    public IEnumerable<object> GetContextMenu() 
    { 
     yield return new MenuItemViewModel() 
     { 
      Text = "Clear all flags", 
     }; 

     // adds a normal 'Separator' menuitem 
     yield return new Separator(); 

     yield return new MenuItemViewModel() 
     { 
      Text = "High Priority" 
     }; 

     yield return new MenuItemViewModel() 
     { 
      Text = "Medium Priority" 
     }; 

     yield return new MenuItemViewModel() 
     { 
      Text = "Low Priority" 
     }; 

     yield break; 
    } 
5

This solution在代碼後面不需要任何代碼,這使得它更簡單的解決方案。

 <Menu> 
      <MenuItem ItemsSource="{Binding Path=ChildMenuItems}" Header="{Binding Path=Header}"> 
       <MenuItem.Resources> 
        <HierarchicalDataTemplate DataType="{x:Type vm:MenuItemViewModel}" ItemsSource="{Binding ChildMenuItems}"> 
         <MenuItem Header="{Binding Path=Header}" Command="{Binding Path=Command}"/> 
        </HierarchicalDataTemplate> 
        <DataTemplate DataType="{x:Type vm:SeparatorViewModel}"> 
         <Separator> 
          <Separator.Template> 
           <ControlTemplate> 
            <Line X1="0" X2="1" Stroke="Black" StrokeThickness="1" Stretch="Fill"/> 
           </ControlTemplate> 
          </Separator.Template> 
         </Separator> 
        </DataTemplate> 
       </MenuItem.Resources> 
      </MenuItem> 
     </Menu> 

而且菜單項被表示爲:

public class MenuItemViewModel : BaseViewModel 
    { 
     /// <summary> 
     /// Initializes a new instance of the <see cref="MenuItemViewModel"/> class. 
     /// </summary> 
     /// <param name="parentViewModel">The parent view model.</param> 
     public MenuItemViewModel(MenuItemViewModel parentViewModel) 
     { 
      ParentViewModel = parentViewModel; 
      _childMenuItems = new ObservableCollection<MenuItemViewModel>(); 
     } 

     private ObservableCollection<MenuItemViewModel> _childMenuItems; 
     /// <summary> 
     /// Gets the child menu items. 
     /// </summary> 
     /// <value>The child menu items.</value> 
     public ObservableCollection<MenuItemViewModel> ChildMenuItems 
     { 
      get 
      { 
       return _childMenuItems; 
      } 
     } 

     private string _header; 
     /// <summary> 
     /// Gets or sets the header. 
     /// </summary> 
     /// <value>The header.</value> 
     public string Header 
     { 
      get 
      { 
       return _header; 
      } 
      set 
      { 
       _header = value; NotifyOnPropertyChanged("Header"); 
      } 
     } 

     /// <summary> 
     /// Gets or sets the parent view model. 
     /// </summary> 
     /// <value>The parent view model.</value> 
     public MenuItemViewModel ParentViewModel { get; set; } 

     public virtual void LoadChildMenuItems() 
     { 

     } 
    } 

具體的MenuItems可以直接或者實例化,或者你可以通過繼承使自己的亞型。