ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-05-29 19:00:22  阅读:159  来源: 互联网

标签:WebApi DelegateCommand return string int private Prism WPF public


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

涉及知识点

在本篇文章中,虽然只介绍一个模块的开发,但是麻雀虽小,五脏俱全,而且掌握一个模块的开发方法,相当于掌握通用的开发方法,就可以举一反三,其他模块的开发也可以水到渠成。涉及到的知识点如下:

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

数据访问流程

一般情况下,交付给客户的都是可视化的操作页面,而不是一些只有专业开发人员才能看懂的WebApi接口。为了更好的描述数据访问流程,以具体代码模块为例,如下所示:

 

数据操作上下文DataContext

关于WebApi和数据库的相关操作,在上一篇文章中已有说明,本文不再赘述,主要侧重于业务代码。关于课程管理模块的数据操作上下文,有两点需要加以说明:

  1. DbContext 是EntityFramwork提供的用于访问数据库中数据表的类,一个DbContext实例表示一个session,可用于查询或保存数据实体对应数据表。
  2. DbSet表示一个对具体数据表中数据的操作映射,如增加,删除,修改,查询等,都是可通过DbSet类型完成。

关于DataContext具体代码,如下所示:

 1 namespace SIMS.WebApi.Data
 2 {
 3     public class DataContext:DbContext
 4     {
 5         public DbSet<UserEntity> Users { get; set; }
 6 
 7         public DbSet<MenuEntity> Menus { get; set; }
 8 
 9         public DbSet<RoleEntity> Roles { get; set; }
10 
11         public DbSet<UserRoleEntity> UserRoles { get; set; }
12 
13         public DbSet<RoleMenuEntity> RoleMenus { get; set; }
14 
15         /// <summary>
16         /// 学生
17         /// </summary>
18         public DbSet<StudentEntity> Students { get; set; }
19 
20         /// <summary>
21         /// 班级
22         /// </summary>
23         public DbSet<ClassesEntity> Classes { get; set; }
24 
25         /// <summary>
26         /// 课程
27         /// </summary>
28         public DbSet<CourseEntity> Courses { get; set; }
29 
30         /// <summary>
31         /// 成绩
32         /// </summary>
33         public DbSet<ScoreEntity> Scores { get; set; }
34 
35         public DataContext(DbContextOptions options) : base(options)
36         {
37 
38         }
39 
40         protected override void OnModelCreating(ModelBuilder modelBuilder)
41         {
42             base.OnModelCreating(modelBuilder);
43             modelBuilder.Entity<UserEntity>().ToTable("Users");
44             modelBuilder.Entity<MenuEntity>().ToTable("Menus");
45             modelBuilder.Entity<StudentEntity>().ToTable("Students");
46             modelBuilder.Entity<RoleEntity>().ToTable("Roles");
47             modelBuilder.Entity<UserRoleEntity>().ToTable("UserRoles");
48             modelBuilder.Entity<RoleMenuEntity>().ToTable("RoleMenus");
49         }
50     }
51 }

数据服务Service

数据服务是对DataContext操作的封装,使得业务更加明确,当然如果通过控制器直接访问DataContext,省掉数据服务,也可以实现对应功能,只是加入数据服务层,使得结构更加清晰。

数据服务接口ICourseAppService,提供了五个接口,包括查询课程列表,查询当个课程信息,新增课程,修改课程,删除课程,具体如下所示:

 1 namespace SIMS.WebApi.Services.Course
 2 {
 3     public interface ICourseAppService
 4     {
 5         /// <summary>
 6         /// 查询列表
 7         /// </summary>
 8         /// <param name="courseName"></param>
 9         /// <param name="teacher"></param>
10         /// <param name="pageNum"></param>
11         /// <param name="pageSize"></param>
12         /// <returns></returns>
13         public PagedRequest<CourseEntity> GetCourses(string courseName,string teacher, int pageNum,int pageSize);
14 
15         /// <summary>
16         /// 通过id查询课程信息
17         /// </summary>
18         /// <param name="id"></param>
19         /// <returns></returns>
20         public CourseEntity GetCourse(int id);
21 
22         /// <summary>
23         /// 新增课程
24         /// </summary>
25         /// <param name="course"></param>
26         /// <returns></returns>
27         public int AddCourse(CourseEntity course);
28 
29         /// <summary>
30         /// 修改课程
31         /// </summary>
32         /// <param name="course"></param>
33         /// <returns></returns>
34         public int UpdateCourse(CourseEntity course);
35 
36         /// <summary>
37         /// 删除课程
38         /// </summary>
39         /// <param name="id"></param>
40         public int DeleteCourse(int id);
41     }
42 }

