WPF开发学生信息管理系统【WPF+Prism+MAH+WebApi】(一)

最近通过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接口,就表示一个模块,以学生模块为例,如下所示:

using Prism.Ioc;
using Prism.Modularity;
using SIMS.StudentModule.ViewModels;
using SIMS.StudentModule.Views;
using System;

namespace SIMS.StudentModule
{
    public class StudentModule : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {

        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<Student, StudentViewModel>(nameof(Student));
        }
    }
}

注意:在模块中,需要实现两个接口方法。在此模块中的RegisterTypes方法中,可以注册导航,窗口等以及初始化工作。

如果不注册为导航,而是需要注册到某一个Region中,则可以在OnInitialized方法中进行,以导航模块为例,如下所示:

using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;
using SIMS.NavigationModule.Views;
using System;

namespace SIMS.NavigationModule
{
    public class NavigationModule : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {
            var regionManager = containerProvider.Resolve<IRegionManager>();
            regionManager.RegisterViewWithRegion("NavRegion",typeof(Navigation));
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
    }
}

View和ViewModel自动适配

View和ViewMode在注册导航时,可以手动匹配,也可以自动匹配【需要以固定的方式命名才可以自动适配】。自动适配,需要是在UserControl中,增加一句prism:ViewModelLocator.AutoWireViewModel="True"即可,以标题头为例,如下所示:

<UserControl x:Class="SIMS.Views.Header"
             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:local="clr-namespace:SIMS.Views"
             mc:Ignorable="d"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls"
             d:DesignHeight="100" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

    <Grid Background="{DynamicResource MahApps.Brushes.Accent}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Text="学生信息管理系统" Foreground="White" FontSize="32" FontWeight="Bold" HorizontalAlignment="Left" VerticalAlignment="Center"  Margin="20,5"></TextBlock>
        <StackPanel Grid.Row="1" HorizontalAlignment="Right" Orientation="Horizontal">
            <TextBlock Text="Hello" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="3"></TextBlock>
            <TextBlock Text="Admin" Foreground="White"  Margin="3" FontWeight="Bold"></TextBlock>
            <TextBlock Text="|" Foreground="White"  Margin="3"></TextBlock>
            <TextBlock Text="Logout" Foreground="White"  Margin="3" FontWeight="Bold"></TextBlock>
        </StackPanel>
    </Grid>

</UserControl>

弹出模态窗口

在Prism中,模块中的视图都是以UserControl的形式存在,那么如果需要弹出窗体页面,就需要在ViewModel中,实现IDialogAware接口,以Login登录窗口为例,如下所示:

using Prism.Regions;
using Prism.Services.Dialogs;
using SIMS.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace SIMS.ViewModels
{
    public class LoginViewModel : BindableBase, IDialogAware
    {
        private IRegionManager _regionManager;
        private IContainerExtension _container;

        private string userName;

        public string UserName
        {
            get { return userName; }
            set {SetProperty(ref userName , value); }
        }

        private string password;

        public string Password
        {
            get { return password; }
            set { SetProperty(ref  password , value); }
        }


        public LoginViewModel(IContainerExtension container,IRegionManager regionManager)
        {
            this._container = container;
            this._regionManager = regionManager;
        }

        private void InitInfo() {
            var footer = _container.Resolve<Footer>();
            IRegion footerRegion = _regionManager.Regions["LoginFooterRegion"];
            footerRegion.Add(footer);
        }

        #region 命令

        private DelegateCommand loadedCommand;

        public DelegateCommand LoadedCommand
        {
            get
            {
                if (loadedCommand == null)
                {
                    loadedCommand = new DelegateCommand(Loaded);
                }
                return loadedCommand;
            }
        }

        private void Loaded()
        {
            //InitInfo();
        }

        private DelegateCommand loginCommand;

        public DelegateCommand LoginCommand
        {
            get
            {
                if (loginCommand == null)
                {
                    loginCommand = new DelegateCommand(Login);
                }
                return loginCommand;
            }
        }

        private void Login() {
            if (string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Password)) {
                MessageBox.Show("用户名或密码为空,请确认");
                return;
            }
            if (UserName == "admin" && Password == "abc123")
            {
                CloseWindow();
            }
            else {
                MessageBox.Show("用户名密码不正确,请确认");
                return;
            }
        }

        private DelegateCommand cancelCommand;

        public DelegateCommand CancelCommand
        {
            get
            {
                if (cancelCommand == null)
                {
                    cancelCommand = new DelegateCommand(Cancel);
                }
                return cancelCommand;
            }
        }

        private void Cancel() {
            RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel));
        }

        #endregion

        #region DialogAware接口

        public string Title => "SIMS-Login";

        public event Action<IDialogResult> RequestClose;

        /// <summary>
        /// 成功时关闭窗口
        /// </summary>
        public void CloseWindow() {
            RequestClose?.Invoke(new DialogResult(ButtonResult.OK));
        }

        public bool CanCloseDialog()
        {
            return true;
        }

        public void OnDialogClosed()
        {
            //当关闭时
            RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel));
        }

        public void OnDialogOpened(IDialogParameters parameters)
        {
            //传递解析参数
        }

        #endregion
    }
}

