WPF开发学生信息管理系统【WPF+Prism+MAH+WebApi】(四)
最近通过WPF开发项目,为了对WPF知识点进行总结,所以利用业余时间,开发一个学生信息管理系统【Student Information Management System】。前三篇文章进行了框架搭建和模块划分,后台WebApi接口编写,以及课程管理模块开发,本文在前三篇基础之上,继续深入开发学生信息管理系统的班级管理和学生管理模块,通过本篇文章,将继续巩固之前的知识点,本文仅供学习分享使用,如有不足之处,还请指正。
涉及知识点
由于班级管理和学生管理的服务端开发,在第二篇文章中以后介绍,所以本篇专注介绍客户端功能的开发。涉及知识点如下:
- WPF开发中TextBlock,TextBox,DataGrid,Combox等控件的基础使用以及数据绑定等操作。
- MAH样式的使用,在本示例中MAH主要用于统一页面风格,提高用户体验。
- HttpClient使用,主要用于访问服务端提供的接口。
业务逻辑
首先班级管理和学生管理既有关联,又相互独立,不像课程管理模块独立存在,不与其他模块存在依赖。所以两个模块一起放在一篇文章进行讲解。关系如下:
- 学生属于某一班级之学生,所以学生中包含班级信息。
- 班级中存在班长,班长又属于学生的一个实体。
班级管理
1. 接口访问类ClassesHttpUtil
班级数据表结构和服务接口,在第二篇文章中已有介绍,如有疑问,可前往参考。接口访问类用于封装访问服务端提供的接口。如下所示:
1 namespace SIMS.Utils.Http 2 { 3 public class ClassesHttpUtil:HttpUtil 4 { 5 ///6 /// 通过id查询学生信息 7 /// 8 /// 9 /// 10 public static ClassesEntity GetClasses(int id) 11 { 12 Dictionary<string, object> data = new Dictionary<string, object>(); 13 data["id"] = id; 14 var str = Get(UrlConfig.CLASSES_GETCLASSES, data); 15 var classes = StrToObject (str); 16 return classes; 17 } 18 19 public static PagedRequest GetClassess(string? dept, string? grade, int pageNum, int pageSize) 20 { 21 Dictionary<string, object> data = new Dictionary<string, object>(); 22 data["dept"] = dept; 23 data["grade"] = grade; 24 data["pageNum"] = pageNum; 25 data["pageSize"] = pageSize; 26 var str = Get(UrlConfig.CLASSES_GETCLASSESS, data); 27 var classess = StrToObject >(str); 28 return classess; 29 } 30 31 public static bool AddClasses(ClassesEntity classes) { 32 var ret = Post (UrlConfig.CLASSES_ADDCLASSES, classes); 33 return int.Parse(ret)==0; 34 } 35 36 public static bool UpdateClasses(ClassesEntity classes) { 37 var ret = Put (UrlConfig.CLASSES_UPDATECLASSES, classes); 38 return int.Parse(ret) == 0; 39 } 40 41 public static bool DeleteClasses(int Id) 42 { 43 Dictionary<string, string> data = new Dictionary<string, string>(); 44 data["Id"] = Id.ToString(); 45 var ret = Delete(UrlConfig.CLASSES_DELETECLASSES, data); 46 return int.Parse(ret) == 0; 47 } 48 } 49 }
2. 客户端页面视图
班级管理的客户端页面视图共两个,一个查询列表页面,一个新增编辑页面,共同组成了班级管理的增删改查。
查询班级列表页面,涉及知识点如下所示:
- 查询条件或者列表中数据列展示,通过数据绑定Binding的方式与ViewModel进行交互,即点击查询按钮,不再传递参数,因为ViewModel中的属性已经同步更新。
- ViewModel中并非所有属性都可实现双向绑定,必须是实现具有通知功能的属性才可以。在Prism框架中,通过BindableBase的SetProperty方法可以快速实现。
- View视图中如果控件存在Command命令,则可以直接绑定,如果不存在,则可以i:Interaction.Triggers将事件转换为命令,如Load事件等。
- 在列表中,如果需要添加按钮,可以通过DataTemplate进行数据定制。
查询班级页面代码,如下所示:
1 <UserControl x:Class="SIMS.ClassesModule.Views.Classes" 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.ClassesModule.Views" 7 xmlns:prism="http://prismlibrary.com/" 8 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 9 xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls" 10 xmlns:ctrls ="clr-namespace:SIMS.Utils.Controls;assembly=SIMS.Utils" 11 prism:ViewModelLocator.AutoWireViewModel="True" 12 mc:Ignorable="d" 13 d:DesignHeight="450" d:DesignWidth="800"> 14 <UserControl.Resources> 15 <ResourceDictionary> 16 <ResourceDictionary.MergedDictionaries> 17 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" /> 18 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" /> 19 <ResourceDictionary> 20 <Style x:Key="LinkButton" TargetType="Button"> 21 <Setter Property="Background" Value="White">Setter> 22 <Setter Property="Cursor" Value="Hand">Setter> 23 <Setter Property="Margin" Value="3">Setter> 24 <Setter Property="MinWidth" Value="80">Setter> 25 <Setter Property="MinHeight" Value="25">Setter> 26 <Setter Property="BorderThickness" Value="0 0 0 0">Setter> 27 Style> 28 ResourceDictionary> 29 ResourceDictionary.MergedDictionaries> 30 ResourceDictionary> 31 UserControl.Resources> 32 <i:Interaction.Triggers> 33 <i:EventTrigger EventName="Loaded"> 34 <i:InvokeCommandAction Command="{Binding LoadedCommand}">i:InvokeCommandAction> 35 i:EventTrigger> 36 i:Interaction.Triggers> 37 <Grid> 38 <Grid.RowDefinitions> 39 <RowDefinition Height="Auto">RowDefinition> 40 <RowDefinition Height="Auto">RowDefinition> 41 <RowDefinition Height="*">RowDefinition> 42 <RowDefinition Height="Auto">RowDefinition> 43 Grid.RowDefinitions> 44 <TextBlock Text="班级信息" FontSize="20" Background="AliceBlue" Margin="2">TextBlock> 45 <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center"> 46 <TextBlock Text="专业" VerticalAlignment="Center" Margin="2">TextBlock> 47 <TextBox Margin="4" MinWidth="120" Height="30" 48 Text="{Binding Dept}" 49 HorizontalContentAlignment="Stretch" 50 mahApps:TextBoxHelper.ClearTextButton="True" 51 mahApps:TextBoxHelper.Watermark="专业" 52 mahApps:TextBoxHelper.WatermarkAlignment="Left" 53 SpellCheck.IsEnabled="True" /> 54 <TextBlock Text="年级" VerticalAlignment="Center" Margin="2">TextBlock> 55 <TextBox Margin="4" MinWidth="120" Height="30" 56 Text="{Binding Grade}" 57 HorizontalContentAlignment="Stretch" 58 mahApps:TextBoxHelper.ClearTextButton="True" 59 mahApps:TextBoxHelper.Watermark="年级" 60 mahApps:TextBoxHelper.WatermarkAlignment="Left" 61 SpellCheck.IsEnabled="True" /> 62 <Button Content="查询" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Width="120" Height="30" Margin="3" Command="{Binding QueryCommand}">Button> 63 <Button Content="新增" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Width="120" Height="30" Margin="3" Command="{Binding AddCommand}">Button> 64 StackPanel> 65 <DataGrid x:Name="dgClasses" 66 Grid.Row="2" 67 Grid.Column="0" 68 Margin="2" 69 AutoGenerateColumns="False" 70 CanUserAddRows="False" 71 CanUserDeleteRows="False" 72 ItemsSource="{Binding Classes}" 73 RowHeaderWidth="0"> 74 <DataGrid.Columns> 75 <DataGridTextColumn Binding="{Binding Dept}" Header="专业" Width="*" /> 76 <DataGridTextColumn Binding="{Binding Grade}" Header="年级" Width="*"/> 77 <DataGridTextColumn Binding="{Binding Name}" Header="班级" Width="*"/> 78 <DataGridTextColumn Binding="{Binding HeadTeacher}" Header="班主任" Width="*"/> 79 <DataGridTextColumn Binding="{Binding MonitorName}" Header="班长" Width="*" /> 80 <DataGridTemplateColumn Header="操作" Width="*"> 81 <DataGridTemplateColumn.CellTemplate> 82 <DataTemplate> 83 <StackPanel Orientation="Horizontal"> 84 <Button Content="Edit" Style="{StaticResource LinkButton}" Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}, Path=DataContext.EditCommand}" CommandParameter="{Binding Id}"> 85 <Button.Template> 86 <ControlTemplate TargetType="Button"> 87 <TextBlock TextDecorations="Underline" HorizontalAlignment="Center"> 88 <ContentPresenter /> 89 TextBlock> 90 ControlTemplate> 91 Button.Template> 92 Button> 93 <Button Content="Delete" Style="{StaticResource LinkButton}" Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}, Path=DataContext.DeleteCommand}" CommandParameter="{Binding Id}"> 94 <Button.Template> 95 <ControlTemplate TargetType="Button"> 96 <TextBlock TextDecorations="Underline" HorizontalAlignment="Center"> 97 <ContentPresenter /> 98 TextBlock> 99 ControlTemplate> 100 Button.Template> 101 Button> 102 StackPanel> 103 DataTemplate> 104 DataGridTemplateColumn.CellTemplate> 105 DataGridTemplateColumn> 106 DataGrid.Columns> 107 DataGrid> 108 <ctrls:PageControl Grid.Row="3" DataContext="{Binding}" >ctrls:PageControl> 109 Grid> 110 UserControl>
新增编辑页面
班级的新增功能和编辑功能,共用一个页面,涉及知识点如下所示:
- 在新增编辑的ViewModel中,班级实体是一个属性,所以在视图控件中进行数据绑定时,需要带上属性名,如:Classes.Name 。
- 班长列表在新增班级时尚无对应学生,可为空,待维护学生后,可通过学生列表进行选择即可。
- 班长列表为Combox下拉框,绑定的是学生实体列表,但客户端只需看到学生姓名即可,所以需要重写DataTemplate,只显示学生姓名。
新增班级信息视图,具体代码如下所示:
1 <UserControl x:Class="SIMS.ClassesModule.Views.AddEditClasses" 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.ClassesModule.Views" 7 mc:Ignorable="d" 8 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 9 xmlns:mahApps ="http://metro.mahapps.com/winfx/xaml/controls" 10 xmlns:prism="http://prismlibrary.com/" 11 d:DesignHeight="400" d:DesignWidth="600"> 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 <RowDefinition>RowDefinition> 44 <RowDefinition>RowDefinition> 45 <RowDefinition>RowDefinition> 46 Grid.RowDefinitions> 47 48 49 <TextBlock Text="专业" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 50 <TextBox Grid.Row="0" Grid.Column="2" MinWidth="120" Height="35" VerticalAlignment="Center" Margin="3" Text="{Binding Classes.Dept}">TextBox> 51 <TextBlock Text="年级" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 52 <TextBox Grid.Row="1" Grid.Column="2" MinWidth="120" Height="35" VerticalAlignment="Center" Margin="3" Text="{Binding Classes.Grade}">TextBox> 53 <TextBlock Text="班级" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 54 <TextBox Grid.Row="2" Grid.Column="2" MinWidth="120" Height="35" VerticalAlignment="Center" Margin="3" Text="{Binding Classes.Name}">TextBox> 55 <TextBlock Text="班主任" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 56 <TextBox Grid.Row="3" Grid.Column="2" MinWidth="120" Height="35" VerticalAlignment="Center" Margin="3" Text="{Binding Classes.HeadTeacher}">TextBox> 57 <TextBlock Text="班长" Grid.Row="4" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 58 <ComboBox Grid.Row="4" Grid.Column="2" MinWidth="120" Height="35" ItemsSource="{Binding Monitors}" mahApps:TextBoxHelper.ClearTextButton="True" SelectedItem="{Binding Monitor}"> 59 <ComboBox.ItemTemplate> 60 <DataTemplate> 61 <TextBlock Text="{Binding Name}">TextBlock> 62 DataTemplate> 63 ComboBox.ItemTemplate> 64 ComboBox> 65 <StackPanel Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="3"> 66 <Button Content="取消" Margin="5" MinWidth="120" Height="35" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Command="{Binding CancelCommand}">Button> 67 <Button Content="保存" Margin="5" MinWidth="120" Height="35" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Command="{Binding SaveCommand}">Button> 68 StackPanel> 69 <TextBlock Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="2" Text="注意:新增班级时,班长可空,等维护学生后,再设置班长。" Foreground="Red">TextBlock> 70 Grid> 71 UserControl>
3. 客户端ViewModel
班级管理模块,ViewModel和视图对应,也分为查询列表ViewModel和新增编辑ViewModel,如下所示:
ClassesViewModel页面代码分为三部分:
- 属性和构造函数,主要用于数据绑定,如查询条件,列表等。所有属性的set赋值时,均采用SetProperty进行赋值。
- 命令Command,如查询,新增,编辑,删除命令等。所有命令可以定义成DelegateCommand类型。
- 分页部分,因分页功能代码大同小异,所以此处略去。
ClassesViewModel具体代码如下所示:
1 namespace SIMS.ClassesModule.ViewModels 2 { 3 public class ClassesViewModel :BindableBase 4 { 5 #region 属性及构造函数 6 7 ///8 /// 专业 9 /// 10 private string dept; 11 12 public string Dept 13 { 14 get { return dept; } 15 set { SetProperty(ref dept , value); } 16 } 17 18 /// 19 /// 年级 20 /// 21 private string grade; 22 23 public string Grade 24 { 25 get { return grade; } 26 set { SetProperty(ref grade , value); } 27 } 28 29 30 31 private ObservableCollection classes; 32 33 public ObservableCollection Classes 34 { 35 get { return classes; } 36 set { SetProperty(ref classes, value); } 37 } 38 39 private IDialogService dialogService; 40 41 public ClassesViewModel(IDialogService dialogService) 42 { 43 this.dialogService = dialogService; 44 this.pageNum = 1; 45 this.pageSize = 20; 46 } 47 48 private void InitInfo() 49 { 50 Classes = new ObservableCollection (); 51 var pagedRequst = ClassesHttpUtil.GetClassess(this.Dept,this.Grade, this.pageNum, this.pageSize); 52 var entities = pagedRequst.items; 53 Classes.AddRange(entities.Select(r=>new ClassesInfo(r))); 54 // 55 this.TotalCount = pagedRequst.count; 56 this.TotalPage = ((int)Math.Ceiling(this.TotalCount * 1.0 / this.pageSize)); 57 } 58 59 #endregion 60 61 #region 事件 62 63 private DelegateCommand loadedCommand; 64 65 public DelegateCommand LoadedCommand 66 { 67 get 68 { 69 if (loadedCommand == null) 70 { 71 loadedCommand = new DelegateCommand(Loaded); 72 } 73 return loadedCommand; 74 } 75 } 76 77 private void Loaded() 78 { 79 InitInfo(); 80 } 81 82 private DelegateCommand queryCommand; 83 84 public DelegateCommand QueryCommand 85 { 86 get 87 { 88 if (queryCommand == null) 89 { 90 queryCommand = new DelegateCommand(Query); 91 } 92 return queryCommand; 93 } 94 } 95 96 private void Query() { 97 this.pageNum = 1; 98 this.InitInfo(); 99 } 100 101 /// 102 /// 新增命令 103 /// 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("addEditClasses",null, AddEditCallBack, "MetroDialogWindow"); 121 } 122 123 private void AddEditCallBack(IDialogResult dialogResult) { 124 if (dialogResult != null && dialogResult.Result == ButtonResult.OK) { 125 //刷新列表 126 this.pageNum = 1; 127 this.InitInfo(); 128 } 129 } 130 131 /// 132 /// 编辑命令 133 /// 134 private DelegateCommand<object> editCommand; 135 136 public DelegateCommand<object> EditCommand 137 { 138 get 139 { 140 if (editCommand == null) 141 { 142 editCommand = new DelegateCommand<object>(Edit); 143 } 144 return editCommand; 145 } 146 } 147 148 private void Edit(object obj) 149 { 150 if (obj == null) { 151 return; 152 } 153 var Id = int.Parse(obj.ToString()); 154 var classes = this.Classes.FirstOrDefault(r => r.Id == Id); 155 if (classes == null) 156 { 157 MessageBox.Show("无效的班级ID"); 158 return; 159 } 160 if (MessageBoxResult.Yes != MessageBox.Show("Are you sure to delete?", "Confirm", MessageBoxButton.YesNo)) 161 { 162 return; 163 } 164 IDialogParameters dialogParameters = new DialogParameters(); 165 dialogParameters.Add("classes",classes); 166 this.dialogService.ShowDialog("addEditClasses", dialogParameters, AddEditCallBack, "MetroDialogWindow"); 167 } 168 169 /// 170 /// 编辑命令 171 /// 172 private DelegateCommand<object> deleteCommand; 173 174 public DelegateCommand<object> DeleteCommand 175 { 176 get 177 { 178 if (deleteCommand == null) 179 { 180 deleteCommand = new DelegateCommand<object>(Delete); 181 } 182 return deleteCommand; 183 } 184 } 185 186 private void Delete(object obj) 187 { 188 if (obj == null) 189 { 190 return; 191 } 192 var Id = int.Parse(obj.ToString()); 193 var classes = this.Classes.FirstOrDefault(r => r.Id == Id); 194 if (classes == null) 195 { 196 MessageBox.Show("无效的班级ID"); 197 return; 198 } 199 bool flag = ClassesHttpUtil.DeleteClasses(Id); 200 if (flag) { 201 this.pageNum = 1; 202 this.InitInfo(); 203 } 204 } 205 206 #endregion 207 208 } 209 }
AddEditClassesViewModel代码同样分为三部分:
- 属性和构造函数,主要用于数据绑定,如页面文本框,下拉选择框等。
- 命令Command,主要用于响应事件,如保存,取消等。
- 对话框接口,因为新增编辑是以弹出框的形式呈现,所以根据Prism框架的 要求,需要实现IDialogAware接口。
AddEditClassesViewModel具体代码如下所示:
1 namespace SIMS.ClassesModule.ViewModels 2 { 3 public class AddEditClassesViewModel : BindableBase, IDialogAware 4 { 5 #region 属性和构造函数 6 7 ///8 /// 班级实体 9 /// 10 private ClassesInfo classes; 11 12 public ClassesInfo Classes 13 { 14 get { return classes; } 15 set { SetProperty(ref classes ,value); } 16 } 17 18 private List monitors; 19 20 public List Monitors 21 { 22 get { return monitors; } 23 set { SetProperty(ref monitors , value); } 24 } 25 26 private StudentEntity monitor; 27 28 public StudentEntity Monitor 29 { 30 get { return monitor; } 31 set { SetProperty(ref monitor, value); } 32 } 33 34 public AddEditClassesViewModel() { 35 36 } 37 38 #endregion 39 40 #region Command 41 42 private DelegateCommand loadedCommand; 43 44 public DelegateCommand LoadedCommand 45 { 46 get 47 { 48 if (loadedCommand == null) 49 { 50 loadedCommand = new DelegateCommand(Loaded); 51 } 52 return loadedCommand; 53 } 54 } 55 56 private void Loaded() 57 { 58 this.Monitors= new List (); 59 if (Classes?.Id>0) { 60 var pagedRequst = StudentHttpUtil.GetStudentsByClasses(Classes.Id); 61 var entities = pagedRequst.items; 62 Monitors.AddRange(entities); 63 //如果有班长,则为班长赋值 64 if (Classes.Monitor > 0) { 65 this.Monitor= this.Monitors?.FirstOrDefault(r=>r.Id==Classes.Monitor); 66 } 67 } 68 69 } 70 71 private DelegateCommand cancelCommand; 72 73 public DelegateCommand CancelCommand 74 { 75 get 76 { 77 if (cancelCommand == null) 78 { 79 cancelCommand = new DelegateCommand(Cancel); 80 } 81 return cancelCommand; 82 } 83 } 84 85 private void Cancel() 86 { 87 RequestClose?.Invoke((new DialogResult(ButtonResult.Cancel))); 88 } 89 90 private DelegateCommand saveCommand; 91 92 public DelegateCommand SaveCommand 93 { 94 get 95 { 96 if (saveCommand == null) 97 { 98 saveCommand = new DelegateCommand(Save); 99 } 100 return saveCommand; 101 } 102 } 103 104 private void Save() { 105 if (Classes != null) { 106 Classes.CreateTime = DateTime.Now; 107 Classes.LastEditTime = DateTime.Now; 108 if (Monitor != null) { 109 Classes.Monitor = Monitor.Id; 110 } 111 bool flag=false; 112 if (Classes.Id > 0) { 113 flag = ClassesHttpUtil.UpdateClasses(Classes); 114 } 115 else { 116 flag = ClassesHttpUtil.AddClasses(Classes); 117 } 118 if (flag) 119 { 120 RequestClose?.Invoke((new DialogResult(ButtonResult.OK))); 121 } 122 } 123 } 124 125 126 #endregion 127 128 #region 对话框 129 130 public string Title => "新增或编辑班级信息"; 131 132 public event Action RequestClose; 133 134 public bool CanCloseDialog() 135 { 136 return true; 137 } 138 139 public void OnDialogClosed() 140 { 141 142 } 143 144 public void OnDialogOpened(IDialogParameters parameters) 145 { 146 if (parameters != null && parameters.ContainsKey("classes")) 147 { 148 this.Classes = parameters.GetValue ("classes"); 149 } 150 else { 151 this.Classes = new ClassesInfo(); 152 } 153 } 154 155 #endregion 156 157 } 158 }
4. 示例截图
班级管理示例截图,如下所示:
学生管理
1. 接口访问类StudentHttpUtil
学生数据表结构和服务接口,在第二篇文章中已有介绍,如有疑问,可前往参考。接口访问类用于封装访问服务端提供的接口。如下所示:
1 namespace SIMS.Utils.Http 2 { 3 ///4 /// 学生类Http访问通用类 5 /// 6 public class StudentHttpUtil:HttpUtil 7 { 8 /// 9 /// 通过id查询学生信息 10 /// 11 /// 12 /// 13 public static StudentEntity GetStudent(int id) 14 { 15 Dictionary<string, object> data = new Dictionary<string, object>(); 16 data["id"] = id; 17 var str = Get(UrlConfig.STUDENT_GETSTUDENT, data); 18 var student = StrToObject (str); 19 return student; 20 } 21 22 public static PagedRequest GetStudents(string no,string name, int pageNum, int pageSize) { 23 Dictionary<string, object> data = new Dictionary<string, object>(); 24 data["no"] = no; 25 data["name"] = name; 26 data["pageNum"] = pageNum; 27 data["pageSize"] = pageSize; 28 var str = Get(UrlConfig.STUDENT_GETSTUDENTS, data); 29 var students = StrToObject >(str); 30 return students; 31 } 32 33 public static PagedRequest GetStudentsByClasses(int classId) 34 { 35 Dictionary<string, object> data = new Dictionary<string, object>(); 36 data["classId"] = classId; 37 var str = Get(UrlConfig.STUDENT_GETSTUDENTSBYCLASSES, data); 38 var students = StrToObject >(str); 39 return students; 40 } 41 42 public static bool AddStudent(StudentEntity student) 43 { 44 var ret = Post (UrlConfig.STUDENT_ADDSTUDENT, student); 45 return int.Parse(ret) == 0; 46 } 47 48 public static bool UpdateStudent(StudentEntity student) 49 { 50 var ret = Put (UrlConfig.STUDENT_UPDATESTUDENT, student); 51 return int.Parse(ret) == 0; 52 } 53 54 public static bool DeleteStudent(int Id) 55 { 56 Dictionary<string, string> data = new Dictionary<string, string>(); 57 data["Id"] = Id.ToString(); 58 var ret = Delete(UrlConfig.STUDENT_DELETESTUDENT, data); 59 return int.Parse(ret) == 0; 60 } 61 } 62 }
2. 客户端页面视图
学生管理模块的客户端视图页面,也分为两个:学生列表查询,新增编辑学生信息页面。
学生列表查询页面
学生信息列表页面设计知识点,和班级管理差不多,只有一点差异:
- 在数据库中,性别保存的是bit类型,在C#中,数据类型为bool,但是在列表中需要转换成男或女显示,所以需要用到Converter进行转换。
学生信息查询页面,具体代码如下所示:
1 <UserControl x:Class="SIMS.StudentModule.Views.Student" 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.StudentModule.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 xmlns:ctrls ="clr-namespace:SIMS.Utils.Controls;assembly=SIMS.Utils" 11 xmlns:conv="clr-namespace:SIMS.Utils.Converter;assembly=SIMS.Utils" 12 mc:Ignorable="d" 13 prism:ViewModelLocator.AutoWireViewModel="True" 14 d:DesignHeight="450" d:DesignWidth="800"> 15 16 <UserControl.Resources> 17 <ResourceDictionary> 18 <ResourceDictionary.MergedDictionaries> 19 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" /> 20 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" /> 21 <ResourceDictionary> 22 <conv:SexConverter x:Key="SexConverter">conv:SexConverter> 23 <Style x:Key="LinkButton" TargetType="Button"> 24 <Setter Property="Background" Value="White">Setter> 25 <Setter Property="Cursor" Value="Hand">Setter> 26 <Setter Property="Margin" Value="3">Setter> 27 <Setter Property="MinWidth" Value="80">Setter> 28 <Setter Property="MinHeight" Value="25">Setter> 29 <Setter Property="BorderThickness" Value="0 0 0 0">Setter> 30 Style> 31 ResourceDictionary> 32 ResourceDictionary.MergedDictionaries> 33 ResourceDictionary> 34 UserControl.Resources> 35 <i:Interaction.Triggers> 36 <i:EventTrigger EventName="Loaded"> 37 <i:InvokeCommandAction Command="{Binding LoadedCommand}">i:InvokeCommandAction> 38 i:EventTrigger> 39 i:Interaction.Triggers> 40 <Grid> 41 <Grid.RowDefinitions> 42 <RowDefinition Height="Auto">RowDefinition> 43 <RowDefinition Height="Auto">RowDefinition> 44 <RowDefinition Height="*">RowDefinition> 45 <RowDefinition Height="Auto">RowDefinition> 46 Grid.RowDefinitions> 47 <TextBlock Text="学生信息" FontSize="20" Background="AliceBlue" Margin="2">TextBlock> 48 <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center"> 49 <TextBlock Text="学号" VerticalAlignment="Center" Margin="2">TextBlock> 50 <TextBox Margin="4" MinWidth="120" Height="30" 51 Text="{Binding No}" 52 HorizontalContentAlignment="Stretch" 53 mahApps:TextBoxHelper.ClearTextButton="True" 54 mahApps:TextBoxHelper.Watermark="学号" 55 mahApps:TextBoxHelper.WatermarkAlignment="Left" 56 SpellCheck.IsEnabled="True" /> 57 <TextBlock Text="姓名" VerticalAlignment="Center" Margin="2">TextBlock> 58 <TextBox Margin="4" MinWidth="120" Height="30" 59 Text="{Binding Name}" 60 HorizontalContentAlignment="Stretch" 61 mahApps:TextBoxHelper.ClearTextButton="True" 62 mahApps:TextBoxHelper.Watermark="学生姓名" 63 mahApps:TextBoxHelper.WatermarkAlignment="Left" 64 SpellCheck.IsEnabled="True" /> 65 <Button Content="查询" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Width="120" Height="30" Margin="3" Command="{Binding QueryCommand}">Button> 66 <Button Content="新增" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Width="120" Height="30" Margin="3" Command="{Binding AddCommand}">Button> 67 StackPanel> 68 <DataGrid x:Name="dgStudent" 69 Grid.Row="2" 70 Grid.Column="0" 71 Margin="2" 72 AutoGenerateColumns="False" 73 CanUserAddRows="False" 74 CanUserDeleteRows="False" 75 ItemsSource="{Binding Students}" 76 RowHeaderWidth="0"> 77 <DataGrid.Columns> 78 <DataGridTextColumn Binding="{Binding No}" Header="学号" Width="*" /> 79 <DataGridTextColumn Binding="{Binding Name}" Header="姓名" Width="*"/> 80 <DataGridTextColumn Binding="{Binding Age}" Header="年龄" Width="*"/> 81 <DataGridTextColumn Binding="{Binding Sex, Converter={StaticResource SexConverter}}" Header="性别" Width="*"/> 82 <DataGridTextColumn Binding="{Binding ClassesName}" Header="班级" Width="*" /> 83 <DataGridTemplateColumn Header="操作" Width="*"> 84 <DataGridTemplateColumn.CellTemplate> 85 <DataTemplate> 86 <StackPanel Orientation="Horizontal"> 87 <Button Content="Edit" Style="{StaticResource LinkButton}" Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}, Path=DataContext.EditCommand}" CommandParameter="{Binding Id}" > 88 <Button.Template> 89 <ControlTemplate TargetType="Button"> 90 <TextBlock TextDecorations="Underline" HorizontalAlignment="Center"> 91 <ContentPresenter /> 92 TextBlock> 93 ControlTemplate> 94 Button.Template> 95 Button> 96 <Button Content="Delete" Style="{StaticResource LinkButton}" Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}, Path=DataContext.DeleteCommand}" CommandParameter="{Binding Id}"> 97 <Button.Template> 98 <ControlTemplate TargetType="Button"> 99 <TextBlock TextDecorations="Underline" HorizontalAlignment="Center"> 100 <ContentPresenter /> 101 TextBlock> 102 ControlTemplate> 103 Button.Template> 104 Button> 105 StackPanel> 106 DataTemplate> 107 DataGridTemplateColumn.CellTemplate> 108 DataGridTemplateColumn> 109 DataGrid.Columns> 110 DataGrid> 111 <ctrls:PageControl Grid.Row="3" DataContext="{Binding}" >ctrls:PageControl> 112 Grid> 113 UserControl>
新增编辑页面
新增编辑页面涉及知识点和班级新增页面差不多,仅有一处差异:
- C#中的bool类型,需要绑定到两个单选按钮上,以表示男,女,所以需要扩展。
新增编辑页面,具体代码如下所示:
1 <UserControl x:Class="SIMS.StudentModule.Views.AddEditStudent" 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.StudentModule.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="600"> 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 <RowDefinition>RowDefinition> 44 <RowDefinition>RowDefinition> 45 Grid.RowDefinitions> 46 47 48 <TextBlock Text="学号" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 49 <TextBox Grid.Row="0" Grid.Column="2" MinWidth="120" Height="35" VerticalAlignment="Center" Margin="3" Text="{Binding Student.No}">TextBox> 50 <TextBlock Text="姓名" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 51 <TextBox Grid.Row="1" Grid.Column="2" MinWidth="120" Height="35" VerticalAlignment="Center" Margin="3" Text="{Binding Student.Name}">TextBox> 52 <TextBlock Text="年龄" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 53 <TextBox Grid.Row="2" Grid.Column="2" MinWidth="120" Height="35" VerticalAlignment="Center" Margin="3" Text="{Binding Student.Age}">TextBox> 54 <TextBlock Text="性别" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 55 <StackPanel Grid.Row="3" Grid.Column="2" Orientation="Horizontal" > 56 <RadioButton Content="男" IsChecked="{Binding Student.IsBoy}">RadioButton> 57 <RadioButton Content="女" IsChecked="{Binding Student.IsGirl}">RadioButton> 58 StackPanel> 59 60 <TextBlock Text="班级" Grid.Row="4" Grid.Column="1" VerticalAlignment="Center" Margin="3">TextBlock> 61 <ComboBox Grid.Row="4" Grid.Column="2" MinWidth="120" Height="35" ItemsSource="{Binding Classess}" mahApps:TextBoxHelper.ClearTextButton="True" SelectedItem="{Binding Classes}"> 62 <ComboBox.ItemTemplate> 63 <DataTemplate> 64 <StackPanel Orientation="Horizontal"> 65 <TextBlock Text="{Binding Dept}">TextBlock> 66 <TextBlock Text="{Binding Grade}">TextBlock> 67 <TextBlock Text="{Binding Name}">TextBlock> 68 StackPanel> 69 DataTemplate> 70 ComboBox.ItemTemplate> 71 ComboBox> 72 <StackPanel Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="3"> 73 <Button Content="取消" Margin="5" MinWidth="120" Height="35" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Command="{Binding CancelCommand}" >Button> 74 <Button Content="保存" Margin="5" MinWidth="120" Height="35" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Command="{Binding SaveCommand}">Button> 75 StackPanel> 76 Grid> 77 UserControl>
3. 客户端ViewModel
学生管理客户端ViewModel,与页面视图对应,也分为两个部分
StudentViewModel代码分为三部分:
- 属性和构造函数,主要用于数据绑定,如查询条件,列表等。所有属性的set赋值时,均采用SetProperty进行赋值。
- 命令Command,如查询,新增,编辑,删除命令等。所有命令可以定义成DelegateCommand类型。
- 分页部分,因分页功能代码大同小异,所以此处略去。
StudentViewModel具体代码如下所示:
1 namespace SIMS.StudentModule.ViewModels 2 { 3 public class StudentViewModel:BindableBase 4 { 5 #region 属性及构造函数 6 7 ///8 /// 学号 9 /// 10 private string no; 11 12 public string No 13 { 14 get { return no; } 15 set { SetProperty(ref no , value); } 16 } 17 18 /// 19 /// 学生姓名 20 /// 21 private string name; 22 23 public string Name 24 { 25 get { return name; } 26 set { SetProperty(ref name, value); } 27 } 28 29 private ObservableCollection students; 30 31 public ObservableCollection Students 32 { 33 get { return students; } 34 set { SetProperty(ref students, value); } 35 } 36 37 private IDialogService dialogService; 38 39 public StudentViewModel(IDialogService dialogService) { 40 this.dialogService = dialogService; 41 this.pageNum = 1; 42 this.pageSize = 20; 43 } 44 45 private void InitInfo() { 46 Students = new ObservableCollection (); 47 var pagedRequst = StudentHttpUtil.GetStudents(this.No,this.Name, this.pageNum, this.pageSize); 48 var entities = pagedRequst.items; 49 Students.AddRange(entities.Select(r=>new StudentInfo(r))); 50 // 51 this.TotalCount = pagedRequst.count; 52 this.TotalPage=((int)Math.Ceiling(this.TotalCount*1.0/this.pageSize)); 53 } 54 55 #endregion 56 57 #region 事件 58 59 private DelegateCommand loadedCommand; 60 61 public DelegateCommand LoadedCommand 62 { 63 get 64 { 65 if (loadedCommand == null) 66 { 67 loadedCommand = new DelegateCommand(Loaded); 68 } 69 return loadedCommand; 70 } 71 } 72 73 private void Loaded() 74 { 75 InitInfo(); 76 } 77 78 /// 79 /// 新增命令 80 /// 81 private DelegateCommand addCommand; 82 83 public DelegateCommand AddCommand 84 { 85 get 86 { 87 if (addCommand == null) 88 { 89 addCommand = new DelegateCommand(Add); 90 } 91 return addCommand; 92 } 93 } 94 95 private void Add() 96 { 97 this.dialogService.ShowDialog("addEditStudent", null, AddEditCallBack, "MetroDialogWindow"); 98 } 99 100 private void AddEditCallBack(IDialogResult dialogResult) 101 { 102 if (dialogResult != null && dialogResult.Result == ButtonResult.OK) 103 { 104 //刷新列表 105 this.pageNum = 1; 106 this.InitInfo(); 107 } 108 } 109 110 /// 111 /// 编辑命令 112 /// 113 private DelegateCommand<object> editCommand; 114 115 public DelegateCommand<object> EditCommand 116 { 117 get 118 { 119 if (editCommand == null) 120 { 121 editCommand = new DelegateCommand<object>(Edit); 122 } 123 return editCommand; 124 } 125 } 126 127 private void Edit(object obj) { 128 if (obj == null) 129 { 130 return; 131 } 132 var Id = int.Parse(obj.ToString()); 133 var student = this.Students.FirstOrDefault(r => r.Id == Id); 134 if (student == null) 135 { 136 MessageBox.Show("无效的学生ID"); 137 return; 138 } 139 if (MessageBoxResult.Yes != MessageBox.Show("Are you sure to delete?", "Confirm", MessageBoxButton.YesNo)) 140 { 141 return; 142 } 143 IDialogParameters dialogParameters = new DialogParameters(); 144 dialogParameters.Add("student", student); 145 this.dialogService.ShowDialog("addEditStudent", dialogParameters, AddEditCallBack, "MetroDialogWindow"); 146 } 147 148 /// 149 /// 编辑命令 150 /// 151 private DelegateCommand<object> deleteCommand; 152 153 public DelegateCommand<object> DeleteCommand 154 { 155 get 156 { 157 if (deleteCommand == null) 158 { 159 deleteCommand = new DelegateCommand<object>(Delete); 160 } 161 return deleteCommand; 162 } 163 } 164 165 private void Delete(object obj) 166 { 167 if (obj == null) 168 { 169 return; 170 } 171 var Id = int.Parse(obj.ToString()); 172 var classes = this.Students.FirstOrDefault(r => r.Id == Id); 173 if (classes == null) 174 { 175 MessageBox.Show("无效的学生ID"); 176 return; 177 } 178 bool flag = StudentHttpUtil.DeleteStudent(Id); 179 if (flag) 180 { 181 this.pageNum = 1; 182 this.InitInfo(); 183 } 184 } 185 186 #endregion 187 188 } 189 }
AddEditStudentViewModel代码同样分为三部分:
- 属性和构造函数,主要用于数据绑定,如页面文本框,下拉选择框等。
- 命令Command,主要用于响应事件,如保存,取消等。
- 对话框接口,因为新增编辑是以弹出框的形式呈现,所以根据Prism框架的 要求,需要实现IDialogAware接口。(实现接口代码大同小异,在此略去)
AddEditStudentViewModel具体代码如下所示:
1 namespace SIMS.StudentModule.ViewModels 2 { 3 public class AddEditStudentViewModel : BindableBase, IDialogAware 4 { 5 6 ///7 /// 班级实体 8 /// 9 private ClassesEntity classes; 10 11 public ClassesEntity Classes 12 { 13 get { return classes; } 14 set { SetProperty(ref classes, value); } 15 } 16 17 /// 18 /// 班级列表 19 /// 20 private List classess; 21 22 public List Classess 23 { 24 get { return classess; } 25 set { SetProperty(ref classess, value); } 26 } 27 28 private StudentInfo student; 29 30 public StudentInfo Student 31 { 32 get { return student; } 33 set { student = value; } 34 } 35 36 public AddEditStudentViewModel() { 37 38 } 39 40 #region Command 41 42 private DelegateCommand loadedCommand; 43 44 public DelegateCommand LoadedCommand 45 { 46 get 47 { 48 if (loadedCommand == null) 49 { 50 loadedCommand = new DelegateCommand(Loaded); 51 } 52 return loadedCommand; 53 } 54 } 55 56 private void Loaded() 57 { 58 this.Classess = new List (); 59 var pagedRequst = ClassesHttpUtil.GetClassess(null,null,1,0);//0表示所有班级 60 var entities = pagedRequst.items; 61 Classess.AddRange(entities); 62 } 63 64 private DelegateCommand cancelCommand; 65 66 public DelegateCommand CancelCommand 67 { 68 get 69 { 70 if (cancelCommand == null) 71 { 72 cancelCommand = new DelegateCommand(Cancel); 73 } 74 return cancelCommand; 75 } 76 } 77 78 private void Cancel() { 79 RequestClose?.Invoke((new DialogResult(ButtonResult.Cancel))); 80 } 81 82 private DelegateCommand saveCommand; 83 84 public DelegateCommand SaveCommand 85 { 86 get 87 { 88 if (saveCommand == null) 89 { 90 saveCommand = new DelegateCommand(Save); 91 } 92 return saveCommand; 93 } 94 } 95 96 private void Save() 97 { 98 if (Student != null) 99 { 100 Student.CreateTime = DateTime.Now; 101 Student.LastEditTime = DateTime.Now; 102 bool flag = false; 103 if (Classes != null) { 104 Student.ClassesId = Classes.Id; 105 } 106 if (Student.Id > 0) 107 { 108 flag = StudentHttpUtil.UpdateStudent(Student); 109 } 110 else 111 { 112 flag = StudentHttpUtil.AddStudent(Student); 113 } 114 if (flag) 115 { 116 RequestClose?.Invoke((new DialogResult(ButtonResult.OK))); 117 } 118 } 119 } 120 121 #endregion 122 } 123 }
4. 示例截图
学生管理示例截图,如下所示:
总结
通过本篇文章的班级管理模块,学生管理模块,以及上一篇文章中的课程管理模块,不难发现,每一个模块的开发都是由列表DataGrid,文本框TextBox,下拉框Combox,单选按钮RadioButton,按钮Button等组成的,虽功能略有差异,但总归万变不离其宗。开发方法也大同小异,复杂的功能都是普通的功能累加起来的。这也是本系列文章由浅入深的渐进安排。希望能够抛砖引玉,不局限于某一功能,而是能够举一反三,自我理解,以达到自我开发的能力。