数据服务接口实现类CourseAppService,对数据服务接口ICourseAppService的实现,即通过操作DataContext来访问数据库中的课程表,如下所示:

 1 namespace SIMS.WebApi.Services.Course
 2 {
 3     public class CourseAppService : ICourseAppService
 4     {
 5         private DataContext dataContext;
 6 
 7         public CourseAppService(DataContext dataContext)
 8         {
 9             this.dataContext = dataContext;
10         }
11 
12         public int AddCourse(CourseEntity course)
13         {
14             var entry = dataContext.Courses.Add(course);
15             dataContext.SaveChanges();
16             return 0;
17         }
18 
19         public int DeleteCourse(int id)
20         {
21             var entity = dataContext.Courses.FirstOrDefault(x => x.Id == id);
22             if (entity != null)
23             {
24                 dataContext.Courses.Remove(entity);
25                 dataContext.SaveChanges();
26             }
27             return 0;
28         }
29 
30         /// <summary>
31         /// 根据ID获取单个课程
32         /// </summary>
33         /// <param name="id"></param>
34         /// <returns></returns>
35         public CourseEntity GetCourse(int id)
36         {
37             var entity = dataContext.Courses.FirstOrDefault(r => r.Id == id);
38             return entity;
39         }
40 
41         /// <summary>
42         /// 获取课程列表
43         /// </summary>
44         /// <param name="courseName"></param>
45         /// <param name="teacher"></param>
46         /// <param name="pageNum"></param>
47         /// <param name="pageSize"></param>
48         /// <returns></returns>
49         public PagedRequest<CourseEntity> GetCourses(string courseName, string teacher, int pageNum, int pageSize)
50         {
51             IQueryable<CourseEntity> courses = null;
52             if (!string.IsNullOrEmpty(courseName) && !string.IsNullOrEmpty(teacher))
53             {
54                 courses = dataContext.Courses.Where(r => r.Name.Contains(courseName) && r.Teacher.Contains(teacher)).OrderBy(r => r.Id);
55             }
56             else if (!string.IsNullOrEmpty(courseName))
57             {
58                 courses = dataContext.Courses.Where(r => r.Name.Contains(courseName)).OrderBy(r => r.Id);
59             }
60             else if (!string.IsNullOrEmpty(teacher))
61             {
62                 courses = dataContext.Courses.Where(r => r.Teacher.Contains(teacher)).OrderBy(r => r.Id);
63             }
64             else
65             {
66                 courses = dataContext.Courses.Where(r => true).OrderBy(r => r.Id);
67             }
68             int count = courses.Count();
69             List<CourseEntity> items;
70             if (pageSize > 0)
71             {
72                 items = courses.Skip((pageNum - 1) * pageSize).Take(pageSize).ToList();
73             }
74             else
75             {
76                 items = courses.ToList();
77             }
78             return new PagedRequest<CourseEntity>()
79             {
80                 count = count,
81                 items = items
82             };
83         }
84 
85         public int UpdateCourse(CourseEntity course)
86         {
87             dataContext.Courses.Update(course);
88             dataContext.SaveChanges();
89             return 0;
90         }
91     }
92 }

