prism项目搭建 wpf_Prism完成的一个WPF项目
本著每天記錄一點(diǎn)成長一點(diǎn)的原則,打算將目前完成的一個(gè)WPF項(xiàng)目相關(guān)的技術(shù)分享出來,供團(tuán)隊(duì)學(xué)習(xí)與總結(jié)。
總共分三個(gè)部分:
基礎(chǔ)篇主要針對C#初學(xué)者,鞏固C#常用知識點(diǎn);
中級篇主要針對WPF布局與MaterialDesign美化設(shè)計(jì),在減輕代碼量的情況做出漂亮的應(yīng)用;
終極篇為框架應(yīng)用實(shí)戰(zhàn),包含系統(tǒng)分層、MVVM框架Prism安裝與使用、ORM框架EntityFramework Core配置與使用、開源數(shù)據(jù)庫Postgresql配置與使用。
目錄
前言
此篇主要介紹系統(tǒng)分層模型、如何安裝Prism快速開發(fā)模板與MVVM框架使用、如何配置ORM框架Entity Framework Core與使用、以及Postgresql數(shù)據(jù)庫配置。
系統(tǒng)分層
項(xiàng)目比較簡單,大概分層模型如下:
View雙向綁定ViewModel;
ViewModel調(diào)用Service取得DataModel業(yè)務(wù)數(shù)據(jù);
Service通過調(diào)用Repository取得Entity數(shù)據(jù);
Repository調(diào)用Entity Framework Core,自動(dòng)創(chuàng)建Sql執(zhí)行并返回Entity對象;
Entity Framework Core通過驅(qū)動(dòng)鏈接數(shù)據(jù)庫。
如果項(xiàng)目功能或者對接端末比較多,最好擴(kuò)展成微服務(wù)。
MVVM框架之Prism
MVVM(Model–view–viewmodel)是微軟的WPF和Silverlight架構(gòu)師之一John Gossman于2005年發(fā)布的軟件架構(gòu)模式。目的就是把用戶界面設(shè)計(jì)與業(yè)務(wù)邏輯開發(fā)分離,方便團(tuán)隊(duì)開發(fā)和自動(dòng)化測試。目前流行的Android開發(fā)、Web開發(fā)都在使用,具體MVVM的介紹參照個(gè)人博客:核心框架MVVM與MVC、MVP的區(qū)別(圖文詳解)。
一、無框架的MVVM實(shí)現(xiàn)
設(shè)計(jì)與邏輯分離的基本就是綁定,通過發(fā)布者訂閱者模式實(shí)現(xiàn)數(shù)據(jù)更新通知。
1、屬性綁定
默認(rèn)屬性為單向綁定,如果需要雙向綁定需要實(shí)現(xiàn)INotifyPropertyChanged接口。
第一步:一般是建立如下基類。
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MvvmDemo.Common
{
///
/// Viewmodel基類,屬性雙向綁定基礎(chǔ)
///
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
///
/// 屬性變更通知
///
/// 屬性名
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
第二步:各個(gè)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綁定屬性,實(shí)現(xiàn)消息通知。
備注:通過IValueConverter可以做一些特殊綁定處理。比如,經(jīng)典的就是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屬性需要實(shí)現(xiàn)了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;
///
/// 命令可否執(zhí)行判斷
///
private Func _CanExecute;
///
/// 可執(zhí)行判斷結(jié)束后通知命令執(zhí)行
///
public event EventHandler CanExecuteChanged;
///
/// 構(gòu)造函數(shù)
///
/// 命令
public DelegateCommand(Action command):this(command,null)
{
}
///
/// 構(gòu)造函數(shù)
///
/// 命令
/// 命令可執(zhí)行判斷
public DelegateCommand(Action command,Func canexecute)
{
if(command==null)
{
throw new ArgumentException("command");
}
_Command = command;
_CanExecute = canexecute;
}
///
/// 命令執(zhí)行判斷
///
/// 判斷數(shù)據(jù)
/// 判定結(jié)果(True:可執(zhí)行,False:不可執(zhí)行)
public bool CanExecute(object parameter)
{
return _CanExecute == null ? true : _CanExecute((T)parameter);
}
///
/// 執(zhí)行命令
///
/// 參數(shù)
public void Execute(object parameter)
{
_Command((T)parameter);
}
}
}
使用它作為事件屬性的類型就可以了。
///
/// 登陸命令
///
public DelegateCommand LoginCommand => new DelegateCommand(
s =>
{
// todo
},
s => !string.IsNullOrEmpty(s)
);
二、Prism的MVVM實(shí)現(xiàn)
至于Prism有很多種理由讓我選擇它,比如:
支持MVVM(Binding、Notification、Command等)、微軟成員維護(hù)
支持Unity和DryIoc兩種IOC容器
支持WPF、UWP、Xamarin.Froms開發(fā)
封裝界面跳轉(zhuǎn)
封裝彈出消息框
自帶項(xiàng)目模板與快速開發(fā)代碼片段
創(chuàng)建View時(shí)自動(dòng)創(chuàng)建ViewModel
默認(rèn)自動(dòng)綁定ViewModel到View
...等等
1、配置Prism
最簡單的方法:安裝Prism Template Pack擴(kuò)展包。
2、使用Prism
通過Prism項(xiàng)目模板創(chuàng)建項(xiàng)目,目前可以創(chuàng)建WPF(.Net Framework和.Net Core)、UWP、Xamarin.Froms等應(yīng)用。
以前支持四種容器,現(xiàn)在只支持兩種IOC容器:Unity、DryIoc。
*備注:如果通過Prism模板創(chuàng)建項(xiàng)目時(shí)出現(xiàn)以下錯(cuò)誤:
這是因?yàn)锳utofac已經(jīng)不被支持。解決辦法:regedit進(jìn)入注冊表HKEY_CURRENT_USER\Software\Prism,把SelectedContainer刪除或者改成Unity。
生成的解決方案如下:
亮點(diǎn):解決方案中自動(dòng)設(shè)置了ViewModel的IOC配置,MainWindow.xaml中ViewModel的綁定也自動(dòng)設(shè)置了。
下面通過建立一個(gè)簡單的局部界面跳轉(zhuǎn)實(shí)例,體驗(yàn)一把Prism的高效率:cmd、propp、vs智能提示。
Prism包提供的代碼片段如下,要好好加以利用:
此次項(xiàng)目還用到了以下特性:
2.1 Region Navigation
局部頁面跳轉(zhuǎn):
傳遞對象參數(shù);
跳轉(zhuǎn)前確認(rèn);
自定義如何處理已經(jīng)顯示過的頁面(覆蓋與否);
通過IRegionNavigationJournal接口可以操作頁面跳轉(zhuǎn)履歷(返回與前進(jìn)等)。
如上例所示簡單應(yīng)用。
第一步:標(biāo)識顯示位置。
第二步:在App.xaml.cs注冊跳轉(zhuǎn)頁面。
?View Code
第三步:使用IRegionManager實(shí)現(xiàn)跳轉(zhuǎn)。
// 指定需要顯示的頁面名字與顯示位置的ContentControl的名字
_manager.RequestNavigate("ContentRegion", "PageTwo");
2.2、Modules
如果系統(tǒng)功能比較多最好進(jìn)行分塊處理,如下面訂單和用戶信息的分塊處理。
App.xaml.cs中統(tǒng)一各個(gè)模塊數(shù)據(jù)。
// ModuleLoader會(huì)把各個(gè)模塊的IOC依賴注入數(shù)據(jù)匯總共有管理
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule();
moduleCatalog.AddModule();
}
各個(gè)Module里面還是一樣,使用到的所有Service和Repository都注冊,使用IOC容器進(jìn)行生命周期管理。
public class OrderModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation(PageDefine.Order);
containerRegistry.Register();
}
}
2.3、Dialog Service
自定義消息彈出框,比如警告、錯(cuò)誤、提示等消息框。
第一步:自定義消息框控件,ViewModel繼承IDialogAware接口并實(shí)現(xiàn):
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中注冊自定義的消息框,從而覆蓋默認(rèn)的消息框:
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:是對象關(guān)系映射(ORM)程序,支持語言集成查詢Linq,是輕量、可擴(kuò)展、開源跨平臺的數(shù)據(jù)訪問框架。下一個(gè)5.0版本將與.NET 5.0一起發(fā)布。EntityFrameworkCore只支持CodeFirst,EntityFramework支持DB First和Code First。之所以選擇EFCore是因?yàn)?#xff1a;
支持CodeFirst
支持Linq
雙向映射(linq映射成sql,結(jié)果集映射成對象)
速度很快
PostgreSQL:是開源先進(jìn)的對象-關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(ORDBMS),有些特性甚至連商業(yè)數(shù)據(jù)庫都不具備。支持JSON數(shù)據(jù)存儲,表之間還可以繼承。
一、配置EFCore與PostgreSQL
1、引入針對PostgreSQL的EFCore包
2、添加DB操作上下文
數(shù)據(jù)庫鏈接替換為你的鏈接,一般都是放配置文件管理。
添加Users字段,通過EFCore將自動(dòng)創(chuàng)建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必備神器。進(jìn)入程序包管理器控制臺,輸入以下命名安裝EFCore設(shè)計(jì)工具:
※必須安裝在啟動(dòng)項(xiàng)目里面,不然會(huì)失敗。
Install-Package Microsoft.EntityFrameworkCore.Tools
4、創(chuàng)建Migration
程序包管理器控制臺,默認(rèn)項(xiàng)目一定要選擇DB操作上下文的項(xiàng)目,然后執(zhí)行命令:InitDB是文件區(qū)分,可以任意修改。
Add-Migration InitDB
執(zhí)行成功之后,生成帶InitDB區(qū)分的表定義數(shù)據(jù)文件:
6、生成數(shù)據(jù)庫腳本(生產(chǎn)階段用,開發(fā)階段可跳過)
程序包管理器控制臺,執(zhí)行如下命令生成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');
如果系統(tǒng)已經(jīng)上線,安全起見則需要使用這個(gè)方法生成SQL腳本,手動(dòng)執(zhí)行SQL更新數(shù)據(jù)庫。
7、更新數(shù)據(jù)庫(開發(fā)階段用)
程序包管理器控制臺,執(zhí)行如下命令將表定義更新到DB(按文件名的時(shí)間順順添加):
Update-Database
這樣我們就通過類創(chuàng)建了一個(gè)數(shù)據(jù)庫表Users,同時(shí)默認(rèn)會(huì)在__EFMigrationsHistory履歷表添加一條合并記錄。
※如果__EFMigrationsHistory中記錄存在則忽略本次更新。
二、使用DB上下文操作數(shù)據(jù)庫
1、創(chuàng)建IRepository,DB操作基本接口
public interface IRepository where TEntity : class
{
Task GetAsync(int id);
Task AddAsync(TEntity obj);
}
2、創(chuàng)建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);
}
}
如果需要進(jìn)行事務(wù)操作,可以使用下面方法:
var tran= _dbContext.Database.BeginTransaction();
tran.Commit();
3、Service層調(diào)用UserRepository就可以完成用戶的操作。
總結(jié)
此篇量稍微有點(diǎn)多,非常感謝能看到這里。整體來說Prism簡化了應(yīng)用的設(shè)計(jì)與架構(gòu),EFCore簡化了數(shù)據(jù)庫操作。
總結(jié)
以上是生活随笔為你收集整理的prism项目搭建 wpf_Prism完成的一个WPF项目的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: win10安装misql8_Win10安
 - 下一篇: pycharm 敲代码时的效果插件_精选