- A+
最近通过WPF开发项目,为了对WPF知识点进行总结,所以利用业余时间,开发一个学生信息管理系统【Student Information Management System】。前四篇文章进行了框架搭建和模块划分,后台WebApi接口编写,以及课程管理模块,班级管理模块,学生管理模块的开发,本文在前四篇基础之上,继续深入开发学生信息管理系统的成绩管理和系统管理模块,通过本篇文章,将继续巩固之前的知识点,本文仅供学习分享使用,如有不足之处,还请指正。
涉及知识点
学生信息管理系统SIMS属于一个小型的完整系统开发,涉及的知识点比较,具体如下所示:
- WPF开发中TextBlock,TextBox,DataGrid,Combox,TabControl等控件的基础使用以及数据绑定等操作。
- MAH样式的使用,在本示例中MAH主要用于统一页面风格,提高用户体验。
- HttpClient使用,主要用于访问服务端提供的接口。
业务逻辑
前面几篇文章,由浅入深,逐步介绍了课程管理模块,班级管理模块,学生管理模块,今天继续介绍成绩管理模块,业务逻辑关系如下:
- 学生属于某一班级之学生,所以学生中包含班级信息。
- 班级中存在班长,同时班长又属于学生的一个实体。
- 成绩是某一学生的成绩,且一名学生有各门课程的成绩。所以成绩和学生有关,且和课程有关。
实体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。如下所示:
- Score视图,主要用于成绩的查询,以及新增,修改,删除的链接入口。
- AddEditScore视图,主要用于成绩信息的新增和修改,共用一个视图页面。
- 成绩课程不需要页面,所以没有对应视图。
4.1. Score视图
Score视图,主要是成绩的查询和新增,修改,删除的链接入口。涉及知识点如下:
- Score视图页面布局采用Grid方式和StackPanel混合布局,即整体布局采用Grid,细微布局采用StackPanel。
- 成绩采用分页列表的方式展示,需要用到DataGrid,及分页控件【WPF默认不提供分页控件,可自行编写分页控件】。
- 查询条件采用按钮Button和文本框TextBox等组成,关于基础控件的使用,不再详细论述,可参考其他文章。
- 在本系统的所有WPF视图中,均需要引入Prism和 MAH组件。
- 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上的链接 或者关注个人公众号【老码识途】进行下载,如下所示:
编辑