WebApi接口控制器

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

 1 namespace SIMS.WebApi.Controllers
 2 {
 3     /// <summary>
 4     /// 课程控制器
 5     /// </summary>
 6     [Route("api/[controller]/[action]")]
 7     [ApiController]
 8     public class CourseController : ControllerBase
 9     {
10         private readonly ILogger<CourseController> logger;
11 
12         private readonly ICourseAppService courseAppService;
13 
14         public CourseController(ILogger<CourseController> logger, ICourseAppService courseAppService)
15         {
16             this.logger = logger;
17             this.courseAppService = courseAppService;
18         }
19 
20         [HttpGet]
21         public PagedRequest<CourseEntity> GetCourses( int pageNum, int pageSize, string? courseName=null, string? teacher=null) { 
22             return courseAppService.GetCourses(courseName,teacher,pageNum,pageSize);
23         }
24 
25         /// <summary>
26         /// 获取课程信息
27         /// </summary>
28         /// <param name="id"></param>
29         /// <returns></returns>
30         [HttpGet]
31         public CourseEntity GetCourse(int id)
32         {
33             return courseAppService.GetCourse(id);
34         }
35 
36         /// <summary>
37         /// 新增课程
38         /// </summary>
39         /// <param name="course"></param>
40         /// <returns></returns>
41         [HttpPost]
42         public int AddCourse(CourseEntity course)
43         {
44             return courseAppService.AddCourse(course);
45         }
46 
47         /// <summary>
48         /// 修改课程
49         /// </summary>
50         /// <param name="course"></param>
51         /// <returns></returns>
52         [HttpPut]
53         public int UpdateCourse(CourseEntity course)
54         {
55             return courseAppService.UpdateCourse(course);
56         }
57 
58         /// <summary>
59         /// 删除课程
60         /// </summary>
61         /// <param name="id"></param>
62         [HttpDelete]
63         public int DeleteCourse(int id)
64         {
65             return courseAppService.DeleteCourse(id);
66         }
67     }
68 }

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

 

 客户端接口访问类HttpUtil

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

 1 namespace SIMS.Utils.Http
 2 {
 3     /// <summary>
 4     /// HTTP访问基类
 5     /// </summary>
 6     public class HttpUtil
 7     {
 8         private static readonly string absoluteUrl = "https://localhost:7299/";
 9 
10         /// <summary>
11         /// get方式
12         /// </summary>
13         /// <param name="url"></param>
14         /// <param name="param"></param>
15         /// <returns></returns>
16         public static string Get(string url, Dictionary<string, object> param)
17         {
18             string p=string.Empty;  
19             if (param != null && param.Count() > 0) {
20                 foreach (var keypair in param)
21                 {
22                     if (keypair.Value != null)
23                     {
24                         p += $"{keypair.Key}={keypair.Value}&";
25                     }
26                 }
27             }
28             if (!string.IsNullOrEmpty(p)) { 
29                 p=p.TrimEnd('&');
30             }
31             var httpClient = new HttpClient();
32             var response = httpClient.GetAsync($"{absoluteUrl}{url}?{p}",HttpCompletionOption.ResponseContentRead).Result;
33             string strJson = string.Empty;
34             Stream stream = response.Content.ReadAsStream();
35             using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
36             {
37                 strJson = reader.ReadToEnd();
38             }
39             return strJson;
40         }
41 
42         /// <summary>
43         /// POST方式
44         /// </summary>
45         /// <param name="url"></param>
46         /// <param name="param"></param>
47         /// <returns></returns>
48         public static string Post<T>(string url, T t)
49         {
50             var content = JsonConvert.SerializeObject(t);
51             
52             var httpContent = new StringContent(content,Encoding.UTF8,"application/json");
53             var httpClient = new HttpClient();
54             var response =  httpClient.PostAsync($"{absoluteUrl}{url}", httpContent).Result;
55             
56             var respContent =  response.Content.ReadAsStringAsync().Result;
57             return respContent;
58         }
59 
60         public static string Put<T>(string url, T t) {
61             var content = JsonConvert.SerializeObject(t);
62 
63             var httpContent = new StringContent(content,Encoding.UTF8,"application/json");
64             var httpClient = new HttpClient();
65             var response =  httpClient.PutAsync($"{absoluteUrl}{url}", httpContent).Result;
66 
67             var respContent = response.Content.ReadAsStringAsync().Result;
68             return respContent;
69         }
70 
71         public static string Delete(string url, Dictionary<string, string> param) {
72             string p = string.Empty;
73             if (param != null && param.Count() > 0)
74             {
75                 foreach (var keypair in param)
76                 {
77                     p += $"{keypair.Key}={keypair.Value}&";
78                 }
79             }
80             if (!string.IsNullOrEmpty(p))
81             {
82                 p = p.TrimEnd('&');
83             }
84             var httpClient = new HttpClient();
85             var response =  httpClient.DeleteAsync($"{absoluteUrl}{url}?{p}").Result;
86             var respContent = response.Content.ReadAsStringAsync().Result;
87             return respContent;
88         }
89 
90 
91         public static T StrToObject<T>(string str) { 
92             var t = JsonConvert.DeserializeObject<T>(str);
93             return t;
94         }
95     }
96 }

