一.菜单导航实现
1.首先创建出所有的页面(View)及对应的页面逻辑处理类(ViewModel)
- IndexView(首页)-----------------IndexViewModel
- ToDoView(待办事项)------------ToDoViewModel
- MemoView(忘备录)--------------MemoViewModel
- SettingsView(设置)--------------SettingsViewModel
注意:创建View时,添加的是用户控件
2. 在 MainView.xaml 添加内容展示区域
通俗点理解就是,其他页面的内容需要有控件去展现出来给用户看。然后就用到一个ContentControl 控件来展现内容,并且也需要用到 Prism 框架里面的区域 (并且通过定义一个区域名称来定位到展现的位置)。我是这样理解的。首先这不是教程,是我学习的记录,如果错了,就错了。
- 例如,在 ContentControl 中定义一个区域名称,并且使用 prism 注册这个区域
<ContentControl prism:RegionManager.RegionName=""/>
建议:通过一个扩展类来管理定义的一些属性名称
例如:当前定义的区域名称,通过建立一个扩展类来进行管理
public static class PrismManager
{
public static readonly string MainViewRegionName = "MainViewReion";
}
- 扩展类定义完成后,需要在使用到的 MainView 页面中,添加命名空间进行使用这个静态扩展类
xmlns:ext="clr-namespace:MyToDo.Extensions"
- xmlns: 是引入命名空间固定的写法
- ext 是自定义的名称
- = 号后面是扩展类所在的命名空间
- 最后,在 ContentControl 控件中去引用这个静态类定义的属性
<ContentControl prism:RegionManager.RegionName="{x:Static ext:PrismManager.MainViewRegionName}"/>
x:Static 是引用静态类型的属性的固定前缀的写法
3.进行页面导航注册
- 在App.xaml.cs中,把页面(View)和 业务处理类(ViewModel) 进行依赖关联并注册进导航容器中
/// <summary>
/// 依懒注入的方法
/// </summary>
/// <param name="containerRegistry"></param>
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<IndexView, IndexViewModel>();
containerRegistry.RegisterForNavigation<MemoView, MemoViewModel>();
containerRegistry.RegisterForNavigation<ToDoView, ToDoViewModel>();
containerRegistry.RegisterForNavigation<SettingsView, SettingsViewModel>();
}
5. 接着在添加导航命令
- 在MainViewModel.cs 中,添加导航命令。作用是用来驱动整个页面的导航
- 接着,还需要添加 IRegionManager,通过在Prism 提供的IRegionManager.Regions 来找到应用程序所注册的导航区域名称(就是内容展现区域定义的名称)
- 最后,通过.出来RequestNavigate 属性,取菜单传过来的命名空间做为导航的目标页面。
public class MainViewModel: BindableBase
{
public MainViewModel(IRegionManager regionManager)
{
NavigateCommand = new DelegateCommand<MenuBar>(Navigate);
this.regionManager = regionManager;
}
/// <summary>
/// 导航方法
/// </summary>
/// <param name="bar">菜单</param>
private void Navigate(MenuBar bar)
{
//命名空间为空,不进行导航
if (bar == null || string.IsNullOrEmpty(bar.NameSpace)) return;
regionManager.Regions[PrismManager.MainViewRegionName].RequestNavigate(bar.NameSpace);
}
/// <summary>
/// 导航命令
/// </summary>
public DelegateCommand<MenuBar> NavigateCommand { get; private set; }
}
6.实现上一步,下一步导航功能
通过添加导航日志 IRegionNavigationJournal 来实现,上一步,下一步的导航功能
- 修改导航方法,添加导航成功回调函数
/// <summary>
/// 导航方法
/// </summary>
/// <param name="bar">菜单</param>
private void Navigate(MenuBar bar)
{
//命名空间为空,不进行导航
if (bar == null || string.IsNullOrEmpty(bar.NameSpace)) return;
regionManager.Regions[PrismManager.MainViewRegionName].RequestNavigate(bar.NameSpace, back =>
{
});
}
- 添加一个区域导航日志 IRegionNavigationJournal,用来记录导航的结果 ,并且在回调函数中实例化导航日志
public class MainViewModel: BindableBase
{
public MainViewModel(IRegionManager regionManager)
{
NavigateCommand = new DelegateCommand<MenuBar>(Navigate);
this.regionManager = regionManager;
}
/// <summary>
/// 导航方法
/// </summary>
/// <param name="bar">菜单</param>
private void Navigate(MenuBar bar)
{
//命名空间为空,不进行导航
if (bar == null || string.IsNullOrEmpty(bar.NameSpace)) return;
regionManager.Regions[PrismManager.MainViewRegionName].RequestNavigate(bar.NameSpace, back =>
{
journal=back.Context.NavigationService.Journal;
});
}
/// <summary>
/// 导航命令
/// </summary>
public DelegateCommand<MenuBar> NavigateCommand { get; private set; }
private readonly IRegionManager regionManager;
/// <summary>
/// 导航日志
/// </summary>
private IRegionNavigationJournal journal;
}
- 接着,再添加两个命令,绑定前端按钮点击上一步或下一步按钮,并且进行初始化
public class MainViewModel: BindableBase
{
public MainViewModel(IRegionManager regionManager)
{
NavigateCommand = new DelegateCommand<MenuBar>(Navigate);
this.regionManager = regionManager;
GoBackCommand = new DelegateCommand(() =>
{
if(journal!=null && journal.CanGoBack) journal.GoBack();
});
GoForwardCommand = new DelegateCommand(() =>
{
if (journal != null && journal.CanGoForward) journal.GoForward();
});
}
/// <summary>
/// 导航方法
/// </summary>
/// <param name="bar">菜单</param>
private void Navigate(MenuBar bar)
{
//命名空间为空,不进行导航
if (bar == null || string.IsNullOrEmpty(bar.NameSpace)) return;
regionManager.Regions[PrismManager.MainViewRegionName].RequestNavigate(bar.NameSpace, back =>
{
journal=back.Context.NavigationService.Journal;
});
}
/// <summary>
/// 导航命令
/// </summary>
public DelegateCommand<MenuBar> NavigateCommand { get; private set; }
/// <summary>
/// 下一步
/// </summary>
public DelegateCommand GoBackCommand { get; private set; }
/// <summary>
/// 上一步
/// </summary>
public DelegateCommand GoForwardCommand { get; private set; }
private readonly IRegionManager regionManager;
/// <summary>
/// 导航日志
/// </summary>
private IRegionNavigationJournal journal;
}
- 最后,在MainView页面的上一步和下一步按钮绑定ViewMode 写好的命令
7.在ListBox中,添加事件行为,作用是,当用户选中子项内容的时候,触发导航的行为。
- 需要添加行为的命名空间
引入行为命名空间 xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
- 接着在ListBox 中,添加选中行为事件,来触发导航
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding NavigateCommand}" CommandParameter="{Binding ElementName=menuBar,Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
- Interaction.Triggers 行为触发器
- EventTrigger 触发的事件
- EventName 触发事件的名称,这个事件命名,一定是ListBox存在的事件名称,不是随便起的名称
- InvokeCommandAction 绑定后台要触发的导航命令
- CommandParameter 绑定当前选中项。例如。这是传过去后台命令的参数
例如,上面是通过选中ListBox Item的子项来触发导航命令
8.优化点击菜单子项的同时关闭左侧菜单
-
只需要绑定ListBox 的SelectionChanged 选择事件,点击的时候让左侧边框收起即可
//菜单选择事件
menuBar.SelectionChanged += (s, e) =>
{
drawerHost.IsLeftDrawerOpen = false;
};
- menuBar 是ListBox 定义的名称
- drawerHost 是左侧弹框定义的名称
二.源码
-
MainView.xaml
<Window x:Class="MyToDo.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ext="clr-namespace:MyToDo.Extensions"
xmlns:local="clr-namespace:MyToDo"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
prism:ViewModelLocator.AutoWireViewModel="True"
WindowStyle="None" WindowStartupLocation="CenterScreen" AllowsTransparency="True"
Style="{StaticResource MaterialDesignWindow}"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
Background="{DynamicResource MaterialDesignPaper}"
TextElement.FontWeight="Medium"
TextElement.FontSize="14"
FontFamily="{materialDesign:MaterialDesignFont}"
mc:Ignorable="d"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
Title="MainWindow" Height="768" Width="1280">
<materialDesign:DialogHost DialogTheme="Inherit"
Identifier="RootDialog"
SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}">
<materialDesign:DrawerHost x:Name="drawerHost" IsLeftDrawerOpen="{Binding ElementName=MenuToggleButton, Path=IsChecked}">
<!--左边菜单-->
<materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel MinWidth="220" >
<!--头像-->
<StackPanel DockPanel.Dock="Top" Margin="0,20">
<Image Source="/Images/user.jpg" Width="50" Height="50">
<Image.Clip>
<EllipseGeometry Center="25,25" RadiusX="25" RadiusY="25" />
</Image.Clip>
</Image>
<TextBlock Text="WPF gg" Margin="0,10" HorizontalAlignment="Center" />
</StackPanel>
<!--列表-->
<ListBox x:Name="menuBar" ItemContainerStyle="{StaticResource MyListBoxItemStyle}" ItemsSource="{Binding MenuBars}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding NavigateCommand}" CommandParameter="{Binding ElementName=menuBar,Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="Transparent">
<materialDesign:PackIcon Kind="{Binding Icon}" Margin="15,0" />
<TextBlock Text="{Binding Title}" Margin="10,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel >
<!--导航条色块-->
<materialDesign:ColorZone Padding="16" x:Name="ColorZone"
materialDesign:ElevationAssist.Elevation="Dp4"
DockPanel.Dock="Top"
Mode="PrimaryMid">
<DockPanel LastChildFill="False">
<!--上左边内容-->
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="MenuToggleButton"
AutomationProperties.Name="HamburgerToggleButton"
IsChecked="False"
Style="{StaticResource MaterialDesignHamburgerToggleButton}" />
<Button Margin="24,0,0,0"
materialDesign:RippleAssist.Feedback="{Binding RelativeSource={RelativeSource Self}, Path=Foreground, Converter={StaticResource BrushRoundConverter}}"
Command="{Binding GoForwardCommand}"
Content="{materialDesign:PackIcon Kind=ArrowLeft,
Size=24}"
Foreground="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"
Style="{StaticResource MaterialDesignToolButton}"
ToolTip="Previous Item" />
<Button Margin="16,0,0,0"
materialDesign:RippleAssist.Feedback="{Binding RelativeSource={RelativeSource Self}, Path=Foreground, Converter={StaticResource BrushRoundConverter}}"
Command="{Binding GoBackCommand}"
Content="{materialDesign:PackIcon Kind=ArrowRight,
Size=24}"
Foreground="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"
Style="{StaticResource MaterialDesignToolButton}"
ToolTip="Next Item" />
<TextBlock Margin="16,0,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.Name="Material Design In XAML Toolkit"
FontSize="22"
Text="笔记本" />
</StackPanel>
<!--上右边图标-->
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
<Image Source="/Images/user.jpg" Width="25" Height="25">
<Image.Clip>
<EllipseGeometry Center="12.5,12.5" RadiusX="12.5" RadiusY="12.5" />
</Image.Clip>
</Image>
<Button x:Name="btnMin" Style="{StaticResource MaterialDesignFlatMidBgButton}">
<materialDesign:PackIcon Kind="MoveResizeVariant" />
</Button>
<Button x:Name="btnMax" Style="{StaticResource MaterialDesignFlatMidBgButton}">
<materialDesign:PackIcon Kind="CardMultipleOutline" />
</Button>
<Button x:Name="btnClose" Style="{StaticResource MaterialDesignFlatMidBgButton}" Cursor="Hand">
<materialDesign:PackIcon Kind="WindowClose" />
</Button>
</StackPanel>
</DockPanel>
</materialDesign:ColorZone>
<!--内容展现区域-->
<ContentControl prism:RegionManager.RegionName="{x:Static ext:PrismManager.MainViewRegionName}"/>
</DockPanel>
</materialDesign:DrawerHost>
</materialDesign:DialogHost>
</Window>
-
MainView.xaml.cs
/// <summary>
/// MainView.xaml 的交互逻辑
/// </summary>
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
//最小化
btnMin.Click += (s, e) =>
{
this.WindowState = WindowState.Minimized;//窗口设置最小
};
//最大化
btnMax.Click += (s, e) =>
{
//判断窗口是否是最小化状态
if (this.WindowState == WindowState.Maximized)
{
this.WindowState = WindowState.Normal; //改成正常状态
}
else
{
this.WindowState = WindowState.Maximized;//最大化
}
};
//关闭
btnClose.Click += (s, e) =>
{
this.Close();
};
//鼠标拖动事件
ColorZone.MouseMove += (s, e) =>
{
//如果鼠标在拖动
if (e.LeftButton == MouseButtonState.Pressed)
{
this.DragMove();//让窗口移动
}
};
//导航栏双击事件
ColorZone.MouseDoubleClick += (s, e) =>
{
//双击时,如果是窗口是正常形态,就变成最大化
if (this.WindowState == WindowState.Normal)
{
this.WindowState = WindowState.Maximized;
}
else
{
this.WindowState = WindowState.Normal;//否则就变成正常的形态
}
};
//菜单选择事件
menuBar.SelectionChanged += (s, e) =>
{
drawerHost.IsLeftDrawerOpen = false;
};
}
}
-
MainViewModel
public class MainViewModel: BindableBase
{
public MainViewModel(IRegionManager regionManager)
{
MenuBars=new ObservableCollection<MenuBar>();
CreateMenuBar();
NavigateCommand = new DelegateCommand<MenuBar>(Navigate);
this.regionManager = regionManager;
GoBackCommand = new DelegateCommand(() =>
{
if(journal!=null && journal.CanGoBack) journal.GoBack();
});
GoForwardCommand = new DelegateCommand(() =>
{
if (journal != null && journal.CanGoForward) journal.GoForward();
});
}
/// <summary>
/// 导航方法
/// </summary>
/// <param name="bar">菜单</param>
private void Navigate(MenuBar bar)
{
//命名空间为空,不进行导航
if (bar == null || string.IsNullOrEmpty(bar.NameSpace)) return;
regionManager.Regions[PrismManager.MainViewRegionName].RequestNavigate(bar.NameSpace, back =>
{
journal=back.Context.NavigationService.Journal;
});
}
/// <summary>
/// 导航命令
/// </summary>
public DelegateCommand<MenuBar> NavigateCommand { get; private set; }
/// <summary>
/// 下一步
/// </summary>
public DelegateCommand GoBackCommand { get; private set; }
/// <summary>
/// 上一步
/// </summary>
public DelegateCommand GoForwardCommand { get; private set; }
private ObservableCollection<MenuBar> menuBars;
private readonly IRegionManager regionManager;
/// <summary>
/// 导航日志
/// </summary>
private IRegionNavigationJournal journal;
public ObservableCollection<MenuBar> MenuBars
{
get { return menuBars; }
set { menuBars = value; RaisePropertyChanged(); }
}
void CreateMenuBar()
{
MenuBars.Add(new MenuBar() { Icon="Home",Title="首页",NameSpace="IndexView"});
MenuBars.Add(new MenuBar() { Icon = "NotebookCheckOutline", Title = "待办事项", NameSpace = "ToDoView" });
MenuBars.Add(new MenuBar() { Icon = "NotebookPlusOutline", Title = "忘备录", NameSpace = "MemoView" });
MenuBars.Add(new MenuBar() { Icon = "Cog", Title = "设置", NameSpace = "SettingsView" });
}
}
- App.xaml.cs
public partial class App : PrismApplication
{
/// <summary>
/// 创建启动页面
/// </summary>
/// <returns></returns>
protected override Window CreateShell()
{
return Container.Resolve<MainView>();
}
/// <summary>
/// 依懒注入的方法
/// </summary>
/// <param name="containerRegistry"></param>
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<IndexView, IndexViewModel>();
containerRegistry.RegisterForNavigation<MemoView, MemoViewModel>();
containerRegistry.RegisterForNavigation<ToDoView, ToDoViewModel>();
containerRegistry.RegisterForNavigation<SettingsView, SettingsViewModel>();
}
}