实现了IDialogAware接口,表示以窗口的形态出现,在需要弹出窗口的地方进行调用即可。如下所示:

public MainWindowViewModel(IContainerExtension container, IRegionManager regionManager, IEventAggregator eventAggregator,IDialogService dialogService) {
    this._container = container;
    this._regionManager = regionManager;
    this.eventAggregator = eventAggregator;
    this._dialogService = dialogService;
    //弹出登录窗口
    this._dialogService.ShowDialog("Login", null, LoginCallback, "MetroDialogWindow");
    this.eventAggregator.GetEvent<NavEvent>().Subscribe(Navigation);
}

注意:MetroDialogWindow是自定义个一个Metro风格的窗口,如果为空,则采用默认窗口风格。

模块间交互

按照模块化设计思想,虽然各个模块之间相互独立,但是难免为遇到模块之间进行交互的情况,所以Prism提供了事件聚合器,通过命令的发布和订阅来实现模块间的数据交互。以导航模块为例,当点击某一个导航时,发布一个命令,在主窗口订阅此事件,当收到事件时,将此导航对应的页面渲染到主页面区域中。步骤如下:

1. 定义一个事件

定义一个事件,继承PubSubEvent基类,如下所示:

using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SIMS.Utils.Events
{
    /// <summary>
    /// 导航事件
    /// </summary>
    public class NavEvent : PubSubEvent<string>
    {
    }
}

2. 发布事件

用户点击导航菜单时,触发NavCommand,然后发布命令。

private DelegateCommand<object> navCommand;

public DelegateCommand<object> NavCommand
{
    get
    {
        if (navCommand == null)
        {

            navCommand = new DelegateCommand<object>(Navigation);
        }
        return navCommand;
    }
}

private void Navigation(object obj) {
    var menuItem = (HamburgerMenuItem)obj;
    if (menuItem != null) {
        var tag = menuItem.Tag;
        if (tag!=null) {
            this.eventAggregator.GetEvent<NavEvent>().Publish(tag.ToString());
        }
    }
}

3. 订阅命令

在主窗口,订阅命令,当收到命令时,再初始化模块信息,如下所示:

namespace SIMS.ViewModels
{
    public class MainWindowViewModel:BindableBase
    {

        private IEventAggregator eventAggregator;
        private IContainerExtension _container;
        private IRegionManager _regionManager;
        private IDialogService _dialogService;
        public MainWindowViewModel(IContainerExtension container, IRegionManager regionManager, IEventAggregator eventAggregator,IDialogService dialogService) {
            this._container = container;
            this._regionManager = regionManager;
            this.eventAggregator = eventAggregator;
            this._dialogService = dialogService;
            //弹出登录窗口
            this._dialogService.ShowDialog("Login", null, LoginCallback, "MetroDialogWindow");
            this.eventAggregator.GetEvent<NavEvent>().Subscribe(Navigation);
        }