然后课课程接口访问通用类CourseHttpUtil继承了HttpUtil,以对应课程信息的操作。如下所示:

 1 namespace SIMS.Utils.Http
 2 {
 3     public class CourseHttpUtil:HttpUtil
 4     {
 5         /// <summary>
 6         /// 通过id查询课程信息
 7         /// </summary>
 8         /// <param name="id"></param>
 9         /// <returns></returns>
10         public static CourseEntity GetCourse(int id)
11         {
12             Dictionary<string, object> data = new Dictionary<string, object>();
13             data["id"] = id;
14             var str = Get(UrlConfig.COURSE_GETCOURSE, data);
15             var course = StrToObject<CourseEntity>(str);
16             return course;
17         }
18 
19         public static PagedRequest<CourseEntity> GetCourses(string? courseName, string? teacher, int pageNum, int pageSize)
20         {
21             Dictionary<string, object> data = new Dictionary<string, object>();
22             data["courseName"] = courseName;
23             data["teacher"] = teacher;
24             data["pageNum"] = pageNum;
25             data["pageSize"] = pageSize;
26             var str = Get(UrlConfig.COURSE_GETCOURSES, data);
27             var courses = StrToObject<PagedRequest<CourseEntity>>(str);
28             return courses;
29         }
30 
31         public static bool AddCourse(CourseEntity course)
32         {
33             var ret = Post<CourseEntity>(UrlConfig.COURSE_ADDCOURSE, course);
34             return int.Parse(ret) == 0;
35         }
36 
37         public static bool UpdateCourse(CourseEntity course)
38         {
39             var ret = Put<CourseEntity>(UrlConfig.SCORE_UPDATESCORE, course);
40             return int.Parse(ret) == 0;
41         }
42 
43         public static bool DeleteCourse(int Id)
44         {
45             Dictionary<string, string> data = new Dictionary<string, string>();
46             data["Id"] = Id.ToString();
47             var ret = Delete(UrlConfig.COURSE_DELETECOURSE, data);
48             return int.Parse(ret) == 0;
49         }
50     }
51 }

客户端操作

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

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

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

1. Course视图

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

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

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

  1 <UserControl x:Class="SIMS.CourseModule.Views.Course"
  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.CourseModule.Views"
  7              xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
  8              xmlns:prism="http://prismlibrary.com/"
  9              xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls"
 10              prism:ViewModelLocator.AutoWireViewModel="True"
 11              xmlns:ctrls ="clr-namespace:SIMS.Utils.Controls;assembly=SIMS.Utils"
 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 CourseName}"
 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 Teacher}"
 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 Courses}"
 73                   RowHeaderWidth="0">
 74             <DataGrid.Columns>
 75                 <DataGridTextColumn Binding="{Binding Name}" Header="课程名称" Width="*" />
 76                 <DataGridTextColumn Binding="{Binding Teacher}" Header="老师" Width="*"/>
 77                 <DataGridTextColumn Binding="{Binding CreateTime, StringFormat=yyyy-MM-dd HH:mm:ss}" Header="创建时间" Width="*"/>
 78                 <DataGridTextColumn Binding="{Binding LastEditTime,StringFormat=yyyy-MM-dd HH:mm:ss}" Header="最后修改时间" Width="*"/>
 79                 <DataGridTemplateColumn Header="操作" Width="*">
 80                     <DataGridTemplateColumn.CellTemplate>
 81                         <DataTemplate>
 82                             <StackPanel Orientation="Horizontal">
 83                                 <Button  Content="Edit" Style="{StaticResource LinkButton}" Command="{Binding RelativeSource={RelativeSource  AncestorType=DataGrid,  Mode=FindAncestor}, Path=DataContext.EditCommand}" CommandParameter="{Binding Id}">
 84                                     <Button.Template>
 85                                         <ControlTemplate TargetType="Button">
 86                                             <TextBlock TextDecorations="Underline" HorizontalAlignment="Center">
 87                                                 <ContentPresenter />
 88                                             </TextBlock>
 89                                         </ControlTemplate>
 90                                     </Button.Template>
 91                                 </Button>
 92                                 <Button Content="Delete" Style="{StaticResource LinkButton}" Command="{Binding RelativeSource={RelativeSource  AncestorType=DataGrid,  Mode=FindAncestor}, Path=DataContext.DeleteCommand}" CommandParameter="{Binding Id}">
 93                                     <Button.Template>
 94                                         <ControlTemplate TargetType="Button">
 95                                             <TextBlock TextDecorations="Underline" HorizontalAlignment="Center">
 96                                                 <ContentPresenter />
 97                                             </TextBlock>
 98                                         </ControlTemplate>
 99                                     </Button.Template>
