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


    最近通过WPF开发项目,为了对WPF知识点进行总结,所以利用业余时间,开发一个学生信息管理系统【Student Information Management System】。前四篇文章进行了框架搭建和模块划分,后台WebApi接口编写,以及课程管理模块,班级管理模块,学生管理模块的开发,本文在前四篇基础之上,继续深入开发学生信息管理系统的成绩管理和系统管理模块,通过本篇文章,将继续巩固之前的知识点,本文仅供学习分享使用,如有不足之处,还请指正。

    涉及知识点

    学生信息管理系统SIMS属于一个小型的完整系统开发,涉及的知识点比较,具体如下所示:

    1. WPF开发中TextBlock,TextBox,DataGrid,Combox,TabControl等控件的基础使用以及数据绑定等操作。
    2. MAH样式的使用,在本示例中MAH主要用于统一页面风格,提高用户体验。
    3. HttpClient使用,主要用于访问服务端提供的接口。

    业务逻辑

    前面几篇文章,由浅入深,逐步介绍了课程管理模块,班级管理模块,学生管理模块,今天继续介绍成绩管理模块,业务逻辑关系如下:

    1. 学生属于某一班级之学生,所以学生中包含班级信息。
    2. 班级中存在班长,同时班长又属于学生的一个实体。
    3. 成绩是某一学生的成绩,且一名学生有各门课程的成绩。所以成绩和学生有关,且和课程有关。

    实体E-R图

    学生表,成绩表,班级表,课程表,各个数据表之间的E-R图,如下所示:

    由此可见,成绩表与课程和学生表,都有关联,所以放在最后。

    成绩管理

    成绩管理主要用于录入各个学生各个课程的成绩,包含成绩表的增删改查功能。

    1. 成绩管理后台服务Service

    IScoreAppService接口是对成绩管理的抽象,如下所示:

     1 namespace SIMS.WebApi.Services.Score
     2 {
     3     public interface IScoreAppService
     4     {
     5         public PagedRequest<ScoreEntity> GetScores(string studentName,string courseName,int pageNum,int pageSize);
     6 
     7         /// <summary>
     8         /// 通过id查询成绩信息
     9         /// </summary>
    10         /// <param name="id"></param>
    11         /// <returns></returns>
    12         public ScoreEntity GetScore(int id);
    13 
    14         /// <summary>
    15         /// 新增成绩
    16         /// </summary>
    17         /// <param name="score"></param>
    18         /// <returns></returns>
    19         public int AddScore(ScoreEntity score);
    20 
    21         /// <summary>
    22         /// 修改成绩
    23         /// </summary>
    24         /// <param name="score"></param>
    25         /// <returns></returns>
    26         public int UpdateScore(ScoreEntity score);
    27 
    28         /// <summary>
    29         /// 删除成绩
    30         /// </summary>
    31         /// <param name="id"></param>
    32         public int DeleteScore(int id);
    33     }
    34 }

    服务实现类ScoreAppService,是对接口的实现,具体如下所示:

     1 namespace SIMS.WebApi.Services.Score
     2 {
     3     public class ScoreAppService : IScoreAppService
     4     {
     5         private DataContext dataContext;
     6 
     7         public ScoreAppService(DataContext dataContext)
     8         {
     9             this.dataContext = dataContext;
    10         }
    11 
    12         public int AddScore(ScoreEntity score)
    13         {
    14             var entity = this.dataContext.Scores.Add(score);
    15             this.dataContext.SaveChanges();
    16             return 0;
    17         }
    18 
    19         public int DeleteScore(int id)
    20         {
    21             var entity = dataContext.Scores.FirstOrDefault(x => x.Id == id);
    22             if (entity != null)
    23             {
    24                 dataContext.Scores.Remove(entity);
    25                 dataContext.SaveChanges();
    26             }
    27             return 0;
    28         }
    29 
    30         public ScoreEntity GetScore(int id)
    31         {
    32             var entity = dataContext.Scores.FirstOrDefault(r => r.Id == id);
    33             return entity;
    34         }
    35 
    36         /// <summary>
    37         /// 按条件查询成绩列表
    38         /// </summary>
    39         /// <param name="studentName"></param>
    40         /// <param name="courseName"></param>
    41         /// <param name="pageNum"></param>
    42         /// <param name="pageSize"></param>
    43         /// <returns></returns>
    44         public PagedRequest<ScoreEntity> GetScores(string studentName, string courseName, int pageNum, int pageSize)
    45         {
    46             IQueryable<ScoreEntity> scores = null;
    47             if (!string.IsNullOrEmpty(studentName) && !string.IsNullOrEmpty(courseName))
    48             {
    49                 var students = this.dataContext.Students.Where(r => r.Name.Contains(studentName));
    50                 var courses = this.dataContext.Courses.Where(r => r.Name.Contains(courseName));
    51                 scores = this.dataContext.Scores.Where(r => students.Select(t => t.Id).Contains(r.StudentId)).Where(r => courses.Select(t => t.Id).Contains(r.CourseId));
    52             }
    53             else if (!string.IsNullOrEmpty(studentName))
    54             {
    55                 var students = this.dataContext.Students.Where(r => r.Name.Contains(studentName));
    56                 scores = this.dataContext.Scores.Where(r => students.Select(t => t.Id).Contains(r.StudentId));
    57             }
    58             else if (!string.IsNullOrEmpty(courseName))
    59             {
    60                 var courses = this.dataContext.Courses.Where(r => r.Name.Contains(courseName));
    61                 scores = this.dataContext.Scores.Where(r => courses.Select(t => t.Id).Contains(r.CourseId));
    62             }
    63             else {
    64                 scores = dataContext.Scores.Where(r => true).OrderBy(r => r.Id);
    65             }
    66             int count = scores.Count();
    67             List<ScoreEntity> items;
    68             if (pageSize > 0)
    69             {
    70                 items = scores.Skip((pageNum - 1) * pageSize).Take(pageSize).ToList();
    71             }
    72             else
    73             {
    74                 items = scores.ToList();
    75             }
    76             return new PagedRequest<ScoreEntity>()
    77             {
    78                 count = count,
    79                 items = items
    80             };
    81         }
    82 
    83         public int UpdateScore(ScoreEntity score)
    84         {
    85             dataContext.Scores.Update(score);
    86             dataContext.SaveChanges();
    87             return 0;
    88         }
    89     }
    90 }

    2. 成绩管理WebApi接口控制器

    控制器是对数据服务的公开,每一个控制器的方法表示一个Action,即表示一个客户端可以访问的入口。具体如下所示:

     1 namespace SIMS.WebApi.Controllers
     2 {
     3     /// <summary>
     4     /// 成绩控制器
     5     /// </summary>
     6     [Route("api/[controller]/[action]")]
     7     [ApiController]
     8     public class ScoreController : ControllerBase
     9     {
    10         private readonly ILogger<ScoreController> logger;
    11 
    12         private readonly IScoreAppService scoreAppService;
    13 
    14         public ScoreController(ILogger<ScoreController> logger, IScoreAppService scoreAppService)
    15         {
    16             this.logger = logger;
    17             this.scoreAppService = scoreAppService;
    18         }
    19 
    20         /// <summary>
    21         /// 获取成绩信息
    22         /// </summary>
    23         /// <param name="id"></param>
    24         /// <returns></returns>
    25         [HttpGet]
    26         public PagedRequest<ScoreEntity> GetScores(string? studentName, string? courseName, int pageNum, int pageSize)
    27         {
    28             return scoreAppService.GetScores(studentName, courseName, pageNum, pageSize);
    29         }
    30 
    31         /// <summary>
    32         /// 获取成绩信息
    33         /// </summary>
    34         /// <param name="id"></param>
    35         /// <returns></returns>
    36         [HttpGet]
    37         public ScoreEntity GetScore(int id)
    38         {
    39             return scoreAppService.GetScore(id);
    40         }
    41 
    42         /// <summary>
    43         /// 新增成绩
    44         /// </summary>
    45         /// <param name="score"></param>
    46         /// <returns></returns>
    47         [HttpPost]
    48         public int AddScore(ScoreEntity score)
    49         {
    50             return scoreAppService.AddScore(score);
    51         }
    52 
    53         /// <summary>
    54         /// 修改成绩
    55         /// </summary>
    56         /// <param name="score"></param>
    57         /// <returns></returns>
    58         [HttpPut]
    59         public int UpdateScore(ScoreEntity score)
    60         {
    61             return scoreAppService.UpdateScore(score);
    62         }
    63 
    64         /// <summary>
    65         /// 删除成绩
    66         /// </summary>
    67         /// <param name="id"></param>
    68         [HttpDelete]
    69         public int DeleteScore(int id)
    70         {
    71             return scoreAppService.DeleteScore(id);
    72         }
    73     }
    74 }

    当服务运行起来后,Swagger还每一个控制器都进行归类,可以清晰的看到每一个接口对应的网址,成绩管理模块对应的接口如下所示:

    3. 成绩管理客户端接口访问类HttpUtil

    在学生信息系统开发的过程中,发现所有的接口访问都是通用的,所以对接口访问功能提取成一个HttpUtil基类【包括GET,POST,PUT,DELETE等功能】,其他具体业务再继承基类,并细化具体业务即可。ScoreHttpUtil代码如下所示:

     1 namespace SIMS.Utils.Http
     2 {
     3     public class ScoreHttpUtil:HttpUtil
     4     {
     5         /// <summary>
     6         /// 通过id查询成绩信息
     7         /// </summary>
     8         /// <param name="id"></param>
     9         /// <returns></returns>
    10         public static ScoreEntity GetScore(int id)
    11         {
    12             Dictionary<string, object> data = new Dictionary<string, object>();
    13             data["id"] = id;
    14             var str = Get(UrlConfig.SCORE_GETSCORE, data);
    15             var socre = StrToObject<ScoreEntity>(str);
    16             return socre;
    17         }
    18 
    19         /// <summary>
    20         /// 
    21         /// </summary>
    22         /// <param name="studentName"></param>
    23         /// <param name="courseName"></param>
    24         /// <param name="pageNum"></param>
    25         /// <param name="pageSize"></param>
    26         /// <returns></returns>
    27         public static PagedRequest<ScoreEntity> GetScores(string? studentName, string? courseName, int pageNum, int pageSize)
    28         {
    29             Dictionary<string, object> data = new Dictionary<string, object>();
    30             data["courseName"] = courseName;
    31             data["studentName"] = studentName;
    32             data["pageNum"] = pageNum;
    33             data["pageSize"] = pageSize;
    34             var str = Get(UrlConfig.SCORE_GETSCORES, data);
    35             var socres = StrToObject<PagedRequest<ScoreEntity>>(str);
    36             return socres;
    37         }
    38 
    39         public static bool AddScore(ScoreEntity socre)
    40         {
    41             var ret = Post<ScoreEntity>(UrlConfig.SCORE_ADDSCORE, socre);
    42             return int.Parse(ret) == 0;
    43         }
    44 
    45         public static bool UpdateScore(ScoreEntity socre)
    46         {
    47             var ret = Put<ScoreEntity>(UrlConfig.SCORE_UPDATESCORE, socre);
    48             return int.Parse(ret) == 0;
    49         }
    50 
    51         public static bool DeleteScore(int Id)
    52         {
    53             Dictionary<string, string> data = new Dictionary<string, string>();
    54             data["Id"] = Id.ToString();
    55             var ret = Delete(UrlConfig.SCORE_DELETESCORE, data);
    56             return int.Parse(ret) == 0;
    57         }
    58     }
    59 }

    4. 成绩管理客户端操作

    经过前面四个部分的开发,客户端就可以与数据接口进行交互,展示数据到客户端。客户端所有的开发,均采用MVVM模式进行。

    在成绩管理模块中,根据功能区分,主要包含两个View视图及对应的ViewModel。如下所示:

    1. Score视图,主要用于成绩的查询,以及新增,修改,删除的链接入口。
    2. AddEditScore视图,主要用于成绩信息的新增和修改,共用一个视图页面。
    3. 成绩课程不需要页面,所以没有对应视图。

    4.1. Score视图

    Score视图,主要是成绩的查询和新增,修改,删除的链接入口。涉及知识点如下:

    1. Score视图页面布局采用Grid方式和StackPanel混合布局,即整体布局采用Grid,细微布局采用StackPanel。
    2. 成绩采用分页列表的方式展示,需要用到DataGrid,及分页控件【WPF默认不提供分页控件,可自行编写分页控件】。
    3. 查询条件采用按钮Button和文本框TextBox等组成,关于基础控件的使用,不再详细论述,可参考其他文章。
    4. 在本系统的所有WPF视图中,均需要引入Prism和 MAH组件。
    5. Score视图中,所有的数据均采用Binding的方式与ViewModel进行交互。

    Score视图具体代码,如下所示:

      1 <UserControl x:Class="SIMS.ScoreModule.Views.Score"
      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:i="http://schemas.microsoft.com/xaml/behaviors"
      7              xmlns:prism="http://prismlibrary.com/"
      8              xmlns:local="clr-namespace:SIMS.ScoreModule.Views"
      9              mc:Ignorable="d" 
     10              xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls"
     11              xmlns:ctrls ="clr-namespace:SIMS.Utils.Controls;assembly=SIMS.Utils"
     12              prism:ViewModelLocator.AutoWireViewModel="True"
     13              d:DesignHeight="450" d:DesignWidth="800">
     14 
     15     <UserControl.Resources>
     16         <ResourceDictionary>
     17             <ResourceDictionary.MergedDictionaries>
     18                 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
     19                 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
     20                 <ResourceDictionary>
     21                     <Style x:Key="LinkButton" TargetType="Button">
     22                         <Setter Property="Background" Value="White"></Setter>
     23                         <Setter Property="Cursor" Value="Hand"></Setter>
     24                         <Setter Property="Margin" Value="3"></Setter>
     25                         <Setter Property="MinWidth" Value="80"></Setter>
     26                         <Setter Property="MinHeight" Value="25"></Setter>
     27                         <Setter Property="BorderThickness" Value="0 0 0 0"></Setter>
     28                     </Style>
     29                 </ResourceDictionary>
     30             </ResourceDictionary.MergedDictionaries>
     31         </ResourceDictionary>
     32     </UserControl.Resources>
     33     <i:Interaction.Triggers>
     34         <i:EventTrigger EventName="Loaded">
     35             <i:InvokeCommandAction Command="{Binding LoadedCommand}"></i:InvokeCommandAction>
     36         </i:EventTrigger>
     37     </i:Interaction.Triggers>
     38     <Grid>
     39         <Grid.RowDefinitions>
     40             <RowDefinition Height="Auto"></RowDefinition>
     41             <RowDefinition Height="Auto"></RowDefinition>
     42             <RowDefinition Height="*"></RowDefinition>
     43             <RowDefinition Height="Auto"></RowDefinition>
     44         </Grid.RowDefinitions>
     45         <TextBlock Text="成绩信息" FontSize="20" Background="AliceBlue" Margin="2"></TextBlock>
     46         <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center">
     47             <TextBlock Text="学生名称" VerticalAlignment="Center" Margin="2"></TextBlock>
     48             <TextBox Margin="4" MinWidth="120" Height="30"
     49                      Text="{Binding StudentName}"
     50                              HorizontalContentAlignment="Stretch"
     51                              mahApps:TextBoxHelper.ClearTextButton="True"
     52                              mahApps:TextBoxHelper.Watermark="学生名称"
     53                              mahApps:TextBoxHelper.WatermarkAlignment="Left"
     54                              SpellCheck.IsEnabled="True" />
     55             <TextBlock Text="课程名称" VerticalAlignment="Center" Margin="2"></TextBlock>
     56             <TextBox Margin="4" MinWidth="120" Height="30"
     57                      Text="{Binding CourseName}"
     58                              HorizontalContentAlignment="Stretch"
     59                              mahApps:TextBoxHelper.ClearTextButton="True"
     60                              mahApps:TextBoxHelper.Watermark="课程名称"
     61                              mahApps:TextBoxHelper.WatermarkAlignment="Left"
     62                              SpellCheck.IsEnabled="True" />
     63             <Button Content="查询" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Width="120" Height="30" Margin="3" Command="{Binding QueryCommand}"></Button>
     64             <Button Content="新增" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Width="120" Height="30" Margin="3" Command="{Binding AddCommand}"></Button>
     65         </StackPanel>
     66         <DataGrid x:Name="dgScores"
     67                   Grid.Row="2"
     68                   Grid.Column="0"
     69                   Margin="2"
     70                   AutoGenerateColumns="False"
     71                   CanUserAddRows="False"
     72                   CanUserDeleteRows="False"
     73                   ItemsSource="{Binding Scores}"
     74                   RowHeaderWidth="0">
     75             <DataGrid.Columns>
     76                 <DataGridTextColumn Binding="{Binding Student.Name}" Header="学生" Width="*" />
     77                 <DataGridTextColumn Binding="{Binding Course.Name}" Header="课程" Width="*"/>
     78                 <DataGridTextColumn Binding="{Binding Score}" Header="成绩" Width="*"/>
     79                 <DataGridTextColumn Binding="{Binding CreateTime, StringFormat=yyyy-MM-dd HH:mm:ss}" Header="创建时间" Width="*"/>
     80                 <DataGridTextColumn Binding="{Binding LastEditTime,StringFormat=yyyy-MM-dd HH:mm:ss}" Header="最后修改时间" Width="*"/>
     81                 <DataGridTemplateColumn Header="操作" Width="*">
     82                     <DataGridTemplateColumn.CellTemplate>
     83                         <DataTemplate>
     84                             <StackPanel Orientation="Horizontal">
     85                                 <Button  Content="Edit" Style="{StaticResource LinkButton}" Command="{Binding RelativeSource={RelativeSource  AncestorType=DataGrid,  Mode=FindAncestor}, Path=DataContext.EditCommand}" CommandParameter="{Binding Id}">
     86                                     <Button.Template>
     87                                         <ControlTemplate TargetType="Button">
     88                                             <TextBlock TextDecorations="Underline" HorizontalAlignment="Center">
     89                                                 <ContentPresenter />
     90                                             </TextBlock>
     91                                         </ControlTemplate>
     92                                     </Button.Template>
     93                                 </Button>
     94                                 <Button Content="Delete" Style="{StaticResource LinkButton}" Command="{Binding RelativeSource={RelativeSource  AncestorType=DataGrid,  Mode=FindAncestor}, Path=DataContext.DeleteCommand}" CommandParameter="{Binding Id}">
     95                                     <Button.Template>
     96                                         <ControlTemplate TargetType="Button">
     97                                             <TextBlock TextDecorations="Underline" HorizontalAlignment="Center">
     98                                                 <ContentPresenter />
     99                                             </TextBlock>
    100                                         </ControlTemplate>
    101                                     </Button.Template>
    102                                 </Button>
    103                             </StackPanel>
    104                         </DataTemplate>
    105                     </DataGridTemplateColumn.CellTemplate>
    106                 </DataGridTemplateColumn>
    107             </DataGrid.Columns>
    108         </DataGrid>
    109         <ctrls:PageControl Grid.Row="3" DataContext="{Binding}" ></ctrls:PageControl>
    110     </Grid>
    111 </UserControl>

    4.2. ScoreViewModel

    ScoreViewModel是页面视图的业务逻辑处理,如处理客户端的点击的命令等内容。具体代码如下所示:

      1 namespace SIMS.ScoreModule.ViewModels
      2 {
      3     public class ScoreViewModel : BindableBase
      4     {
      5 
      6         #region 属性或构造方法
      7 
      8         /// <summary>
      9         /// 课程名称
     10         /// </summary>
     11         private string courseName;
     12 
     13         public string CourseName
     14         {
     15             get { return courseName; }
     16             set { SetProperty(ref courseName, value); }
     17         }
     18 
     19         /// <summary>
     20         /// 学生姓名
     21         /// </summary>
     22         private string studentName;
     23 
     24         public string StudentName
     25         {
     26             get { return studentName; }
     27             set { SetProperty(ref studentName, value); }
     28         }
     29 
     30         private ObservableCollection<ScoreInfo> scores;
     31 
     32         public ObservableCollection<ScoreInfo> Scores
     33         {
     34             get { return scores; }
     35             set { SetProperty(ref scores, value); }
     36         }
     37 
     38         private IDialogService dialogService;
     39 
     40         public ScoreViewModel(IDialogService dialogService)
     41         {
     42             this.dialogService = dialogService;
     43             this.pageNum = 1;
     44             this.pageSize = 20;
     45         }
     46 
     47         private void InitInfo()
     48         {
     49             Scores = new ObservableCollection<ScoreInfo>();
     50             var pagedRequst = ScoreHttpUtil.GetScores(this.StudentName, this.CourseName, this.pageNum, this.pageSize);
     51             var entities = pagedRequst.items;
     52             Scores.AddRange(entities.Select(r=>new ScoreInfo(r)));
     53             //
     54             this.TotalCount = pagedRequst.count;
     55             this.TotalPage = ((int)Math.Ceiling(this.TotalCount * 1.0 / this.pageSize));
     56         }
     57 
     58         #endregion
     59 
     60         #region 事件
     61 
     62         private DelegateCommand loadedCommand;
     63 
     64         public DelegateCommand LoadedCommand
     65         {
     66             get
     67             {
     68                 if (loadedCommand == null)
     69                 {
     70                     loadedCommand = new DelegateCommand(Loaded);
     71                 }
     72                 return loadedCommand;
     73             }
     74         }
     75 
     76         private void Loaded()
     77         {
     78             InitInfo();
     79         }
     80 
     81         private DelegateCommand queryCommand;
     82 
     83         public DelegateCommand QueryCommand
     84         {
     85             get
     86             {
     87                 if (queryCommand == null)
     88                 {
     89                     queryCommand = new DelegateCommand(Query);
     90                 }
     91                 return queryCommand;
     92             }
     93         }
     94 
     95         private void Query()
     96         {
     97             this.pageNum = 1;
     98             this.InitInfo();
     99         }
    100 
    101         /// <summary>
    102         /// 新增命令
    103         /// </summary>
    104         private DelegateCommand addCommand;
    105 
    106         public DelegateCommand AddCommand
    107         {
    108             get
    109             {
    110                 if (addCommand == null)
    111                 {
    112                     addCommand = new DelegateCommand(Add);
    113                 }
    114                 return addCommand;
    115             }
    116         }
    117 
    118         private void Add()
    119         {
    120             this.dialogService.ShowDialog("addEditScore", null, AddEditCallBack, "MetroDialogWindow");
    121         }
    122 
    123         private void AddEditCallBack(IDialogResult dialogResult)
    124         {
    125             if (dialogResult != null && dialogResult.Result == ButtonResult.OK)
    126             {
    127                 //刷新列表
    128                 this.pageNum = 1;
    129                 this.InitInfo();
    130             }
    131         }
    132 
    133         /// <summary>
    134         /// 编辑命令
    135         /// </summary>
    136         private DelegateCommand<object> editCommand;
    137 
    138         public DelegateCommand<object> EditCommand
    139         {
    140             get
    141             {
    142                 if (editCommand == null)
    143                 {
    144                     editCommand = new DelegateCommand<object>(Edit);
    145                 }
    146                 return editCommand;
    147             }
    148         }
    149 
    150         private void Edit(object obj)
    151         {
    152             if (obj == null)
    153             {
    154                 return;
    155             }
    156             var Id = int.Parse(obj.ToString());
    157             var score = this.Scores.FirstOrDefault(r => r.Id == Id);
    158             if (score == null)
    159             {
    160                 MessageBox.Show("无效的成绩ID");
    161                 return;
    162             }
    163             IDialogParameters dialogParameters = new DialogParameters();
    164             dialogParameters.Add("score", score);
    165             this.dialogService.ShowDialog("addEditScore", dialogParameters, AddEditCallBack, "MetroDialogWindow");
    166         }
    167 
    168         /// <summary>
    169         /// 编辑命令
    170         /// </summary>
    171         private DelegateCommand<object> deleteCommand;
    172 
    173         public DelegateCommand<object> DeleteCommand
    174         {
    175             get
    176             {
    177                 if (deleteCommand == null)
    178                 {
    179                     deleteCommand = new DelegateCommand<object>(Delete);
    180                 }
    181                 return deleteCommand;
    182             }
    183         }
    184 
    185         private void Delete(object obj)
    186         {
    187             if (obj == null)
    188             {
    189                 return;
    190             }
    191             var Id = int.Parse(obj.ToString());
    192             var score = this.Scores.FirstOrDefault(r => r.Id == Id);
    193             if (score == null)
    194             {
    195                 MessageBox.Show("无效的成绩ID");
    196                 return;
    197             }
    198             if (MessageBoxResult.Yes != MessageBox.Show("Are you sure to delete?", "Confirm", MessageBoxButton.YesNo))
    199             {
    200                 return;
    201             }
    202             bool flag = ScoreHttpUtil.DeleteScore(Id);
    203             if (flag)
    204             {
    205                 this.pageNum = 1;
    206                 this.InitInfo();
    207             }
    208         }
    209 
    210         #endregion
    211     }
    212 }

    注意:关于分页功能,与其他模块代码通用,所以此处略去。

    4. 3. 新增编辑成绩视图AddEditScore

    新增编辑成绩视图,主要用于对成绩的修改和新增,可通过查询页面的新增按钮和具体成绩的编辑按钮弹出对应窗口。如下所示:

     1 <UserControl x:Class="SIMS.ScoreModule.Views.AddEditScore"
     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.ScoreModule.Views"
     7              xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
     8              xmlns:mahApps ="http://metro.mahapps.com/winfx/xaml/controls"
     9              xmlns:prism="http://prismlibrary.com/"      
    10              mc:Ignorable="d" 
    11              d:DesignHeight="450" d:DesignWidth="800">
    12     <prism:Dialog.WindowStyle>
    13         <Style TargetType="Window">
    14             <Setter Property="Width" Value="600"></Setter>
    15             <Setter Property="Height" Value="400"></Setter>
    16         </Style>
    17     </prism:Dialog.WindowStyle>
    18     <UserControl.Resources>
    19         <ResourceDictionary>
    20             <ResourceDictionary.MergedDictionaries>
    21                 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
    22                 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
    23             </ResourceDictionary.MergedDictionaries>
    24         </ResourceDictionary>
    25     </UserControl.Resources>
    26     <i:Interaction.Triggers>
    27         <i:EventTrigger EventName="Loaded">
    28             <i:InvokeCommandAction Command="{Binding LoadedCommand}"></i:InvokeCommandAction>
    29         </i:EventTrigger>
    30     </i:Interaction.Triggers>
    31     <Grid>
    32         <Grid.ColumnDefinitions>
    33             <ColumnDefinition Width="0.2*"></ColumnDefinition>
    34             <ColumnDefinition Width="Auto"></ColumnDefinition>
    35             <ColumnDefinition Width="*"></ColumnDefinition>
    36             <ColumnDefinition Width="0.2*"></ColumnDefinition>
    37         </Grid.ColumnDefinitions>
    38         <Grid.RowDefinitions>
    39             <RowDefinition></RowDefinition>
    40             <RowDefinition></RowDefinition>
    41             <RowDefinition></RowDefinition>
    42             <RowDefinition></RowDefinition>
    43         </Grid.RowDefinitions>
    44         <TextBlock Text="学生" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Margin="3"></TextBlock>
    45         <ComboBox Grid.Row="0" Grid.Column="2" MinWidth="120" Height="35" ItemsSource="{Binding Students}" mahApps:TextBoxHelper.ClearTextButton="True" SelectedItem="{Binding Student}">
    46             <ComboBox.ItemTemplate>
    47                 <DataTemplate>
    48                     <TextBlock Text="{Binding Name}"></TextBlock>
    49                 </DataTemplate>
    50             </ComboBox.ItemTemplate>
    51         </ComboBox>
    52         <TextBlock Text="课程" Grid.Row="1" Grid.Column="1"  VerticalAlignment="Center" Margin="3"></TextBlock>
    53         <ComboBox Grid.Row="1" Grid.Column="2" MinWidth="120" Height="35" ItemsSource="{Binding Courses}" mahApps:TextBoxHelper.ClearTextButton="True" SelectedItem="{Binding Course}">
    54             <ComboBox.ItemTemplate>
    55                 <DataTemplate>
    56                     <TextBlock Text="{Binding Name}"></TextBlock>
    57                 </DataTemplate>
    58             </ComboBox.ItemTemplate>
    59         </ComboBox>
    60         <TextBlock Text="成绩" Grid.Row="2" Grid.Column="1"   VerticalAlignment="Center" Margin="3"></TextBlock>
    61         <TextBox Grid.Row="2" Grid.Column="2" MinWidth="120" Height="35"   VerticalAlignment="Center" Margin="3" Text="{Binding Score.Score}"></TextBox>
    62         <StackPanel Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="3">
    63             <Button Content="取消" Margin="5" MinWidth="120" Height="35" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Command="{Binding CancelCommand}"></Button>
    64             <Button Content="保存" Margin="5" MinWidth="120" Height="35" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Command="{Binding SaveCommand}"></Button>
    65         </StackPanel>
    66     </Grid>
    67 </UserControl>

    4. 新增编辑成绩ViewModel

    AddEditScoreViewModel是对页面具体功能的业务封装,主要是对应成绩信息的保存,也包括数据绑定和命令绑定等内容,与其他模块不同之处,在于此模块关联学生和课程信息,需要绑定下拉框数据源。具体如下所示:

      1 namespace SIMS.ScoreModule.ViewModels
      2 {
      3     public class AddEditScoreViewModel : BindableBase, IDialogAware
      4     {
      5 
      6         #region 属性和构造函数
      7 
      8         /// <summary>
      9         /// 当前实体
     10         /// </summary>
     11         private ScoreEntity score;
     12 
     13         public ScoreEntity Score
     14         {
     15             get { return score; }
     16             set { SetProperty(ref score , value); }
     17         }
     18 
     19 
     20         /// <summary>
     21         /// 下拉框选择的学生
     22         /// </summary>
     23         private StudentEntity student;
     24 
     25         public StudentEntity Student
     26         {
     27             get { return student; }
     28             set { SetProperty(ref student , value); }
     29         }
     30 
     31         /// <summary>
     32         /// 学生列表
     33         /// </summary>
     34         private List<StudentEntity> students;
     35 
     36         public List<StudentEntity> Students
     37         {
     38             get { return students; }
     39             set { SetProperty(ref students, value); }
     40         }
     41 
     42         private CourseEntity course;
     43 
     44         public CourseEntity Course
     45         {
     46             get { return course; }
     47             set {SetProperty(ref course , value); }
     48         }
     49 
     50 
     51         /// <summary>
     52         /// 课程列表
     53         /// </summary>
     54         private List<CourseEntity> courses;
     55 
     56         public List<CourseEntity> Courses
     57         {
     58             get { return courses; }
     59             set { SetProperty(ref courses, value); }
     60         }
     61 
     62         public AddEditScoreViewModel() { 
     63         
     64         }
     65 
     66 
     67         #endregion
     68 
     69         #region Command
     70 
     71         private DelegateCommand loadedCommand;
     72 
     73         public DelegateCommand LoadedCommand
     74         {
     75             get
     76             {
     77                 if (loadedCommand == null)
     78                 {
     79                     loadedCommand = new DelegateCommand(Loaded);
     80                 }
     81                 return loadedCommand;
     82             }
     83         }
     84 
     85         private void Loaded()
     86         {
     87             LoadStudents();
     88             LoadCourses();
     89 
     90             //如果有班长,则为班长赋值
     91             if (Score.StudentId > 0)
     92             {
     93                 this.Student = this.Students?.FirstOrDefault(r => r.Id == Score.StudentId);
     94             }
     95             if (Score.CourseId > 0) { 
     96                 this.Course = this.Courses?.FirstOrDefault(r=>r.Id == Score.CourseId);
     97             }
     98         }
     99 
    100 
    101         private DelegateCommand cancelCommand;
    102 
    103         public DelegateCommand CancelCommand
    104         {
    105             get
    106             {
    107                 if (cancelCommand == null)
    108                 {
    109                     cancelCommand = new DelegateCommand(Cancel);
    110                 }
    111                 return cancelCommand;
    112             }
    113         }
    114 
    115         private void Cancel()
    116         {
    117             RequestClose?.Invoke((new DialogResult(ButtonResult.Cancel)));
    118         }
    119 
    120         private DelegateCommand saveCommand;
    121 
    122         public DelegateCommand SaveCommand
    123         {
    124             get
    125             {
    126                 if (saveCommand == null)
    127                 {
    128                     saveCommand = new DelegateCommand(Save);
    129                 }
    130                 return saveCommand;
    131             }
    132         }
    133 
    134         private void Save()
    135         {
    136             if (Score != null)
    137             {
    138                 Score.CreateTime = DateTime.Now;
    139                 Score.LastEditTime = DateTime.Now;
    140                 if (Student != null)
    141                 {
    142                     Score.StudentId = Student.Id;
    143                 }
    144                 if (Course != null) { 
    145                     Score.CourseId = Course.Id;
    146                 }
    147                 bool flag = false;
    148                 if (Score.Id > 0)
    149                 {
    150                     flag = ScoreHttpUtil.UpdateScore(Score);
    151                 }
    152                 else
    153                 {
    154                     flag = ScoreHttpUtil.AddScore(Score);
    155                 }
    156                 if (flag)
    157                 {
    158                     RequestClose?.Invoke((new DialogResult(ButtonResult.OK)));
    159                 }
    160             }
    161         }
    162 
    163 
    164         #endregion
    165 
    166         #region 函数
    167 
    168         /// <summary>
    169         /// 加载学生列表
    170         /// </summary>
    171         private void LoadStudents() {
    172             this.Students = new List<StudentEntity>();
    173             var pagedRequst = StudentHttpUtil.GetStudents(null, null, 1, -1);
    174             var entities = pagedRequst.items;
    175             Students.AddRange(entities);
    176         }
    177 
    178         /// <summary>
    179         /// 加载课程列表
    180         /// </summary>
    181         private void LoadCourses() {
    182             this.Courses = new List<CourseEntity>();
    183             var pagedRequst = CourseHttpUtil.GetCourses(null, null, 1, -1);
    184             var entities = pagedRequst.items;
    185             Courses.AddRange(entities);
    186         }
    187 
    188         #endregion
    189     }
    190 }

    注意:弹出窗口实现方法与其他模块通用,所以此处略去。

    5. 成绩管理示例效果图

    经过上述步骤后,成绩管理模块就开发完成,运行VS后,效果如下所示:

    系统管理模块

    1. 系统管理模块核心代码

    系统管理模块,主要包含四个部分,用户管理,角色管理,菜单管理,个人信息。因篇幅有限,暂时仅列出主要内容:

    从数据库读取用户所属的权限,代码如下所示:

     1 public List<UserRight> GetUserRights(int? userId)
     2 {
     3     if (userId != null)
     4     {
     5         var query = from u in dataContext.UserRoles
     6                     join r in dataContext.Roles on u.RoleId equals r.Id
     7                     join x in dataContext.RoleMenus on r.Id equals x.RoleId
     8                     join m in dataContext.Menus on x.MenuId equals m.Id
     9                     where u.UserId == userId
    10                     select new UserRight { Id = m.Id, RoleName = r.Name, MenuName = m.Name, Url = m.Url,Icon=m.Icon, ParentId = m.ParentId, SortId = m.SortId };
    11 
    12         return query.ToList();
    13     }
    14     return null;
    15 }

    在客户端获取后,转换成导航菜单对象即可,如下所示:

     1 public NavigationViewModel(IEventAggregator eventAggregator)
     2 {
     3     this.eventAggregator = eventAggregator;
     4     navItems = new List<HamburgerMenuItemBase>();
     5     var userRights = RoleHttpUtil.GetUserRights(UserInfo.Instance.Id);
     6     var parents = userRights.Where(x => x.ParentId == null).OrderBy(r=>r.SortId);
     7     foreach (var parent in parents) {
     8         navItems.Add(new HamburgerMenuHeaderItem() { Label = parent.MenuName });
     9         var subItems = userRights.Where(r=>r.ParentId==parent.Id).OrderBy(r=>r.SortId);
    10         foreach (var subItem in subItems) {
    11             navItems.Add(new HamburgerMenuGlyphItem() { Label = subItem.MenuName, Tag = subItem.Url, Glyph = subItem.Icon });
    12         }
    13     }
    14     UserInfo.Instance.Roles = String.Join(',', userRights.Select(r=>r.RoleName).Distinct().ToList());
    15 }

    2. 系统管理模块示例截图

    关于系统管理模块示例截图如下所示:

    个人信息,显示个人基础信息,如下所示:

     用户管理,比其他列表多了一个授权按钮,主要用于为用户分配角色如下所示:

    角色管理模块,比其他列表多了一个分配按钮,主要用于为分配角色对应的菜单如下所示:

    菜单管理,菜单管理模块,主要用于管理菜单信息,与其他模块不同的是,需要配置图标,如下所示:

    总结

    通过本篇文章的成绩管理模块,系统管理模块,以及前两篇文章中的课程管理模块,班级管理模块,学生管理模块,不难发现,每一个模块的开发都是由列表DataGrid,文本框TextBox,下拉框Combox,单选按钮RadioButton,按钮Button等组成的,虽功能略有差异,但总归万变不离其宗。开发方法也大同小异,复杂的功能都是普通的功能累加起来的。这也是本系列文章由浅入深的渐进安排。希望能够抛砖引玉,不局限于某一功能,而是能够举一反三,自我理解,以达到自我开发的能力。

    至此,整个学生信息管理系统系列已完毕。

     

    关于源码

    关于源码下载,可点击CSDN上的链接 或者关注个人公众号【老码识途】进行下载,如下所示:

    编辑 

  • 相关阅读:
    Corn Fields
    状压DP
    全排列函数
    搜索
    前缀和与差分
    最小花费
    【Lintcode】062.Search in Rotated Sorted Array
    【LeetCode】039. Combination Sum
    【LeetCode】040. Combination Sum II
    【LeetCode】047. Permutations II
  • 原文地址:https://www.cnblogs.com/hsiang/p/16344601.html
Copyright © 2020-2023  润新知