        private void LoginCallback(IDialogResult dialogResult) {
            if (dialogResult.Result != ButtonResult.OK) {
                Application.Current.Shutdown();
            }
        }

        #region 事件和命令

        private DelegateCommand loadedCommand;

        public DelegateCommand LoadedCommand
        {
            get {
                if (loadedCommand == null) {
                    loadedCommand = new DelegateCommand(Loaded);
                }
                return loadedCommand; }
        }

        private void Loaded() {
            InitInfo();
        }




        private void InitInfo() {
            var header = _container.Resolve<Header>();
            IRegion headerRegion = _regionManager.Regions["HeaderRegion"];
            headerRegion.Add(header);
            //
            var footer = _container.Resolve<Footer>();
            IRegion footerRegion = _regionManager.Regions["FooterRegion"];
            footerRegion.Add(footer);

            var welcome = _container.Resolve<Welcome>();
            IRegion welcomeRegion = _regionManager.Regions["ContentRegion"];
            welcomeRegion.Add(welcome);
        }

        private void Navigation(string source) {
            _regionManager.RequestNavigate("ContentRegion", source);
            //MessageBox.Show(source);
        }

        #endregion
    }
}

注意:一般情况下,只有在不同模块时,才使用事件聚合器进行事件的订阅和发布。如果是同一模块,则没有必要。

核心代码

模块的配置

各个模块之间相互独立,所以在主模块中进行加载时,需要先进行配置。模块加载的方式有很多种,本例采用App.config配置方式,如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf" />
    </configSections>
    <startup>
    </startup>
    <modules>
        <module assemblyFile="SIMS.NavigationModule.dll" moduleType="SIMS.NavigationModule.NavigationModule, SIMS.NavigationModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="NavigationModule" startupLoaded="True" />
        <module assemblyFile="SIMS.ScoreModule.dll" moduleType="SIMS.ScoreModule.ScoreModule, SIMS.ScoreModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ScoreModule" startupLoaded="True" />
        <module assemblyFile="SIMS.StudentModule.dll" moduleType="SIMS.StudentModule.StudentModule, SIMS.StudentModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="StudentModule" startupLoaded="True" />
        <module assemblyFile="SIMS.ClassesModule.dll" moduleType="SIMS.ClassesModule.ClassesModule, SIMS.ClassesModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ClassesModule" startupLoaded="True" />
        <module assemblyFile="SIMS.CourseModule.dll" moduleType="SIMS.CourseModule.CourseModule, SIMS.CourseModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="CourseModule" startupLoaded="True" />
        <module assemblyFile="SIMS.SysManagementModule.dll" moduleType="SIMS.SysManagementModule.SysManagementModule, SIMS.SysManagementModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="SysManagementModule" startupLoaded="True" />
    </modules>
</configuration>

启动入口App.xaml,可以用来配置资源字典等初始化操作,如下所示:

<prism:PrismApplication x:Class="SIMS.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:SIMS"
             xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
             xmlns:prism="http://prismlibrary.com/"
             >
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
                <ResourceDictionary Source="/Icons/Icons.xaml"></ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</prism:PrismApplication>

启动入口App.xaml.cs,应用程序需要继承PrismApplication基类,才是一个Prism应用程序。在此文件中初始化App.config中配置的模块。如下所示:

namespace SIMS
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterDialog<Login, LoginViewModel>(nameof(Login));
            containerRegistry.Register<IDialogWindow, MetroDialogWindow>("MetroDialogWindow");
        }

        protected override IModuleCatalog CreateModuleCatalog()
        {
            return new ConfigurationModuleCatalog();
        }
    }
}

主窗口MainWindow.xaml页面布局,如下所示:

<mahApps:MetroWindow x:Class="SIMS.Views.MainWindow"
        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:local="clr-namespace:SIMS"
        mc:Ignorable="d"
        xmlns:prism="http://prismlibrary.com/"
        xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        prism:ViewModelLocator.AutoWireViewModel="True"
        mahApps:Title="SIMS--Student Information Management System"
        mahApps:TitleCharacterCasing="Normal"
        d:DesignHeight="1080" d:DesignWidth="1920"
         WindowState="Maximized">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}"></i:InvokeCommandAction>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <ContentControl prism:RegionManager.RegionName="HeaderRegion"  Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" MinHeight="80"></ContentControl>
        <ContentControl prism:RegionManager.RegionName="NavRegion" Grid.Row="1" Grid.Column="0"></ContentControl>
        <ContentControl prism:RegionManager.RegionName="ContentRegion" Grid.Row="1" Grid.Column="1"></ContentControl>
        <ContentControl prism:RegionManager.RegionName="FooterRegion" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" MinHeight="50"></ContentControl>
    </Grid>
</mahApps:MetroWindow>

主窗口MainWindowViewModel,代码如下:

namespace SIMS.ViewModels
{
    public class MainWindowViewModel:BindableBase
    {

        private IEventAggregator eventAggregator;
        private IContainerExtension _container;
        private IRegionManager _regionManager;
        private IDialogService _dialogService;
        public MainWindowViewModel(IContainerExtension container, IRegionManager regionManager, IEventAggregator eventAggregator,IDialogService dialogService) {
            this._container = container;
            this._regionManager = regionManager;
            this.eventAggregator = eventAggregator;
            this._dialogService = dialogService;
            //弹出登录窗口
            this._dialogService.ShowDialog("Login", null, LoginCallback, "MetroDialogWindow");
            this.eventAggregator.GetEvent<NavEvent>().Subscribe(Navigation);
        }

        private void LoginCallback(IDialogResult dialogResult) {
            if (dialogResult.Result != ButtonResult.OK) {
                Application.Current.Shutdown();
            }
        }

        #region 事件和命令

        private DelegateCommand loadedCommand;

        public DelegateCommand LoadedCommand
        {
            get {
                if (loadedCommand == null) {
                    loadedCommand = new DelegateCommand(Loaded);
                }
                return loadedCommand; }
        }

        private void Loaded() {
            InitInfo();
        }




        private void InitInfo() {
            var header = _container.Resolve<Header>();
            IRegion headerRegion = _regionManager.Regions["HeaderRegion"];
            headerRegion.Add(header);
            //
            var footer = _container.Resolve<Footer>();
            IRegion footerRegion = _regionManager.Regions["FooterRegion"];
            footerRegion.Add(footer);

            var welcome = _container.Resolve<Welcome>();
            IRegion welcomeRegion = _regionManager.Regions["ContentRegion"];
            welcomeRegion.Add(welcome);
        }

        private void Navigation(string source) {
            _regionManager.RequestNavigate("ContentRegion", source);
            //MessageBox.Show(source);
        }

        #endregion
    }
}

导航页面Navigation.xaml页面采用MAH的Hamburger菜单,如下所示:

<UserControl x:Class="SIMS.NavigationModule.Views.Navigation"
             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:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
             xmlns:local="clr-namespace:SIMS.NavigationModule.Views"
             xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             d:DesignHeight="300"
             d:DesignWidth="240"
             mc:Ignorable="d">

    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
            </ResourceDictionary.MergedDictionaries>
            <!--  This is the template for all menu items. In this sample we use the glyph items.  -->
            <DataTemplate x:Key="HamburgerMenuItem" DataType="{x:Type mah:HamburgerMenuGlyphItem}">
                <DockPanel Height="48" LastChildFill="True">
                    <Grid x:Name="IconPart"
                          DockPanel.Dock="Left">

                        <Image Margin="12"
                               HorizontalAlignment="Center"
                               VerticalAlignment="Center"
                               Source="{Binding Glyph}" />
                    </Grid>
                    <TextBlock x:Name="TextPart"
                               VerticalAlignment="Center"
                               FontSize="16"
                               Text="{Binding Label}" />
                </DockPanel>
            </DataTemplate>


            <DataTemplate x:Key="HamburgerOptionsMenuItem" DataType="{x:Type mah:HamburgerMenuIconItem}">
                <DockPanel Height="48" LastChildFill="True">
                    <ContentControl x:Name="IconPart"
                                    Content="{Binding Icon}"
                                    DockPanel.Dock="Left"
                                    Focusable="False"
                                    IsTabStop="False" />
                    <TextBlock x:Name="TextPart"
                               VerticalAlignment="Center"
                               FontSize="16"
                               Text="{Binding Label}" />
                </DockPanel>
            </DataTemplate>
        </ResourceDictionary>
    </UserControl.Resources>

    <Grid Background="{DynamicResource MahApps.Brushes.Accent}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <mah:HamburgerMenu x:Name="HamburgerMenuControl" Grid.Column="0"
                               DisplayMode="CompactOverlay"
                               Margin="0"
                               IsPaneOpen="True"

                               HamburgerButtonHelpText="Please click me"
                               HamburgerButtonClick="HamburgerMenuControl_HamburgerButtonClick"
                               ItemTemplate="{StaticResource HamburgerMenuItem}"
                               OptionsItemTemplate="{StaticResource HamburgerOptionsMenuItem}"
                               ItemsSource="{Binding NavItems}"
                               VerticalScrollBarOnLeftSide="False">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="ItemInvoked">
                    <i:InvokeCommandAction Command="{Binding NavCommand}" CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedItem}" ></i:InvokeCommandAction>
                </i:EventTrigger>
            </i:Interaction.Triggers>
            <!--  Header  -->
            <mah:HamburgerMenu.HamburgerMenuHeaderTemplate>
                <DataTemplate>
                    <TextBlock HorizontalAlignment="Center"
                                   VerticalAlignment="Center"
                                   FontSize="16"
                                   Foreground="White"
                                   Text="" />
                </DataTemplate>
            </mah:HamburgerMenu.HamburgerMenuHeaderTemplate>
            <mah:HamburgerMenu.OptionsItemsSource>
                <mah:HamburgerMenuItemCollection>

                    <mah:HamburgerMenuIconItem x:Name="AboutOption"

                                                   Label="About">
                        <mah:HamburgerMenuIconItem.Icon>
                            <iconPacks:PackIconMaterial Width="22"
                                                            Height="22"
                                                            HorizontalAlignment="Center"
                                                            VerticalAlignment="Center"
                                                            Kind="Help" />
                        </mah:HamburgerMenuIconItem.Icon>
                        <mah:HamburgerMenuIconItem.Tag>
                            <TextBlock HorizontalAlignment="Center"
                                           VerticalAlignment="Center"
                                           FontSize="28"
                                           FontWeight="Bold">
                                    About
                            </TextBlock>
                        </mah:HamburgerMenuIconItem.Tag>
                    </mah:HamburgerMenuIconItem>

                </mah:HamburgerMenuItemCollection>
            </mah:HamburgerMenu.OptionsItemsSource>
            <!--  Content  -->
            <mah:HamburgerMenu.ContentTemplate>
                <DataTemplate>
                    <Grid x:Name="ContentGrid">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="48" />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0"
                                    Margin="-1 0 -1 0"
                                    Background="#7A7A7A">
                            <TextBlock x:Name="Header"
                                           HorizontalAlignment="Center"
                                           VerticalAlignment="Center"
                                           FontSize="24"
                                           Foreground="White"
                                           Text="{Binding Label}" />
                        </Border>
                        <mah:TransitioningContentControl Grid.Row="1"
                                                             Content="{Binding}"
                                                             RestartTransitionOnContentChange="True"
                                                             Transition="Default">
                            <mah:TransitioningContentControl.Resources>
                                <DataTemplate DataType="{x:Type mah:HamburgerMenuGlyphItem}">
                                    <Image Source="{Binding Glyph, Mode=OneWay, Converter={mah:NullToUnsetValueConverter}}" />
                                </DataTemplate>
                                <DataTemplate DataType="{x:Type mah:HamburgerMenuIconItem}">
                                    <ContentControl Content="{Binding Tag, Mode=OneWay}"
                                                        Focusable="True"
                                                        IsTabStop="False" />
                                </DataTemplate>
                            </mah:TransitioningContentControl.Resources>
                        </mah:TransitioningContentControl>
                    </Grid>
                </DataTemplate>
            </mah:HamburgerMenu.ContentTemplate>
        </mah:HamburgerMenu>

    </Grid>