100                                 </Button>
101                             </StackPanel>
102                         </DataTemplate>
103                     </DataGridTemplateColumn.CellTemplate>
104                 </DataGridTemplateColumn>
105             </DataGrid.Columns>
106         </DataGrid>
107         <ctrls:PageControl Grid.Row="3" DataContext="{Binding}" ></ctrls:PageControl>
108     </Grid>
109 </UserControl>

2. CourseViewModel

CourseViewModel是页面视图的业务逻辑处理,如处理客户端的点击的命令等内容。主要分为三部分:

数据绑定
数据绑定,如查询条件的文本框内容的绑定,课程查询列表的绑定。其中数据访问采用CourseHttpUtil工具类,如下所示:

 1 #region 属性及构造函数
 2 
 3 /// <summary>
 4 /// 专业
 5 /// </summary>
 6 private string courseName;
 7 
 8 public string CourseName
 9 {
10     get { return courseName; }
11     set { SetProperty(ref courseName, value); }
12 }
13 
14 /// <summary>
15 /// 年级
16 /// </summary>
17 private string teacher;
18 
19 public string Teacher
20 {
21     get { return teacher; }
22     set { SetProperty(ref teacher, value); }
23 }
24 
25 
26 
27 private ObservableCollection<CourseEntity> courses;
28 
29 public ObservableCollection<CourseEntity> Courses
30 {
31     get { return courses; }
32     set { SetProperty(ref courses, value); }
33 }
34 
35 private IDialogService dialogService;
36 
37 public CourseViewModel(IDialogService dialogService)
38 {
39     this.dialogService = dialogService;
40     this.pageNum = 1;
41     this.pageSize = 20;
42 }
43 
44 private void InitInfo()
45 {
46     Courses = new ObservableCollection<CourseEntity>();
47     var pagedRequst = CourseHttpUtil.GetCourses(this.CourseName, this.Teacher, this.pageNum, this.pageSize);
48     var entities = pagedRequst.items;
49     Courses.AddRange(entities);
50     //
51     this.TotalCount = pagedRequst.count;
52     this.TotalPage = ((int)Math.Ceiling(this.TotalCount * 1.0 / this.pageSize));
53 }
54 
55 #endregion

命令绑定

