我有一个菜单和一些子菜单
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel { Header = "Select a Building",
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel { Header = "Building 4",
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel { Header = "< 500",
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel {Header = "Executives" },
new MenuItemViewModel {Header = "Engineers" },
new MenuItemViewModel {Header = "Sales" },
new MenuItemViewModel {Header = "Marketing"},
new MenuItemViewModel {Header = "Support"}
}
},
new MenuItemViewModel { Header = "500 - 999",
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel {Header = "Executives" },
new MenuItemViewModel {Header = "Engineers" },
new MenuItemViewModel {Header = "Sales" },
new MenuItemViewModel {Header = "Marketing"},
new MenuItemViewModel {Header = "Support"}
}
}
}
}
}
我正在尝试捕获用户所做的每个选择的值并将它们显示在列表框中。例如,当选择了这些值时,用户选择“ Building 4”,然后选择“ 500-999”,然后选择“ support”,然后填充列表框。我在MenuItemViewModel中有一个称为Execute()的函数,它将获取最后选择的值的标题,即“ support”,但我无法弄清楚如何将该值添加到列表框中。
这是ViewModel
public class MenuItemViewModel
{
private readonly ICommand _command;
string Value;
public ObservableCollection<Cafe> Cafes
{
get;
set;
}
public MenuItemViewModel()
{
_command = new CommandViewModel(Execute);
}
public string Header { get; set; }
public ObservableCollection<MenuItemViewModel> MenuItems { get; set; }
public ICommand Command
{
get
{
return _command;
}
}
public void Execute()
{
MessageBox.Show("Clicked at " + Header);
}
}
最后是MenuItem和ListBox的xaml:
<Menu x:Name="buildingMenu" Margin="0,15,0,0" HorizontalAlignment="Left" Height="20" Width="200" ItemsSource="{Binding MenuItems}" >
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<TextBlock Text="{Binding Header}"/>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
<ListBox Name="selectionListBox" HorizontalAlignment="Right" Height="179" Margin="0,177,55,0" VerticalAlignment="Top" Width="500" />
我试图将Header添加到ViewModel的列表中,但是我什么都做不了。是否可以在此处使用类似于组合框的SelectedValue的东西?感谢您的帮助
答案 0 :(得分:0)
这实际上比您预期的要难得多,因为您使用菜单的方式并非真正旨在使用。
首先,您需要绑定视图模型中的项目列表,例如:
public ObservableCollection<string> ListItems { get; set; }
以及ListBox中的相应绑定:
<ListBox ItemsSource="{Binding ListItems}" Name="selectionListBox" />
接下来,您需要在单击项目时在其中填充项目。这对您目前的处事方式比较棘手,因为ListItems
必须位于主视图模型中,而命令处理程序位于MenuItemViewModel中。这意味着您要么需要为MenuItemViewModel添加一种调用其父项的方法,要么最好是将命令处理程序移至主视图模型(以便现在可以共享),并让MenuItems传递其数据上下文对象。我使用MVVM Lite,在该框架中,您将执行以下操作:
private ICommand _Command;
public ICommand Command
{
get { return (_Command = (_Command ?? new RelayCommand<MenuItemViewModel>(Execute))); }
}
public void Execute(MenuItemViewModel menuItem)
{
// ... do something here...
}
您似乎并没有使用MVVM Lite,但是无论您使用什么框架,它都支持将参数传递到您的execute函数中,因此我将留给您查找。无论哪种方式,现在都需要修改MenuItem Command绑定以指向此公共处理程序:
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding ElementName=buildingMenu, Path=DataContext.Command}" />
</Style>
您的另一个要求是,在选择每个子菜单项时填充列表,这是令人讨厌的地方。子菜单在关闭时不会触发命令,它们会触发SubmenuOpened和SubmenuClosed事件。事件无法绑定到视图模型中的命令处理程序,因此您必须使用代码隐藏处理程序。如果要在XAML中显式声明MenuItems,则可以直接在其中设置它们,但是Menu绑定到一个集合,因此MenuItems由框架填充,您必须通过将EventSetter添加到您的菜单中来设置处理程序。风格:
<Style TargetType="{x:Type MenuItem}">
<EventSetter Event="SubmenuOpened" Handler="OnSubMenuOpened" />
<Setter Property="Command" Value="{Binding ElementName=buildingMenu, Path=DataContext.Command}" />
</Style>
这有效,因为它在您的MainWindow后台代码中调用了指定的处理函数:
private void OnSubmenuOpened(object sender, RoutedEventArgs e)
{
// uh oh, what now?
}
当然,问题在于事件处理程序存在于视图中,但是在MVVM中,我们需要在视图模型中使用它。如果您很乐意为了使应用程序正常工作而破坏这样的视图,请查看this SO question,以获取一些示例代码来说明如何使其运行。但是,如果您是像我这样的核心MVVM纯粹主义者,则可能需要不涉及代码隐藏的解决方案,在这种情况下,您将再次需要引用您的框架。 MVVM Lite提供了一种行为,可让您将事件转换为命令,通常按以下方式使用它:
<MenuItem>
<i:Interaction.Triggers xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:EventTrigger EventName="OnSubmenuOpened">
<cmd:EventToCommand Command="{Binding SubmenuOpenedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
尽管如此,行为的问题在于您无法将其设置为样式。将样式应用于所有对象,这就是EventSetter可以正常工作的原因。必须为其使用的每个对象创建行为。
因此,最后一个难题是,如果您在MVVM中有一些案例需要在样式中设置行为,那么您将需要使用vspivak在他的文章Using System.Windows.Interactivity Behaviors and Actions in WPF/Silverlight styles中发布的解决方案。我已经在商业项目中使用过它,这对于将事件处理程序重定向为样式中的命令处理程序这一更大的问题是一个很好的通用解决方案。
希望能回答您的问题。我敢肯定这似乎是令人费解的,但是您要尝试的是一个相当病理的场景,远远超出了通常使用WPF的方式。