</UserControl>

导航页面NavigationViewModel页面代码,如下所示:

namespace SIMS.NavigationModule.ViewModels
{
    public class NavigationViewModel : BindableBase
    {
        #region 属性和构造函数

        private IEventAggregator eventAggregator;

        private List<HamburgerMenuItemBase> navItems;

        public List<HamburgerMenuItemBase> NavItems { get { return navItems; } set { SetProperty(ref navItems, value); } }

        public NavigationViewModel(IEventAggregator eventAggregator)
        {
            this.eventAggregator = eventAggregator;
            navItems = new List<HamburgerMenuItemBase>();
            navItems.Add(new HamburgerMenuHeaderItem() { Label = "学生管理"});
            navItems.Add(new HamburgerMenuGlyphItem() { Label="学生管理",Tag="Student" ,Glyph="/images/icon_student.png"   });
            navItems.Add(new HamburgerMenuGlyphItem() { Label = "班级管理",Tag="Classes"  ,  Glyph = "/images/icon_classes.png" });
            navItems.Add(new HamburgerMenuGlyphItem() { Label = "课程管理", Tag="Course" , Glyph = "/images/icon_course.png" });
            navItems.Add(new HamburgerMenuGlyphItem() { Label = "成绩管理" ,Tag="Score",  Glyph = "/images/icon_score.png" });
            navItems.Add(new HamburgerMenuHeaderItem() { Label = "系统管理",  });
            navItems.Add(new HamburgerMenuGlyphItem() { Label = "个人信息",Tag="Personal", Glyph = "/images/icon_personal.png" });
            navItems.Add(new HamburgerMenuGlyphItem() { Label = "用户管理",Tag="User", Glyph = "/images/icon_user.png" });
            navItems.Add(new HamburgerMenuGlyphItem() { Label = "角色管理",Tag="Role", Glyph = "/images/icon_role.png" });
        }

        #endregion

        #region 命令

        private DelegateCommand<object> navCommand;

        public DelegateCommand<object> NavCommand
        {
            get
            {
                if (navCommand == null)
                {

                    navCommand = new DelegateCommand<object>(Navigation);
                }
                return navCommand;
            }
        }

        private void Navigation(object obj) {
            var menuItem = (HamburgerMenuItem)obj;
            if (menuItem != null) {
                var tag = menuItem.Tag;
                if (tag!=null) {
                    this.eventAggregator.GetEvent<NavEvent>().Publish(tag.ToString());
                }
            }
        }

        #endregion
    }
}

示例截图

本示例目前主要实现了登录,及主页面布局,导航等功能,如下所示:

备注

以上是本文介绍的关于学生信息管理系统的主要内容,旨在抛砖引玉一起学习,共同进步。

贺新郎·九日【作者】刘克庄 【朝代】宋