在课程页面,查询按钮,编辑按钮,删除按钮,均与ViewModel中的命令进行绑定,如下所示:

  1 #region 事件
  2 
  3 private DelegateCommand loadedCommand;
  4 
  5 public DelegateCommand LoadedCommand
  6 {
  7     get
  8     {
  9         if (loadedCommand == null)
 10         {
 11             loadedCommand = new DelegateCommand(Loaded);
 12         }
 13         return loadedCommand;
 14     }
 15 }
 16 
 17 private void Loaded()
 18 {
 19     InitInfo();
 20 }
 21 
 22 private DelegateCommand queryCommand;
 23 
 24 public DelegateCommand QueryCommand
 25 {
 26     get
 27     {
 28         if (queryCommand == null)
 29         {
 30             queryCommand = new DelegateCommand(Query);
 31         }
 32         return queryCommand;
 33     }
 34 }
 35 
 36 private void Query()
 37 {
 38     this.pageNum = 1;
 39     this.InitInfo();
 40 }
 41 
 42 /// <summary>
 43 /// 新增命令
 44 /// </summary>
 45 private DelegateCommand addCommand;
 46 
 47 public DelegateCommand AddCommand
 48 {
 49     get
 50     {
 51         if (addCommand == null)
 52         {
 53             addCommand = new DelegateCommand(Add);
 54         }
 55         return addCommand;
 56     }
 57 }
 58 
 59 private void Add()
 60 {
 61     this.dialogService.ShowDialog("addEditCourse", null, AddEditCallBack, "MetroDialogWindow");
 62 }
 63 
 64 private void AddEditCallBack(IDialogResult dialogResult)
 65 {
 66     if (dialogResult != null && dialogResult.Result == ButtonResult.OK)
 67     {
 68         //刷新列表
 69         this.pageNum = 1;
 70         this.InitInfo();
 71     }
 72 }
 73 
 74 /// <summary>
 75 /// 编辑命令
 76 /// </summary>
 77 private DelegateCommand<object> editCommand;
 78 
 79 public DelegateCommand<object> EditCommand
 80 {
 81     get
 82     {
 83         if (editCommand == null)
 84         {
 85             editCommand = new DelegateCommand<object>(Edit);
 86         }
 87         return editCommand;
 88     }
 89 }
 90 
 91 private void Edit(object obj)
 92 {
 93     if (obj == null)
 94     {
 95         return;
 96     }
 97     var Id = int.Parse(obj.ToString());
 98     var course = this.Courses.FirstOrDefault(r => r.Id == Id);
 99     if (course == null)
100     {
101         MessageBox.Show("无效的课程ID");
102         return;
103     }
104     IDialogParameters dialogParameters = new DialogParameters();
105     dialogParameters.Add("course", course);
106     this.dialogService.ShowDialog("addEditCourse", dialogParameters, AddEditCallBack, "MetroDialogWindow");
107 }
108 
109 /// <summary>
110 /// 编辑命令
111 /// </summary>
112 private DelegateCommand<object> deleteCommand;
113 
114 public DelegateCommand<object> DeleteCommand
115 {
116     get
117     {
118         if (deleteCommand == null)
119         {
120             deleteCommand = new DelegateCommand<object>(Delete);
121         }
122         return deleteCommand;
123     }
124 }
125 
126 private void Delete(object obj)
127 {
128     if (obj == null)
129     {
130         return;
131     }
132     var Id = int.Parse(obj.ToString());
133     var course = this.Courses.FirstOrDefault(r => r.Id == Id);
134     if (course == null)
135     {
136         MessageBox.Show("无效的班级ID");
137         return;
138     }
139     if (MessageBoxResult.Yes != MessageBox.Show("Are you sure to delete?", "Confirm", MessageBoxButton.YesNo)) {
140         return;
141     }
142     bool flag = CourseHttpUtil.DeleteCourse(Id);
143     if (flag)
144     {
145         this.pageNum = 1;
146         this.InitInfo();
147     }
148 }
149 
150 #endregion

分页命令与数据绑定

关于分页功能,是通用的功能,几乎每一个查询页面的分页都大同小异,可以进行复制粘贴,快速实现功能。如下所示:

  1 #region 分页
  2 
  3 /// <summary>
  4 /// 当前页码
  5 /// </summary>
  6 private int pageNum;
  7 
  8 public int PageNum
  9 {
 10     get { return pageNum; }
 11     set { SetProperty(ref pageNum, value); }
 12 }
 13 
 14 /// <summary>
 15 /// 每页显示多少条记录
 16 /// </summary>
 17 private int pageSize;
 18 
 19 public int PageSize
 20 {
 21     get { return pageSize; }
 22     set { SetProperty(ref pageSize, value); }
 23 }
 24 
 25 /// <summary>
 26 ///总条数
 27 /// </summary>
 28 private int totalCount;
 29 
 30 public int TotalCount
 31 {
 32     get { return totalCount; }
 33     set { SetProperty(ref totalCount, value); }
 34 }
 35 
 36 /// <summary>
 37 ///总页数
 38 /// </summary>
 39 private int totalPage;
 40 
 41 public int TotalPage
 42 {
 43     get { return totalPage; }
 44     set { SetProperty(ref totalPage, value); }
 45 }
 46 
 47 
 48 /// <summary>
 49 /// 跳转页
 50 /// </summary>
 51 private int jumpNum;
 52 
 53 public int JumpNum
 54 {
 55     get { return jumpNum; }
 56     set { SetProperty(ref jumpNum, value); }
 57 }
 58 
 59 /// <summary>
 60 /// 首页命令
 61 /// </summary>
 62 private DelegateCommand firstPageCommand;
 63 
 64 public DelegateCommand FirstPageCommand
 65 {
 66     get
 67     {
 68         if (firstPageCommand == null)
 69         {
 70             firstPageCommand = new DelegateCommand(FirstPage);
 71         }
 72         return firstPageCommand;
 73     }
 74 
 75 }
 76 
 77 private void FirstPage()
 78 {
 79     this.PageNum = 1;
 80     this.InitInfo();
 81 }
 82 
 83 /// <summary>
 84 /// 跳转页命令
 85 /// </summary>
 86 private DelegateCommand jumpPageCommand;
 87 
 88 public DelegateCommand JumpPageCommand
 89 {
 90     get
 91     {
 92         if (jumpPageCommand == null)
 93         {
 94             jumpPageCommand = new DelegateCommand(JumpPage);
 95         }
 96         return jumpPageCommand;
 97     }
 98 }
 99 
