WPF MVVM 案例:ItemsControl 包含用于更新 ViewModel 中的属性的超链接和命令
本文关键字:属性 超链接 命令 ViewModel 更新 案例 MVVM ItemsControl 用于 包含 WPF | 更新日期: 2023-09-27 18:33:36
我有一个案例,有点复杂,但我尝试说明并进行一些修改以简单的方式显示这一点。
假设我有一个 Window1 作为视图,一个 Window1ViewModel 作为其视图模型。我还有一个SubMenuViewModel类来表示窗口中的子菜单。我想在 Window1 中创建一个包含许多子菜单的项目控件。每次用户单击其中一个子菜单时,属性 CurrentSubMenu 都会更新为相应的子菜单。这就是问题所在,我无法在 Window1ViewModel 中更新当前子菜单。
.
子菜单视图模型 :
public class SubMenuViewModel : INPC
{
private string _submenuname;
public string SubMenuName
{
get
{ return _submenuname; }
set
{
_submenuname = value;
RaisePropertyChanged("SubMenuName");
}
}
private string _displayname;
public string DisplayName
{
get
{ return _displayname; }
set
{
_displayname = value;
RaisePropertyChanged("DisplayName");
}
}
// Command for Hyperlink in ItemsControl //
private RelayCommand _commandSubmenu;
public RelayCommand CommandSubMenu
{
get
{ return _commandSubmenu; }
set
{
_commandSubmenu = value;
RaisePropertyChanged("CommandSubMenu");
}
}
// end of command
public SubMenuViewModel(string Submenu_name, string Display_name)
{
_submenuname = Submenu_name;
_displayname = Display_name;
}
}
窗口 1 :
<Window x:Class="SomeProject.Window1"
xmlns:vm="clr-namespace:SomeProject.ViewModel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="This is Some Project" WindowState="Maximized">
<Window.DataContext>
<vm:Window1ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Name="SubMenuPanelBorder"
Grid.Column="0"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="15 0 15 0"
BorderThickness="2"
BorderBrush="Black"
Padding="10 10">
<HeaderedContentControl Content="{Binding Path=SubMenus}" Header="Panel Submenu :">
<HeaderedContentControl.ContentTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}" Foreground="White" Background="Transparent" Margin="0 15">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding Path=CommandSubMenu}">
<TextBlock Margin="0 5" Text="{Binding Path=DisplayName}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</HeaderedContentControl.ContentTemplate>
</HeaderedContentControl>
</Border>
.......
.......
</Grid>
</Window>
窗口1视图模型 :
class Window1ViewModel : INPC
{
private List<SubMenuViewModel> _submenus;
public List<SubMenuViewModel> SubMenus
{
get
{ return _submenus; }
set
{
_submenus = value;
RaisePropertyChanged("SubMenus");
}
}
private SubMenuViewModel _currentSubMenu;
public SubMenuViewModel CurrentSubMenu
{
get
{ return _currentSubMenu; }
set
{
_currentSubMenu = value;
RaisePropertyChanged("CurrentSubMenu");
}
}
public Window1ViewModel()
{
SubMenus = MenuDefinition.GetSubMenus();
/***
Set the SubMenus's command to update the CurrentSubMenu,
and == HERE IS WHERE I GOT LOST ==.
The CurrentSubMenu is always point to the last submenu in the loop when clicked.
By the way, the reason I use loop is because the submenu in the Menu definition sometimes changed and there are many number of submenus there, and there are some other reasons (I use Menu-submenu mechanism, but it's not essential to show it here because it's not the main point I want to show). So, finally I use loop instead of specifying each command one by one.
***/
foreach(SubMenuViewModel submenu in SubMenus){
submenu.CommandSubMenu=new RelayCommand(()=>clickedSubMenu(submenu.SubMenuName));
}
}
public void clickedSubMenu(string submenu_name)
{
CurrentSubMenu = SubMenus.Find(sub => sub.SubMenuName == submenu_name);
}
}
菜单定义 :
public static List<SubMenuViewModel> GetSubMenus()
{
return new List<SubMenuViewModel>
{
new SubMenuViewModel("overview_product", "Overview Products"),
new SubMenuViewModel("search_product","Search Products"),
new SubMenuViewModel("update_product","Update Data Products"),
new SubMenuViewModel("order_product","Order Products"),
new SubMenuViewModel("supplier_product","Products Supplier"),
.................
.................
.................
};
}
}
我相信
你的绑定属性应该这样写。
private string _Terminal;
public string Terminal
{
get
{
return _Terminal;
}
set
{
_Terminal = value;
OnPropertyChanged("Terminal");
}
}
确保视图模型实现 INotifyPropertyChanged
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
propertyChanged(this, e);
}
}
解决方案很简单,但不知何故,我需要一点时间才能找到这个。在将它传递给 RelayCommand 的委托操作之前,我必须添加一个临时变量。这样,RelayCommand 的委托"操作执行"将采用正确的参数并指向正确的子菜单。当然,并不总是最后一个子菜单。
返回窗口1视图模型:
因此,而不是直接使用子菜单。子菜单名称属性,如下所示
// This way, when clicked, the command will always point to the last submenu from the loop
public Window1ViewModel()
{
SubMenus = MenuDefinition.GetSubMenus();
foreach(SubMenuViewModel submenu in SubMenus){
submenu.CommandSubMenu=new RelayCommand(()=>clickedSubMenu(submenu.SubMenuName));
}
}
我们必须添加一些这样的临时变量。
// This way, when clicked, the command will point to the correct submenu
public Window1ViewModel()
{
SubMenus = MenuDefinition.GetSubMenus();
foreach(SubMenuViewModel submenu in SubMenus){
string Temp=submenu.SubMenuName
submenu.CommandSubMenu=new RelayCommand(()=>clickedSubMenu(Temp));
}
}
并且输出会有所不同。