最近通过WPF开发项目,为了对WPF知识点进行总结,所以利用业余时间,开发一个学生信息管理系统【Student Information Management System】。本文主要简述如何通过WPF+Prism+MAH+WebApi进行开发基于三层架构的桌面版应用程序,仅供学习分享使用,如有不足之处,还请指正。
涉及知识点
- WPF:WPF(Windows Presentation Foundation)是(微软推出的)基于Windows的用户界面框架,提供了统一的编程模型,语言和框架,做到了分离界面设计人员与开发人员的工作;WPF提供了全新的多媒体交互用户图形界面。相比于WinForm传统开发,在WPF中,通过核心的MVVM设计思想,实现前后端的分离。
- Prism:Prism是一个用于在 WPF、Xamarin Form、Uno 平台和 WinUI 中构建松散耦合、可维护和可测试的 XAML 应用程序框架。通过Prism,可以简化原生MVVM实现方式,并引入分模块设计思想。在Prism中,每一个功能,都可以设计成一个独立的模块,各个模块之间松耦合,可维护,可测试。框架中包括 MVVM、依赖注入、Command、Message Event、导航、弹窗等功能。在后续程序功能设计中,都会用到。
- MAH:MahApps是一套基于WPF的界面组件,通过该组件,可以使用较小的开发成本实现一个相对很好的界面效果。作为后端开发,最头疼的就是如何设计美化页面,MAH可以让开发人员用最少的时间来开发Metro风格的页面。
- WebApi:一般是指ASP.NET WebApi 用于快速开发基于REST风格的数据接口的框架。
Prism的模块化思想
在应用程序开发中,如果不采用模块化思想,那么各个页面混合在一起,看起杂乱无章,具体如下所示:
当我们引入模块化思想,那么各个模块的界限将变得清晰,如下所示:
在本文示例的学生信息管理系统中,就是采用模块思想,使项目的各个模块即相对完整,又相互独立。如下所示:
在开发中,引入模块化思想,通过Prism进行代码布局,如下所示:
MVVM思想
MVVM是Model-View-ViewModel(模型-视图-视图模型)的缩写形式,它通常被用于WPF或Silverlight开发。MVVM的根本思想就是界面和业务功能进行分离,View的职责就是负责如何显示数据及发送命令,ViewModel的功能就是如何提供数据和执行命令。各司其职,互不影响。我们可以通过下图来直观的理解MVVM模式:
在本示例中,所有开发都将遵循MVVM思想的设计模式进行开发,如下所示:
页面布局
在学生信息管理系统主界面,根据传统的布局方式,主要分为上(Header),中【左(Navigation),右(Main Content)】,下(Footer)四个部分,如下所示:
创建一个模块
一个模块是一个独立的WPF类库,在项目中,一个普通的类实现了IModule接口,就表示一个模块,以学生模块为例,如下所示:
1 using Prism.Ioc; 2 using Prism.Modularity; 3 using SIMS.StudentModule.ViewModels; 4 using SIMS.StudentModule.Views; 5 using System; 6 7 namespace SIMS.StudentModule 8 { 9 public class StudentModule : IModule 10 { 11 public void OnInitialized(IContainerProvider containerProvider) 12 { 13 14 } 15 16 public void RegisterTypes(IContainerRegistry containerRegistry) 17 { 18 containerRegistry.RegisterForNavigation<Student, StudentViewModel>(nameof(Student)); 19 } 20 } 21 }
注意:在模块中,需要实现两个接口方法。在此模块中的RegisterTypes方法中,可以注册导航,窗口等以及初始化工作。
如果不注册为导航,而是需要注册到某一个Region中,则可以在OnInitialized方法中进行,以导航模块为例,如下所示:
1 using Prism.Ioc; 2 using Prism.Modularity; 3 using Prism.Regions; 4 using SIMS.NavigationModule.Views; 5 using System; 6 7 namespace SIMS.NavigationModule 8 { 9 public class NavigationModule : IModule 10 { 11 public void OnInitialized(IContainerProvider containerProvider) 12 { 13 var regionManager = containerProvider.Resolve<IRegionManager>(); 14 regionManager.RegisterViewWithRegion("NavRegion",typeof(Navigation)); 15 } 16 17 public void RegisterTypes(IContainerRegistry containerRegistry) 18 { 19 } 20 } 21 }
View和ViewModel自动适配
View和ViewMode在注册导航时,可以手动匹配,也可以自动匹配【需要以固定的方式命名才可以自动适配】。自动适配,需要是在UserControl中,增加一句prism:ViewModelLocator.AutoWireViewModel="True"即可,以标题头为例,如下所示:
1 <UserControl x:Class="SIMS.Views.Header" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:SIMS.Views" 7 mc:Ignorable="d" 8 xmlns:prism="http://prismlibrary.com/" 9 prism:ViewModelLocator.AutoWireViewModel="True" 10 xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls" 11 d:DesignHeight="100" d:DesignWidth="800"> 12 <UserControl.Resources> 13 <ResourceDictionary> 14 <ResourceDictionary.MergedDictionaries> 15 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" /> 16 </ResourceDictionary.MergedDictionaries> 17 </ResourceDictionary> 18 </UserControl.Resources> 19 20 <Grid Background="{DynamicResource MahApps.Brushes.Accent}"> 21 <Grid.RowDefinitions> 22 <RowDefinition Height="*"></RowDefinition> 23 <RowDefinition Height="Auto"></RowDefinition> 24 </Grid.RowDefinitions> 25 <TextBlock Grid.Row="0" Text="学生信息管理系统" Foreground="White" FontSize="32" FontWeight="Bold" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="20,5"></TextBlock> 26 <StackPanel Grid.Row="1" HorizontalAlignment="Right" Orientation="Horizontal"> 27 <TextBlock Text="Hello" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="3"></TextBlock> 28 <TextBlock Text="Admin" Foreground="White" Margin="3" FontWeight="Bold"></TextBlock> 29 <TextBlock Text="|" Foreground="White" Margin="3"></TextBlock> 30 <TextBlock Text="Logout" Foreground="White" Margin="3" FontWeight="Bold"></TextBlock> 31 </StackPanel> 32 </Grid> 33 34 </UserControl>
弹出模态窗口
在Prism中,模块中的视图都是以UserControl的形式存在,那么如果需要弹出窗体页面,就需要在ViewModel中,实现IDialogAware接口,以Login登录窗口为例,如下所示:
1 using Prism.Regions; 2 using Prism.Services.Dialogs; 3 using SIMS.Views; 4 using System; 5 using System.Collections.Generic; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows; 10 11 namespace SIMS.ViewModels 12 { 13 public class LoginViewModel : BindableBase, IDialogAware 14 { 15 private IRegionManager _regionManager; 16 private IContainerExtension _container; 17 18 private string userName; 19 20 public string UserName 21 { 22 get { return userName; } 23 set {SetProperty(ref userName , value); } 24 } 25 26 private string password; 27 28 public string Password 29 { 30 get { return password; } 31 set { SetProperty(ref password , value); } 32 } 33 34 35 public LoginViewModel(IContainerExtension container,IRegionManager regionManager) 36 { 37 this._container = container; 38 this._regionManager = regionManager; 39 } 40 41 private void InitInfo() { 42 var footer = _container.Resolve<Footer>(); 43 IRegion footerRegion = _regionManager.Regions["LoginFooterRegion"]; 44 footerRegion.Add(footer); 45 } 46 47 #region 命令 48 49 private DelegateCommand loadedCommand; 50 51 public DelegateCommand LoadedCommand 52 { 53 get 54 { 55 if (loadedCommand == null) 56 { 57 loadedCommand = new DelegateCommand(Loaded); 58 } 59 return loadedCommand; 60 } 61 } 62 63 private void Loaded() 64 { 65 //InitInfo(); 66 } 67 68 private DelegateCommand loginCommand; 69 70 public DelegateCommand LoginCommand 71 { 72 get 73 { 74 if (loginCommand == null) 75 { 76 loginCommand = new DelegateCommand(Login); 77 } 78 return loginCommand; 79 } 80 } 81 82 private void Login() { 83 if (string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Password)) { 84 MessageBox.Show("用户名或密码为空,请确认"); 85 return; 86 } 87 if (UserName == "admin" && Password == "abc123") 88 { 89 CloseWindow(); 90 } 91 else { 92 MessageBox.Show("用户名密码不正确,请确认"); 93 return; 94 } 95 } 96 97 private DelegateCommand cancelCommand; 98 99 public DelegateCommand CancelCommand 100 { 101 get 102 { 103 if (cancelCommand == null) 104 { 105 cancelCommand = new DelegateCommand(Cancel); 106 } 107 return cancelCommand; 108 } 109 } 110 111 private void Cancel() { 112 RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel)); 113 } 114 115 #endregion 116 117 #region DialogAware接口 118 119 public string Title => "SIMS-Login"; 120 121 public event Action<IDialogResult> RequestClose; 122 123 /// <summary> 124 /// 成功时关闭窗口 125 /// </summary> 126 public void CloseWindow() { 127 RequestClose?.Invoke(new DialogResult(ButtonResult.OK)); 128 } 129 130 public bool CanCloseDialog() 131 { 132 return true; 133 } 134 135 public void OnDialogClosed() 136 { 137 //当关闭时 138 RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel)); 139 } 140 141 public void OnDialogOpened(IDialogParameters parameters) 142 { 143 //传递解析参数 144 } 145 146 #endregion 147 } 148 }
实现了IDialogAware接口,表示以窗口的形态出现,在需要弹出窗口的地方进行调用即可。如下所示:
1 public MainWindowViewModel(IContainerExtension container, IRegionManager regionManager, IEventAggregator eventAggregator,IDialogService dialogService) { 2 this._container = container; 3 this._regionManager = regionManager; 4 this.eventAggregator = eventAggregator; 5 this._dialogService = dialogService; 6 //弹出登录窗口 7 this._dialogService.ShowDialog("Login", null, LoginCallback, "MetroDialogWindow"); 8 this.eventAggregator.GetEvent<NavEvent>().Subscribe(Navigation); 9 }
注意:MetroDialogWindow是自定义个一个Metro风格的窗口,如果为空,则采用默认窗口风格。
模块间交互
按照模块化设计思想,虽然各个模块之间相互独立,但是难免为遇到模块之间进行交互的情况,所以Prism提供了事件聚合器,通过命令的发布和订阅来实现模块间的数据交互。以导航模块为例,当点击某一个导航时,发布一个命令,在主窗口订阅此事件,当收到事件时,将此导航对应的页面渲染到主页面区域中。步骤如下:
1. 定义一个事件
1 using Prism.Events; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace SIMS.Utils.Events 9 { 10 /// <summary> 11 /// 导航事件 12 /// </summary> 13 public class NavEvent : PubSubEvent<string> 14 { 15 } 16 }
2. 发布事件
用户点击导航菜单时,触发NavCommand,然后发布命令。
1 private DelegateCommand<object> navCommand; 2 3 public DelegateCommand<object> NavCommand 4 { 5 get 6 { 7 if (navCommand == null) 8 { 9 10 navCommand = new DelegateCommand<object>(Navigation); 11 } 12 return navCommand; 13 } 14 } 15 16 private void Navigation(object obj) { 17 var menuItem = (HamburgerMenuItem)obj; 18 if (menuItem != null) { 19 var tag = menuItem.Tag; 20 if (tag!=null) { 21 this.eventAggregator.GetEvent<NavEvent>().Publish(tag.ToString()); 22 } 23 } 24 }
3. 订阅命令
在主窗口,订阅命令,当收到命令时,再初始化模块信息,如下所示:
1 namespace SIMS.ViewModels 2 { 3 public class MainWindowViewModel:BindableBase 4 { 5 6 private IEventAggregator eventAggregator; 7 private IContainerExtension _container; 8 private IRegionManager _regionManager; 9 private IDialogService _dialogService; 10 public MainWindowViewModel(IContainerExtension container, IRegionManager regionManager, IEventAggregator eventAggregator,IDialogService dialogService) { 11 this._container = container; 12 this._regionManager = regionManager; 13 this.eventAggregator = eventAggregator; 14 this._dialogService = dialogService; 15 //弹出登录窗口 16 this._dialogService.ShowDialog("Login", null, LoginCallback, "MetroDialogWindow"); 17 this.eventAggregator.GetEvent<NavEvent>().Subscribe(Navigation); 18 } 19 20 private void LoginCallback(IDialogResult dialogResult) { 21 if (dialogResult.Result != ButtonResult.OK) { 22 Application.Current.Shutdown(); 23 } 24 } 25 26 #region 事件和命令 27 28 private DelegateCommand loadedCommand; 29 30 public DelegateCommand LoadedCommand 31 { 32 get { 33 if (loadedCommand == null) { 34 loadedCommand = new DelegateCommand(Loaded); 35 } 36 return loadedCommand; } 37 } 38 39 private void Loaded() { 40 InitInfo(); 41 } 42 43 44 45 46 private void InitInfo() { 47 var header = _container.Resolve<Header>(); 48 IRegion headerRegion = _regionManager.Regions["HeaderRegion"]; 49 headerRegion.Add(header); 50 // 51 var footer = _container.Resolve<Footer>(); 52 IRegion footerRegion = _regionManager.Regions["FooterRegion"]; 53 footerRegion.Add(footer); 54 55 var welcome = _container.Resolve<Welcome>(); 56 IRegion welcomeRegion = _regionManager.Regions["ContentRegion"]; 57 welcomeRegion.Add(welcome); 58 } 59 60 private void Navigation(string source) { 61 _regionManager.RequestNavigate("ContentRegion", source); 62 //MessageBox.Show(source); 63 } 64 65 #endregion 66 } 67 }
注意:一般情况下,只有在不同模块时,才使用事件聚合器进行事件的订阅和发布。如果是同一模块,则没有必要。
核心代码
模块的配置
各个模块之间相互独立,所以在主模块中进行加载时,需要先进行配置。模块加载的方式有很多种,本例采用App.config配置方式,如下所示:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 4 <section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf" /> 5 </configSections> 6 <startup> 7 </startup> 8 <modules> 9 <module assemblyFile="SIMS.NavigationModule.dll" moduleType="SIMS.NavigationModule.NavigationModule, SIMS.NavigationModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="NavigationModule" startupLoaded="True" /> 10 <module assemblyFile="SIMS.ScoreModule.dll" moduleType="SIMS.ScoreModule.ScoreModule, SIMS.ScoreModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ScoreModule" startupLoaded="True" /> 11 <module assemblyFile="SIMS.StudentModule.dll" moduleType="SIMS.StudentModule.StudentModule, SIMS.StudentModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="StudentModule" startupLoaded="True" /> 12 <module assemblyFile="SIMS.ClassesModule.dll" moduleType="SIMS.ClassesModule.ClassesModule, SIMS.ClassesModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ClassesModule" startupLoaded="True" /> 13 <module assemblyFile="SIMS.CourseModule.dll" moduleType="SIMS.CourseModule.CourseModule, SIMS.CourseModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="CourseModule" startupLoaded="True" /> 14 <module assemblyFile="SIMS.SysManagementModule.dll" moduleType="SIMS.SysManagementModule.SysManagementModule, SIMS.SysManagementModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="SysManagementModule" startupLoaded="True" /> 15 </modules> 16 </configuration>
启动入口App.xaml,可以用来配置资源字典等初始化操作,如下所示:
1 <prism:PrismApplication x:Class="SIMS.App" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:local="clr-namespace:SIMS" 5 xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" 6 xmlns:prism="http://prismlibrary.com/" 7 > 8 <Application.Resources> 9 <ResourceDictionary> 10 <ResourceDictionary.MergedDictionaries> 11 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" /> 12 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" /> 13 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" /> 14 <ResourceDictionary Source="/Icons/Icons.xaml"></ResourceDictionary> 15 </ResourceDictionary.MergedDictionaries> 16 </ResourceDictionary> 17 </Application.Resources> 18 </prism:PrismApplication>
启动入口App.xaml.cs,应用程序需要继承PrismApplication基类,才是一个Prism应用程序。在此文件中初始化App.config中配置的模块。如下所示:
1 namespace SIMS 2 { 3 /// <summary> 4 /// Interaction logic for App.xaml 5 /// </summary> 6 public partial class App : PrismApplication 7 { 8 protected override Window CreateShell() 9 { 10 return Container.Resolve<MainWindow>(); 11 } 12 13 protected override void RegisterTypes(IContainerRegistry containerRegistry) 14 { 15 containerRegistry.RegisterDialog<Login, LoginViewModel>(nameof(Login)); 16 containerRegistry.Register<IDialogWindow, MetroDialogWindow>("MetroDialogWindow"); 17 } 18 19 protected override IModuleCatalog CreateModuleCatalog() 20 { 21 return new ConfigurationModuleCatalog(); 22 } 23 } 24 }
主窗口MainWindow.xaml页面布局,如下所示:
1 <mahApps:MetroWindow x:Class="SIMS.Views.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:SIMS" 7 mc:Ignorable="d" 8 xmlns:prism="http://prismlibrary.com/" 9 xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls" 10 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 11 prism:ViewModelLocator.AutoWireViewModel="True" 12 mahApps:Title="SIMS--Student Information Management System" 13 mahApps:TitleCharacterCasing="Normal" 14 d:DesignHeight="1080" d:DesignWidth="1920" 15 WindowState="Maximized"> 16 <Window.Resources> 17 <ResourceDictionary> 18 <ResourceDictionary.MergedDictionaries> 19 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" /> 20 </ResourceDictionary.MergedDictionaries> 21 </ResourceDictionary> 22 </Window.Resources> 23 24 <i:Interaction.Triggers> 25 <i:EventTrigger EventName="Loaded"> 26 <i:InvokeCommandAction Command="{Binding LoadedCommand}"></i:InvokeCommandAction> 27 </i:EventTrigger> 28 </i:Interaction.Triggers> 29 <Grid ShowGridLines="True"> 30 <Grid.RowDefinitions> 31 <RowDefinition Height="Auto"></RowDefinition> 32 <RowDefinition Height="*"></RowDefinition> 33 <RowDefinition Height="Auto"></RowDefinition> 34 </Grid.RowDefinitions> 35 <Grid.ColumnDefinitions> 36 <ColumnDefinition Width="Auto"></ColumnDefinition> 37 <ColumnDefinition Width="*"></ColumnDefinition> 38 </Grid.ColumnDefinitions> 39 <ContentControl prism:RegionManager.RegionName="HeaderRegion" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" MinHeight="80"></ContentControl> 40 <ContentControl prism:RegionManager.RegionName="NavRegion" Grid.Row="1" Grid.Column="0"></ContentControl> 41 <ContentControl prism:RegionManager.RegionName="ContentRegion" Grid.Row="1" Grid.Column="1"></ContentControl> 42 <ContentControl prism:RegionManager.RegionName="FooterRegion" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" MinHeight="50"></ContentControl> 43 </Grid> 44 </mahApps:MetroWindow>
主窗口MainWindowViewModel,代码如下:
1 namespace SIMS.ViewModels 2 { 3 public class MainWindowViewModel:BindableBase 4 { 5 6 private IEventAggregator eventAggregator; 7 private IContainerExtension _container; 8 private IRegionManager _regionManager; 9 private IDialogService _dialogService; 10 public MainWindowViewModel(IContainerExtension container, IRegionManager regionManager, IEventAggregator eventAggregator,IDialogService dialogService) { 11 this._container = container; 12 this._regionManager = regionManager; 13 this.eventAggregator = eventAggregator; 14 this._dialogService = dialogService; 15 //弹出登录窗口 16 this._dialogService.ShowDialog("Login", null, LoginCallback, "MetroDialogWindow"); 17 this.eventAggregator.GetEvent<NavEvent>().Subscribe(Navigation); 18 } 19 20 private void LoginCallback(IDialogResult dialogResult) { 21 if (dialogResult.Result != ButtonResult.OK) { 22 Application.Current.Shutdown(); 23 } 24 } 25 26 #region 事件和命令 27 28 private DelegateCommand loadedCommand; 29 30 public DelegateCommand LoadedCommand 31 { 32 get { 33 if (loadedCommand == null) { 34 loadedCommand = new DelegateCommand(Loaded); 35 } 36 return loadedCommand; } 37 } 38 39 private void Loaded() { 40 InitInfo(); 41 } 42 43 44 45 46 private void InitInfo() { 47 var header = _container.Resolve<Header>(); 48 IRegion headerRegion = _regionManager.Regions["HeaderRegion"]; 49 headerRegion.Add(header); 50 // 51 var footer = _container.Resolve<Footer>(); 52 IRegion footerRegion = _regionManager.Regions["FooterRegion"]; 53 footerRegion.Add(footer); 54 55 var welcome = _container.Resolve<Welcome>(); 56 IRegion welcomeRegion = _regionManager.Regions["ContentRegion"]; 57 welcomeRegion.Add(welcome); 58 } 59 60 private void Navigation(string source) { 61 _regionManager.RequestNavigate("ContentRegion", source); 62 //MessageBox.Show(source); 63 } 64 65 #endregion 66 } 67 }
导航页面Navigation.xaml页面采用MAH的Hamburger菜单,如下所示:
1 <UserControl x:Class="SIMS.NavigationModule.Views.Navigation" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" 6 xmlns:local="clr-namespace:SIMS.NavigationModule.Views" 7 xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" 8 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 9 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 10 xmlns:prism="http://prismlibrary.com/" 11 prism:ViewModelLocator.AutoWireViewModel="True" 12 d:DesignHeight="300" 13 d:DesignWidth="240" 14 mc:Ignorable="d"> 15 16 <UserControl.Resources> 17 <ResourceDictionary> 18 <ResourceDictionary.MergedDictionaries> 19 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" /> 20 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" /> 21 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" /> 22 </ResourceDictionary.MergedDictionaries> 23 <!-- This is the template for all menu items. In this sample we use the glyph items. --> 24 <DataTemplate x:Key="HamburgerMenuItem" DataType="{x:Type mah:HamburgerMenuGlyphItem}"> 25 <DockPanel Height="48" LastChildFill="True"> 26 <Grid x:Name="IconPart" 27 DockPanel.Dock="Left"> 28 29 <Image Margin="12" 30 HorizontalAlignment="Center" 31 VerticalAlignment="Center" 32 Source="{Binding Glyph}" /> 33 </Grid> 34 <TextBlock x:Name="TextPart" 35 VerticalAlignment="Center" 36 FontSize="16" 37 Text="{Binding Label}" /> 38 </DockPanel> 39 </DataTemplate> 40 41 42 <DataTemplate x:Key="HamburgerOptionsMenuItem" DataType="{x:Type mah:HamburgerMenuIconItem}"> 43 <DockPanel Height="48" LastChildFill="True"> 44 <ContentControl x:Name="IconPart" 45 Content="{Binding Icon}" 46 DockPanel.Dock="Left" 47 Focusable="False" 48 IsTabStop="False" /> 49 <TextBlock x:Name="TextPart" 50 VerticalAlignment="Center" 51 FontSize="16" 52 Text="{Binding Label}" /> 53 </DockPanel> 54 </DataTemplate> 55 </ResourceDictionary> 56 </UserControl.Resources> 57 58 <Grid Background="{DynamicResource MahApps.Brushes.Accent}"> 59 <Grid.ColumnDefinitions> 60 <ColumnDefinition Width="Auto" /> 61 </Grid.ColumnDefinitions> 62 <mah:HamburgerMenu x:Name="HamburgerMenuControl" Grid.Column="0" 63 DisplayMode="CompactOverlay" 64 Margin="0" 65 IsPaneOpen="True" 66 67 HamburgerButtonHelpText="Please click me" 68 HamburgerButtonClick="HamburgerMenuControl_HamburgerButtonClick" 69 ItemTemplate="{StaticResource HamburgerMenuItem}" 70 OptionsItemTemplate="{StaticResource HamburgerOptionsMenuItem}" 71 ItemsSource="{Binding NavItems}" 72 VerticalScrollBarOnLeftSide="False"> 73 <i:Interaction.Triggers> 74 <i:EventTrigger EventName="ItemInvoked"> 75 <i:InvokeCommandAction Command="{Binding NavCommand}" CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedItem}" ></i:InvokeCommandAction> 76 </i:EventTrigger> 77 </i:Interaction.Triggers> 78 <!-- Header --> 79 <mah:HamburgerMenu.HamburgerMenuHeaderTemplate> 80 <DataTemplate> 81 <TextBlock HorizontalAlignment="Center" 82 VerticalAlignment="Center" 83 FontSize="16" 84 Foreground="White" 85 Text="" /> 86 </DataTemplate> 87 </mah:HamburgerMenu.HamburgerMenuHeaderTemplate> 88 <mah:HamburgerMenu.OptionsItemsSource> 89 <mah:HamburgerMenuItemCollection> 90 91 <mah:HamburgerMenuIconItem x:Name="AboutOption" 92 93 Label="About"> 94 <mah:HamburgerMenuIconItem.Icon> 95 <iconPacks:PackIconMaterial Width="22" 96 Height="22" 97 HorizontalAlignment="Center" 98 VerticalAlignment="Center" 99 Kind="Help" /> 100 </mah:HamburgerMenuIconItem.Icon> 101 <mah:HamburgerMenuIconItem.Tag> 102 <TextBlock HorizontalAlignment="Center" 103 VerticalAlignment="Center" 104 FontSize="28" 105 FontWeight="Bold"> 106 About 107 </TextBlock> 108 </mah:HamburgerMenuIconItem.Tag> 109 </mah:HamburgerMenuIconItem> 110 111 </mah:HamburgerMenuItemCollection> 112 </mah:HamburgerMenu.OptionsItemsSource> 113 <!-- Content --> 114 <mah:HamburgerMenu.ContentTemplate> 115 <DataTemplate> 116 <Grid x:Name="ContentGrid"> 117 <Grid.RowDefinitions> 118 <RowDefinition Height="48" /> 119 <RowDefinition /> 120 </Grid.RowDefinitions> 121 <Border Grid.Row="0" 122 Margin="-1 0 -1 0" 123 Background="#7A7A7A"> 124 <TextBlock x:Name="Header" 125 HorizontalAlignment="Center" 126 VerticalAlignment="Center" 127 FontSize="24" 128 Foreground="White" 129 Text="{Binding Label}" /> 130 </Border> 131 <mah:TransitioningContentControl Grid.Row="1" 132 Content="{Binding}" 133 RestartTransitionOnContentChange="True" 134 Transition="Default"> 135 <mah:TransitioningContentControl.Resources> 136 <DataTemplate DataType="{x:Type mah:HamburgerMenuGlyphItem}"> 137 <Image Source="{Binding Glyph, Mode=OneWay, Converter={mah:NullToUnsetValueConverter}}" /> 138 </DataTemplate> 139 <DataTemplate DataType="{x:Type mah:HamburgerMenuIconItem}"> 140 <ContentControl Content="{Binding Tag, Mode=OneWay}" 141 Focusable="True" 142 IsTabStop="False" /> 143 </DataTemplate> 144 </mah:TransitioningContentControl.Resources> 145 </mah:TransitioningContentControl> 146 </Grid> 147 </DataTemplate> 148 </mah:HamburgerMenu.ContentTemplate> 149 </mah:HamburgerMenu> 150 151 </Grid> 152 153 </UserControl>
导航页面NavigationViewModel页面代码,如下所示:
1 namespace SIMS.NavigationModule.ViewModels 2 { 3 public class NavigationViewModel : BindableBase 4 { 5 #region 属性和构造函数 6 7 private IEventAggregator eventAggregator; 8 9 private List<HamburgerMenuItemBase> navItems; 10 11 public List<HamburgerMenuItemBase> NavItems { get { return navItems; } set { SetProperty(ref navItems, value); } } 12 13 public NavigationViewModel(IEventAggregator eventAggregator) 14 { 15 this.eventAggregator = eventAggregator; 16 navItems = new List<HamburgerMenuItemBase>(); 17 navItems.Add(new HamburgerMenuHeaderItem() { Label = "学生管理"}); 18 navItems.Add(new HamburgerMenuGlyphItem() { Label="学生管理",Tag="Student" ,Glyph="/images/icon_student.png" }); 19 navItems.Add(new HamburgerMenuGlyphItem() { Label = "班级管理",Tag="Classes" , Glyph = "/images/icon_classes.png" }); 20 navItems.Add(new HamburgerMenuGlyphItem() { Label = "课程管理", Tag="Course" , Glyph = "/images/icon_course.png" }); 21 navItems.Add(new HamburgerMenuGlyphItem() { Label = "成绩管理" ,Tag="Score", Glyph = "/images/icon_score.png" }); 22 navItems.Add(new HamburgerMenuHeaderItem() { Label = "系统管理", }); 23 navItems.Add(new HamburgerMenuGlyphItem() { Label = "个人信息",Tag="Personal", Glyph = "/images/icon_personal.png" }); 24 navItems.Add(new HamburgerMenuGlyphItem() { Label = "用户管理",Tag="User", Glyph = "/images/icon_user.png" }); 25 navItems.Add(new HamburgerMenuGlyphItem() { Label = "角色管理",Tag="Role", Glyph = "/images/icon_role.png" }); 26 } 27 28 #endregion 29 30 #region 命令 31 32 private DelegateCommand<object> navCommand; 33 34 public DelegateCommand<object> NavCommand 35 { 36 get 37 { 38 if (navCommand == null) 39 { 40 41 navCommand = new DelegateCommand<object>(Navigation); 42 } 43 return navCommand; 44 } 45 } 46 47 private void Navigation(object obj) { 48 var menuItem = (HamburgerMenuItem)obj; 49 if (menuItem != null) { 50 var tag = menuItem.Tag; 51 if (tag!=null) { 52 this.eventAggregator.GetEvent<NavEvent>().Publish(tag.ToString()); 53 } 54 } 55 } 56 57 #endregion 58 } 59 }
示例截图
本示例目前主要实现了登录,及主页面布局,导航等功能,如下所示:
备注
以上是本文介绍的关于学生信息管理系统的主要内容,旨在抛砖引玉一起学习,共同进步。
贺新郎·九日【作者】刘克庄
湛湛长空黑。更那堪、斜风细雨,乱愁如织。老眼平生空四海,赖有高楼百尺。看浩荡、千崖秋色。白发书生神州泪,尽凄凉、不向牛山滴。追往事,去无迹。
少年自负凌云笔。到而今、春华落尽,满怀萧瑟。常恨世人新意少,爱说南朝狂客。把破帽、年年拈出。若对黄花孤负酒,怕黄花、也笑人岑寂。鸿北去,日西匿。