湛湛长空黑。更那堪、斜风细雨,乱愁如织。老眼平生空四海,赖有高楼百尺。看浩荡、千崖秋色。白发书生神州泪,尽凄凉、不向牛山滴。追往事,去无迹。
少年自负凌云笔。到而今、春华落尽,满怀萧瑟。常恨世人新意少,爱说南朝狂客。把破帽、年年拈出。若对黄花孤负酒,怕黄花、也笑人岑寂。鸿北去,日西匿。

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: WPF是一种基于XAML语言的用户界面开发框架,使开发人员能够轻松创建漂亮和高效的桌面应用程序。MVVM是一种模式,它在WPF应用程序中广泛使用,它提供了一种分离UI和业务逻辑的方式,从而简化了代码结构。Prism是一个开源框架,它基于MVVM模式和WPF框架,提供了一些技术和工具,使开发人员能够更轻松地构建复杂的WPF应用程序。 使用WPF MVVM Prism框架可以帮助开发人员提高应用程序的可维护性和可扩展性。通过MVVM,开发人员可以创建一个适应各种应用程序场景的模型,并将其与UI分离。数据绑定和命令绑定使开发人员能够更轻松地将模型中的数据和逻辑与UI控件集成起来。Prism框架还提供了一些工具和功能,如模块化应用程序开发、事件聚合器、导航、对话框、异常处理等功能。这些工具使开发人员能够更轻松地构建复杂的应用程序,并提供了一种可扩展和可重用的方法。 总之,使用WPF MVVM Prism可以使开发人员更轻松地构建复杂的应用程序,并提高应用程序的可维护性和可扩展性。开发人员可以根据自己的需求选择使用这个框架来开发WPF应用程序,从而提高他们的工作效率和代码质量。 ### 回答2: WPF MVVM Prism是一种基于Windows Presentation Foundation(WPF)的软件开发模式,采用了MVVM(Model-View-ViewModel)设计模式和Prism框架来实现软件开发。MVVM是一种在WPF应用程序中使用的设计模式,它将应用程序的视图(View)与应用程序的模型(Model)分离开来,通过ViewModel来连接二者。Prism是一个帮助开发人员编写出复杂、可管控、可扩展和可维护的WPF应用程序的框架。 WPF MVVM Prism提供了很多好处:首先,它能实现软件的可重用性,通过将代码和视图分离,使得代码可以在多个不同的视图中重复使用,提高了开发效率。其次,它可以提高软件的可维护性,通过将逻辑和视图分离,确保逻辑代码与UI代码之间更少的耦合,提高了软件的可扩展性。此外,它还可以提高软件的可测试性,由于MVVM模式中将视图和逻辑分离开,所以开发人员可以更容易地编写出单元测试代码,来测试逻辑代码。最后,由于Prism框架提供了一些通用的,可定制的模块和服务,使得开发人员可以更快地实现常见功能、缩短开发时间。 总之,WPF MVVM Prism是一种高效、可扩展和易于维护的软件开发模式,它将视图和逻辑分离,提高了软件的可重用性和可测试性,同时也提高了软件的可扩展性和可复用性。 ### 回答3: WPF是一种桌面应用程序框架,它允许您为Windows创建高度交互式和可视化的应用程序界面。MVVM是一种软件编程模式,它将用户界面(UI)与业务逻辑分离,以便更好地实现可维护,可扩展和可组合的代码。Prism是一个WPF和Silverlight应用程序的组件库,它提供了一些可重用和可扩展的基础设施组件,帮助您更快速地构建、测试和维护WPF应用程序。因此,WPF MVVM Prism的组合可以帮助您更轻松高效地开发WPF应用程序。您可以使用MVVM模式来改进应用程序的结构和测试,使用Prism来更好地组织和可扩展您的代码,以及使用WPF来实现交互丰富的UI。同时还可以使用Prism提供的事件聚合器、导航器和模块化架构,实现更灵活的应用程序设计。WPF MVVM Prism的组合提供了一种更有效的方式来构建WPF应用程序,以满足现代用户体验的需求,并且更容易测试和维护,因此它已成为WPF应用程序开发的重要组合。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老码识途呀

写作不易,多谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值