2009-09-08 105 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可以直接或者实例化,或者你可以通过继承使自己的亚型。