本着每天记录一点成长一点的原则,打算将目前完成的一个WPF项目相关的技术分享出来,供团队学习与总结。

总共分三个部分:

基础篇主要针对C#初学者,巩固C#常用知识点;

中级篇主要针对WPF布局与MaterialDesign美化设计,在减轻代码量的情况做出漂亮的应用;

终极篇为框架应用实战,包含系统分层、MVVM框架Prism安装与使用、ORM框架EntityFramework Core配置与使用、开源数据库Postgresql配置与使用。

目录

前言

此篇主要介绍系统分层模型、如何安装Prism快速开发模板与MVVM框架使用、如何配置ORM框架Entity Framework Core与使用、以及Postgresql数据库配置。

系统分层

项目比较简单,大概分层模型如下:

View双向绑定ViewModel;

ViewModel调用Service取得DataModel业务数据;

Service通过调用Repository取得Entity数据;

Repository调用Entity Framework Core,自动创建Sql执行并返回Entity对象;

Entity Framework Core通过驱动链接数据库。

如果项目功能或者对接端末比较多,最好扩展成微服务。

MVVM框架之Prism

MVVM(Model–view–viewmodel)是微软的WPF和Silverlight架构师之一John Gossman于2005年发布的软件架构模式。目的就是把用户界面设计与业务逻辑开发分离,方便团队开发和自动化测试。目前流行的Android开发、Web开发都在使用,具体MVVM的介绍参照个人博客:核心框架MVVM与MVC、MVP的区别(图文详解)。

一、无框架的MVVM实现

设计与逻辑分离的基本就是绑定,通过发布者订阅者模式实现数据更新通知。

1、属性绑定

默认属性为单向绑定,如果需要双向绑定需要实现INotifyPropertyChanged接口。

第一步:一般是建立如下基类。

using System;

using System.ComponentModel;

using System.Runtime.CompilerServices;

namespace MvvmDemo.Common

{

///

/// Viewmodel基类,属性双向绑定基础

///

public class ViewModelBase : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

///

/// 属性变更通知

///

/// 属性名

public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")

{

if (PropertyChanged != null)

{

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}

}

}

}

第二步:各个ViewModel继承基类。

public class UserViewModel : ViewModelBase

{

private string _userId;

private string _userName;

///

/// 用户名

///

public string UserId

{

get

{

return _userId;

}

set

{

_userId = value;

NotifyPropertyChanged();

}

}

///

/// 用户名

///

public string UserName

{

get

{

return _userName;

}

set

{

_userName = value;

NotifyPropertyChanged();

}

}

}

第三步:Xaml绑定属性,实现消息通知。

备注:通过IValueConverter可以做一些特殊绑定处理。比如,经典的就是Bool值控制Visibility。

[ValueConversion(typeof(bool), typeof(Visibility))]

public class BoolToVisibiltyConverter : MarkupExtension, IValueConverter

{

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

{

bool flag = false;

if (value is bool)

{

flag = (bool)value;

}

else if (value is bool?)

{

bool? nullable = (bool?)value;

flag = nullable.HasValue ? nullable.Value : false;

}

return (flag ? Visibility.Visible : Visibility.Collapsed);

}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

{

return value;

}

public override object ProvideValue(IServiceProvider serviceProvider)

{

return this;

}

}

Xaml绑定:头部需要引入命名空间。

xmlns:converter="clr-namespace:WpfMvvm.Core.Converters"

Grid.Row="2"

Visibility="{Binding ShowFlg,Converter={converter:BoolToVisibiltyConverter}}"

Command="{Binding AddCmd}"

Content="登录" />

2、事件绑定

WPF提供了Command事件处理属性,想利用控件中的Command属性需要实现了ICommand接口的属性。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Input;

namespace MvvmDemo.Common

{

public class DelegateCommand: ICommand

{

///

/// 命令

///

private Action _Command;

///

/// 命令可否执行判断

///

private Func _CanExecute;

///

/// 可执行判断结束后通知命令执行

///

public event EventHandler CanExecuteChanged;

///

/// 构造函数

///

/// 命令

public DelegateCommand(Action command):this(command,null)

{

}

///

/// 构造函数

///

/// 命令

/// 命令可执行判断

public DelegateCommand(Action command,Func canexecute)

{

if(command==null)

{

throw new ArgumentException("command");

}

_Command = command;

_CanExecute = canexecute;

}

///

/// 命令执行判断

///

/// 判断数据

/// 判定结果(True:可执行,False:不可执行)

public bool CanExecute(object parameter)

{

return _CanExecute == null ? true : _CanExecute((T)parameter);

}

///

/// 执行命令

///

/// 参数

public void Execute(object parameter)

{

_Command((T)parameter);

}

}

}

使用它作为事件属性的类型就可以了。