100 private void JumpPage()
101 {
102     if (jumpNum < 1)
103     {
104         MessageBox.Show("请输入跳转页");
105         return;
106     }
107     if (jumpNum > this.totalPage)
108     {
109         MessageBox.Show($"跳转页面必须在[1,{this.totalPage}]之间,请确认。");
110         return;
111     }
112     this.PageNum = jumpNum;
113 
114     this.InitInfo();
115 }
116 
117 /// <summary>
118 /// 前一页
119 /// </summary>
120 private DelegateCommand prevPageCommand;
121 
122 public DelegateCommand PrevPageCommand
123 {
124     get
125     {
126         if (prevPageCommand == null)
127         {
128             prevPageCommand = new DelegateCommand(PrevPage);
129         }
130         return prevPageCommand;
131     }
132 }
133 
134 private void PrevPage()
135 {
136     this.PageNum--;
137     if (this.PageNum < 1)
138     {
139         this.PageNum = 1;
140     }
141     this.InitInfo();
142 }
143 
144 /// <summary>
145 /// 下一页命令
146 /// </summary>
147 private DelegateCommand nextPageCommand;
148 
149 public DelegateCommand NextPageCommand
150 {
151     get
152     {
153         if (nextPageCommand == null)
154         {
155             nextPageCommand = new DelegateCommand(NextPage);
156         }
157         return nextPageCommand;
158     }
159 }
160 
161 private void NextPage()
162 {
163     this.PageNum++;
164     if (this.PageNum > this.TotalPage)
165     {
166         this.PageNum = this.TotalPage;
167     }
168     this.InitInfo();
169 }
170 
171 #endregion

3. 新增编辑课程视图AddEditCourse

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

 1 <UserControl x:Class="SIMS.CourseModule.Views.AddEditCourse"
 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.CourseModule.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         </Grid.RowDefinitions>
43 
44 
45         <TextBlock Text="课程名称" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Margin="3"></TextBlock>
46         <TextBox Grid.Row="0" Grid.Column="2" MinWidth="120" Height="35"  VerticalAlignment="Center" Margin="3" Text="{Binding Course.Name}"></TextBox>
47         <TextBlock Text="授课老师" Grid.Row="1" Grid.Column="1"  VerticalAlignment="Center" Margin="3"></TextBlock>
48         <TextBox Grid.Row="1" Grid.Column="2" MinWidth="120" Height="35"   VerticalAlignment="Center" Margin="3" Text="{Binding Course.Teacher}"></TextBox>
49         
50         <StackPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="3">
51             <Button Content="取消" Margin="5" MinWidth="120" Height="35" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Command="{Binding CancelCommand}"></Button>
52             <Button Content="保存" Margin="5" MinWidth="120" Height="35" Style="{DynamicResource MahApps.Styles.Button.Square.Accent}" Command="{Binding SaveCommand}"></Button>
53         </StackPanel>
54         
55     </Grid>
56 </UserControl>

4. 新增编辑课程ViewModel