///

/// 登陆命令

///

public DelegateCommand LoginCommand => new DelegateCommand(

s =>

{

// todo

},

s => !string.IsNullOrEmpty(s)

);

二、Prism的MVVM实现

至于Prism有很多种理由让我选择它,比如:

支持MVVM(Binding、Notification、Command等)、微软成员维护

支持Unity和DryIoc两种IOC容器

支持WPF、UWP、Xamarin.Froms开发

封装界面跳转

封装弹出消息框

自带项目模板与快速开发代码片段

创建View时自动创建ViewModel

默认自动绑定ViewModel到View

...等等

1、配置Prism

最简单的方法:安装Prism Template Pack扩展包。

2、使用Prism

通过Prism项目模板创建项目,目前可以创建WPF(.Net Framework和.Net Core)、UWP、Xamarin.Froms等应用。

以前支持四种容器,现在只支持两种IOC容器:Unity、DryIoc。

*备注:如果通过Prism模板创建项目时出现以下错误:

这是因为Autofac已经不被支持。解决办法:regedit进入注册表HKEY_CURRENT_USER\Software\Prism,把SelectedContainer删除或者改成Unity。

生成的解决方案如下:

亮点:解决方案中自动设置了ViewModel的IOC配置,MainWindow.xaml中ViewModel的绑定也自动设置了。

下面通过建立一个简单的局部界面跳转实例,体验一把Prism的高效率:cmd、propp、vs智能提示。

Prism包提供的代码片段如下,要好好加以利用:

此次项目还用到了以下特性:

2.1 Region Navigation

局部页面跳转:

传递对象参数;

跳转前确认;

自定义如何处理已经显示过的页面(覆盖与否);

通过IRegionNavigationJournal接口可以操作页面跳转履历(返回与前进等)。

如上例所示简单应用。

第一步:标识显示位置。

第二步:在App.xaml.cs注册跳转页面。

 View Code

第三步:使用IRegionManager实现跳转。

// 指定需要显示的页面名字与显示位置的ContentControl的名字

_manager.RequestNavigate("ContentRegion", "PageTwo");

2.2、Modules

如果系统功能比较多最好进行分块处理,如下面订单和用户信息的分块处理。

App.xaml.cs中统一各个模块数据。

// ModuleLoader会把各个模块的IOC依赖注入数据汇总共有管理

protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)

{

moduleCatalog.AddModule();

moduleCatalog.AddModule();

}

各个Module里面还是一样,使用到的所有Service和Repository都注册,使用IOC容器进行生命周期管理。

public class OrderModule : IModule

{

public void OnInitialized(IContainerProvider containerProvider)

{

}

public void RegisterTypes(IContainerRegistry containerRegistry)

{

containerRegistry.RegisterForNavigation(PageDefine.Order);

containerRegistry.Register();

}

}

2.3、Dialog Service

自定义消息弹出框,比如警告、错误、提示等消息框。

第一步:自定义消息框控件,ViewModel继承IDialogAware接口并实现:

public class NotificationDialogViewModel : BindableBase, IDialogAware

{

private DelegateCommand _closeDialogCommand;

public DelegateCommand CloseDialogCommand =>

_closeDialogCommand ?? (_closeDialogCommand = new DelegateCommand(CloseDialog));

private string _message;

public string Message

{

get { return _message; }

set { SetProperty(ref _message, value); }

}

private string _title = "Notification";

public string Title

{

get { return _title; }

set { SetProperty(ref _title, value); }

}

public event Action RequestClose;

protected virtual void CloseDialog(string parameter)

{

ButtonResult result = ButtonResult.None;

if (parameter?.ToLower() == "true")

result = ButtonResult.OK;

else if (parameter?.ToLower() == "false")

result = ButtonResult.Cancel;

RaiseRequestClose(new DialogResult(result));

}

public virtual void RaiseRequestClose(IDialogResult dialogResult)

{

RequestClose?.Invoke(dialogResult);

}

public virtual bool CanCloseDialog()

{

return true;

}

public virtual void OnDialogClosed()

{

}

public virtual void OnDialogOpened(IDialogParameters parameters)

{

Message = parameters.GetValue("message");

}

}

第二步:App.xaml.cs中注册自定义的消息框,从而覆盖默认的消息框:

public partial class App

{

protected override Window CreateShell()

{

return Container.Resolve();

}

protected override void RegisterTypes(IContainerRegistry containerRegistry)

{

containerRegistry.RegisterDialog();

}

}

第三步:通过IDialogService使用消息框:

private void ShowDialog()

{

var message = "This is a message that should be shown in the dialog.";

//using the dialog service as-is

_dialogService.ShowDialog("NotificationDialog", new DialogParameters($"message={message}"), r =>

{

if (r.Result == ButtonResult.None)

Title = "Result is None";

else if (r.Result == ButtonResult.OK)

Title = "Result is OK";

else if (r.Result == ButtonResult.Cancel)

Title = "Result is Cancel";

else

Title = "I Don't know what you did!?";

});

}

第四步:定义消息框显示属性:

Entity Framework Core + Postgresql

EntityFrameworkCore:是对象关系映射(ORM)程序,支持语言集成查询Linq,是轻量、可扩展、开源跨平台的数据访问框架。下一个5.0版本将与.NET 5.0一起发布。EntityFrameworkCore只支持CodeFirst,EntityFramework支持DB First和Code First。之所以选择EFCore是因为:

支持CodeFirst

支持Linq

双向映射(linq映射成sql,结果集映射成对象)

速度很快

PostgreSQL:是开源先进的对象-关系型数据库管理系统(ORDBMS),有些特性甚至连商业数据库都不具备。支持JSON数据存储,表之间还可以继承。

一、配置EFCore与PostgreSQL

1、引入针对PostgreSQL的EFCore包

2、添加DB操作上下文

数据库链接替换为你的链接,一般都是放配置文件管理。

添加Users字段,通过EFCore将自动创建Users表。

using System;

using Microsoft.EntityFrameworkCore;

using WpfMccm.Entitys;

namespace WpfMvvm.DataAccess

{

public class UserDbContext : DbContext

{

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

{

optionsBuilder.UseNpgsql("Server=127.0.0.1;Database=HBMCS;Port=5432;User Id=test;Password=test;Ssl Mode=Prefer;",

npgsqlOptionsAction: options =>

{

options.CommandTimeout(60);

options.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorCodesToAdd: null);

});

}

public DbSet Users { get; set; }

}

}

3、安装Microsoft.EntityFrameworkCore.Tools工具

CodeFirst必备神器。进入程序包管理器控制台,输入以下命名安装EFCore设计工具:

※必须安装在启动项目里面,不然会失败。

Install-Package Microsoft.EntityFrameworkCore.Tools

4、创建Migration

程序包管理器控制台,默认项目一定要选择DB操作上下文的项目,然后执行命令:InitDB是文件区分,可以任意修改。

Add-Migration InitDB

执行成功之后,生成带InitDB区分的表定义数据文件:

6、生成数据库脚本(生产阶段用,开发阶段可跳过)

程序包管理器控制台,执行如下命令生成SQL脚本文件:

Script-Migration

CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (

"MigrationId" character varying(150) NOT NULL,

"ProductVersion" character varying(32) NOT NULL,

CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId")

);

CREATE TABLE "Users" (

"ID" integer NOT NULL GENERATED BY DEFAULT AS IDENTITY,

"Name" text NULL,

"Age" integer NOT NULL,

CONSTRAINT "PK_Users" PRIMARY KEY ("ID")

);

INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")

VALUES ('20200413133616_InitDB', '3.1.3');

如果系统已经上线,安全起见则需要使用这个方法生成SQL脚本,手动执行SQL更新数据库。

7、更新数据库(开发阶段用)

程序包管理器控制台,执行如下命令将表定义更新到DB(按文件名的时间顺顺添加):

Update-Database

这样我们就通过类创建了一个数据库表Users,同时默认会在__EFMigrationsHistory履历表添加一条合并记录。

※如果__EFMigrationsHistory中记录存在则忽略本次更新。

二、使用DB上下文操作数据库

1、创建IRepository,DB操作基本接口

public interface IRepository where TEntity : class

{

Task GetAsync(int id);

Task AddAsync(TEntity obj);

}

2、创建UserRepository,User专用的DB操作类

public class UserRepository : IRepository

{

private readonly DbContext _dbContext;

private readonly DbSet _dbSet;

public UserRepository(UserDbContext dbContext)

{

_dbContext = dbContext;

_dbSet = dbContext.Set();

}

public async Task AddAsync(User obj)

{

_dbSet.Add(obj);

return await _dbContext.SaveChangesAsync() > 0;

}

public async Task GetAsync(int id)

{

return await _dbSet.FindAsync(id);

}

}

如果需要进行事务操作,可以使用下面方法:

var tran= _dbContext.Database.BeginTransaction();

tran.Commit();

3、Service层调用UserRepository就可以完成用户的操作。

总结

此篇量稍微有点多,非常感谢能看到这里。整体来说Prism简化了应用的设计与架构,EFCore简化了数据库操作。