AddEditCourseViewModel是对页面具体功能的业务封装,主要是对应课程信息的保存,也包括数据绑定和命令绑定等内容,如下所示:

  1 namespace SIMS.CourseModule.ViewModels
  2 {
  3     public class AddEditCourseViewModel:BindableBase,IDialogAware
  4     {
  5         #region 属性和构造函数
  6 
  7         /// <summary>
  8         /// 班级实体
  9         /// </summary>
 10         private CourseEntity course;
 11 
 12         public CourseEntity Course
 13         {
 14             get { return course; }
 15             set { SetProperty(ref course, value); }
 16         }
 17 
 18         public AddEditCourseViewModel()
 19         {
 20 
 21         }
 22 
 23         #endregion
 24 
 25         #region Command
 26 
 27         private DelegateCommand loadedCommand;
 28 
 29         public DelegateCommand LoadedCommand
 30         {
 31             get
 32             {
 33                 if (loadedCommand == null)
 34                 {
 35                     loadedCommand = new DelegateCommand(Loaded);
 36                 }
 37                 return loadedCommand;
 38             }
 39         }
 40 
 41         private void Loaded()
 42         {
 43         }
 44 
 45         private DelegateCommand cancelCommand;
 46 
 47         public DelegateCommand CancelCommand
 48         {
 49             get
 50             {
 51                 if (cancelCommand == null)
 52                 {
 53                     cancelCommand = new DelegateCommand(Cancel);
 54                 }
 55                 return cancelCommand;
 56             }
 57         }
 58 
 59         private void Cancel()
 60         {
 61             RequestClose?.Invoke((new DialogResult(ButtonResult.Cancel)));
 62         }
 63 
 64         private DelegateCommand saveCommand;
 65 
 66         public DelegateCommand SaveCommand
 67         {
 68             get
 69             {
 70                 if (saveCommand == null)
 71                 {
 72                     saveCommand = new DelegateCommand(Save);
 73                 }
 74                 return saveCommand;
 75             }
 76         }
 77 
 78         private void Save()
 79         {
 80             if (Course != null)
 81             {
 82                 Course.CreateTime = DateTime.Now;
 83                 Course.LastEditTime = DateTime.Now;
 84                 
 85                 bool flag = false;
 86                 if (Course.Id > 0)
 87                 {
 88                     flag = CourseHttpUtil.UpdateCourse(Course);
 89                 }
 90                 else
 91                 {
 92                     flag = CourseHttpUtil.AddCourse(Course);
 93                 }
 94                 if (flag)
 95                 {
 96                     RequestClose?.Invoke((new DialogResult(ButtonResult.OK)));
 97                 }
 98             }
 99         }
100 
101 
102         #endregion
103 
104         #region 对话框
105 
106         public string Title => "新增或编辑课程信息";
107 
108         public event Action<IDialogResult> RequestClose;
109 
110         public bool CanCloseDialog()
111         {
112             return true;
113         }
114 
115         public void OnDialogClosed()
116         {
117 
118         }
119 
120         public void OnDialogOpened(IDialogParameters parameters)
121         {
122             if (parameters != null && parameters.ContainsKey("course"))
123             {
124                 this.Course = parameters.GetValue<CourseEntity>("course");
125             }
126             else
127             {
128                 this.Course = new CourseEntity();
129             }
130         }
131 
132         #endregion
133 
134     }
135 }

注意:因为新增编辑页面是弹出窗口,所以在Prism框架中,需要实现IDialogAware接口。

5. 控件注册

当页面功能开发完成后,在通过Prism进行注册,就可以通过导航栏和弹出窗口进行展示,如下所示:

 1 namespace SIMS.CourseModule
 2 {
 3     public class CourseModule : IModule
 4     {
 5         public void OnInitialized(IContainerProvider containerProvider)
 6         {
 7 
 8         }
 9 
10         public void RegisterTypes(IContainerRegistry containerRegistry)
11         {
12             containerRegistry.RegisterForNavigation<Course, CourseViewModel>(nameof(Course));
13             containerRegistry.RegisterDialog<AddEditCourse, AddEditCourseViewModel>("addEditCourse");
14         }
15     }
16 }

示例效果图

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

总结

经过上述步骤,不难看出,开发一个完整的功能,涉及到前端,后端,接口访问,数据库等相关内容,麻雀虽小,五脏俱全。其实开发一个课程管理模块,就是对数据库中课程表的增删改查,也可以把所有代码都糅合在一起,简化开发流程和步骤,这样代码量将大幅度减少。但是分层,分模块并不是为了使项目复杂化,而为了更加清晰以及便于维护与扩展,也是本篇文章希望为大家传递的一种开发理念。

备注

江上渔者【作者】范仲淹 【朝代】宋

江上往来人,但爱鲈鱼美。

君看一叶舟,出没风波里。

标签:WebApi,DelegateCommand,return,string,int,private,Prism,WPF,public
来源: https://www.cnblogs.com/hsiang/p/16322653.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有