prism项目搭建 wpf_Prism完成的一个WPF项目相关推荐

  1. WPF入门教程系列(一) 创建你的第一个WPF项目

    WPF基础知识 快速学习绝不是从零学起的,良好的基础是快速入手的关键,下面先为大家摞列以下自己总结的学习WPF的几点基础知识: 1) C#基础语法知识(或者其他.NET支持的语言):这个是当然的了,虽 ...

  2. docker build命令详解_Docker 搭建你的第一个 Node 项目到服务器

    本文你能学到什么 Docker 是什么 Docker 概念 关于 Docker 的概念是确实不太好总结,下面我通过四点向你说明 Docker 到底是个什么东西. Docker 是世界领先的软件容器平台 ...

  3. Angular 5.0 学习2:Angular 5.0 开发环境的搭建和新建第一个ng5项目

    1.安装Node.js 在开始工作之前,我们必须设置好开发环境. 如果你的机器上还没有Node.js®和npm,请先安装它们. 去Node.js的官网,https://nodejs.org/en/,点 ...

  4. vue2.0 项目搭建 和vue 2.0 electron 项目搭建

    1.关于electron vue 项目的搭建 全局或者局部安装项目vue:脚手架指令生成: npm install -g vue-cli vue init simulatedgreg/electron ...

  5. vs中如和根据项目生成类图_迁移WPF项目到.NET Core

    综述# .NET CORE 3.0开始,桌面端支持WPF了.很多.NET FRAMEWORK的项目已经跑了一阵子了,不是很有必要支持.NET CORE,不过最近用一个程序,为了贯彻一些C# 8的特性, ...

  6. php项目打开快捷方式,PHP_克隆一个新项目的快捷方式,有没想过最土的项目如何快速 - phpStudy...

    克隆一个新项目的快捷方式 有没想过最土的项目如何快速复制出一个来,然后改改就成新的团购项目了? 或者说编辑一个老项目的时候想把他另存为一个新项目而不是保存, 看下图 红色部分 具体开发代码(非细节), ...

  7. 如何从0开始开源项目参与_如何开始一个开源项目

    如何从0开始开源项目参与 by Dmitriy Strukov 德米特里·斯特鲁科夫(Dmitriy Strukov) 如何开始一个开源项目 (How to start an Open Source ...

  8. python怎么建立项目经理部的基本原则_一个关于项目经理的故事

    一个关于项目经理的故事 项目经理的养成日记 L在2001年毕业之后加入到了福建实达公司. 在今天这个公司几乎是很少有人听说了,但在当年实达还是在IT这片江湖里有些地位的.当年实达的产品线非常全,有网络 ...

  9. Qt创建项目:手把手创建第一个Qt项目

    上一节介绍了QtCreator编辑器的页面长什么样子,以及都有哪些功能区,每个功能区都是用来做什么的.这一节我就手把手带大家创建一个Qt项目. 创建项目 点击新建按钮 创建项目有两个入口,一个是在欢迎 ...

最新文章

  1. SVN、Apache和AD LDS的集成
  2. 根据您的命令-命令设计模式
  3. python是面向什么的计算机程序设计语言_Python是一种计算机程序设计语言,python到底该怎么学习...
  4. Visualization的学习笔记
  5. catv系统主要有哪三部分组成_有线电视系统,看完电力工程技术专家分析,顿时学会了,太经典...
  6. Fragstats4.2之计算景观格局指数(一)
  7. 高斯混合模型(GMM),c++实现
  8. css样式的补充:鼠标悬停字体变大和改变颜色
  9. 蓦然回首,会员制CRM就在下里巴人处
  10. Android Studio Cannot resolve symbol 解决方法
  11. 中医大计算机考试题目,中医大计算机复习题-20210319205538.docx-原创力文档
  12. 基于kubeadm 部署K8S1.22.3 踩坑
  13. 海康摄像头视频实时监控
  14. Javascript学习笔记(犀牛书1、2章)
  15. 优达(Udacity)smartcab
  16. postmarketOS与Ubports星光闪耀
  17. python 英语词汇量_最强“扫地僧”!北大保安小哥英语词汇量一万五,会用Python编程,网友:北大保安都不会招我...
  18. 三维点云数据的读取和三维曲面重建matlab仿真
  19. 支付宝H5支付,支付页面无响应事件以及解决方案。
  20. 书籍集锦——关于深度学习

热门文章

  1. PHP的Composer install、require、update
  2. Linux安装screen时的问题
  3. C的安装编译Error
  4. oracle启动crs要多久,ORACLE RAC crs 无法启动
  5. JAVA蘑菇西餐,蘑菇的22种西餐做法,简单易上手,让你品尝不一样的风味
  6. 自考计算机及应用心得体会,自考中文专业的心得体会
  7. php关键词分词搜索 最多匹配的排在最前面_百度搜索引擎工作原理,做Seo的建议看一看 - 蜘蛛池博客...
  8. 【Node.js】http-server 实现目录浏览服务
  9. springboot 注解动态赋值_java springboot动态给注解属性参数赋值
  10. python翻页_python实现电子书翻页小程序