循序漸進介紹基於CommunityToolkit.Mvvm 和HandyControl的WPF應用端開發(2)

来源:https://www.cnblogs.com/wuhuacong/archive/2023/09/11/17687905.html
-Advertisement-
Play Games

在前面隨筆《循序漸進介紹基於CommunityToolkit.Mvvm 和HandyControl的WPF應用端開發(1)》中介紹了Mvvm 的開發,以及一些界面效果,本篇隨筆繼續深入探討基於CommunityToolkit.Mvvm 和HandyControl的WPF應用端開發,介紹如何整合Sql... ...


在前面隨筆《循序漸進介紹基於CommunityToolkit.Mvvm 和HandyControl的WPF應用端開發(1)》中介紹了Mvvm 的開發,以及一些界面效果,本篇隨筆繼續深入探討基於CommunityToolkit.Mvvm 和HandyControl的WPF應用端開發,介紹如何整合SqlSugar框架的基礎介面,通過基類繼承的方式,簡化實際項目的開發代碼處理。

1、View模塊中的XAML格式說明

在介紹MVVM幾個部分內容之前,我們先連接一下View模塊中的Xaml格式的說明,我們知道Xaml也是一個xml的擴展,屬於標記語言的一種,編輯器為了更好的驗證格式以及提出上下文的智能提示,必然需要確定對應標記元素的格式,這個就是通過Xaml的頭部定義確立了,如下所示。

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WHC.SugarProject.WpfUI.Views.Pages"

    x:Class="WHC.SugarProject.WpfUI.Views.Pages.UserListPage"
    d:DataContext="{d:DesignInstance local:UserListPage,
                                     IsDesignTimeCreatable=False}"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">

其中紅色部分基本上是約定需要輸入的定義了,主要是通過xmlns來定義定義XML的校驗格式,類似我們常用的命名空間的引用地址了。

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

而下麵 xmlns:local 也是定義了本地的XML命名空間。

xmlns:local="clr-namespace:WHC.SugarProject.WpfUI.Views.Pages"

便於在界面元素標記中引用對應的控制項等。而x:Class則是確定XAML的後臺代碼文件的全名稱,使用過早期ASPX的都知道,ASPX文件有一個後臺代碼文件,同理XAML也是類似的概念。

x:Class="WHC.SugarProject.WpfUI.Views.Pages.UserListPage"

而d:開始的那些設置,是指設計樣式下的一些屬性定義,如下所示定義設計窗體的大小,實際運行可能和這個不一樣,因為WPF是屬於矢量的尺寸標記的。

    d:DesignHeight="450"
    d:DesignWidth="800"

d:DataContext則是聲明View和模型綁定的一個重要的說明,界面視圖View可以綁定模型的命令Command,也可以綁定對應的集合或者屬性等。其中local:UserListPage,就是通過簡寫命名控制項local和UserListPage類的組合,實現一個全名稱的快速定義。而IsDesignTimeCreatable則是說明設計狀態下的數據處理方式。

    d:DataContext="{d:DesignInstance local:UserListPage,
                                     IsDesignTimeCreatable=False}"

有時候,我們可能在視窗或者頁面視圖的定義中,還會看到一些其他命名空間的定義或者屬性的定義,如下代碼所示。

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WHC.SugarProject.WpfUI.Views.Pages"

    xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
    xmlns:helpers="clr-namespace:WHC.SugarProject.WpfUI.Helpers"
    xmlns:hc="https://handyorg.github.io/handycontrol"
    xmlns:Controls="clr-namespace:WHC.SugarProject.WpfUI.Controls"

    x:Class="WHC.SugarProject.WpfUI.Views.Pages.UserListPage"
    d:DataContext="{d:DesignInstance local:UserListPage,
                                     IsDesignTimeCreatable=False}"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">

例如上面的紅色部分,就是一般根據實際情況增加的一些控制項的命名空間的定義,便於引用對應的界面控制項進行使用。

<hc:UniformSpacingPanel
    Margin="0,0,0,20"
    HorizontalAlignment="Right"
    Orientation="Horizontal"
    Spacing="10">
    <Button
        Width="60"
        Height="40"
        Command="{Binding SaveCommand}"
        Content="保存"
        Style="{StaticResource ButtonPrimary}" />
    <Button
        Width="60"
        Height="40"
        Command="{Binding ViewModel.BackCommand}"
        CommandParameter="test"
        Content="關閉" />
</hc:UniformSpacingPanel>

有時候可能還會看到一些屬性設置,如背景色,以及是否滾動頁面等設置。

    Background="{DynamicResource RegionBrush}"
    ScrollViewer.CanContentScroll="true"

 

2、MVVM應用中包括Model、View、ViewModel三者內容中的處理

 前面我們見到,View視圖中綁定ViewModel模型的時候,使用d:DataConext進行定義設置,確定視圖模型的內容,我們剛纔介紹的定義是如下所示。

    d:DataContext="{d:DesignInstance local:UserListPage,
                                     IsDesignTimeCreatable=False}"

這裡你看到的它映射向其本身,而非具體的Viewmodel,這裡沒有錯誤,有些MVVM的設置指向具體的ViewModel定義。

我們來看看這個UserListPage的後端類的定義,如下所示。

首先我們看到一個如下所示。

namespace WHC.SugarProject.WpfUI.Views.Pages;

/// <summary>
/// UserListPage.xaml 交互邏輯
/// </summary>
public partial class UserListPage : INavigableView<UserListViewModel>
{
    /// <summary>
    /// 視圖模型對象
    /// </summary>
    public UserListViewModel ViewModel { get; }

    /// <summary>
    /// 構造函數
    /// </summary>
    /// <param name="viewModel">視圖模型對象</param>
    public UserListPage(UserListViewModel viewModel)
    {
        ViewModel = viewModel;
        DataContext = this;

        InitializeComponent();
    }
}

這裡通過泛型介面的方式,定義一個具體類型的視圖模型給視圖View使用。

其中INavigableView介面的定義如下。

public interface INavigableView<out T>
{
    /// <summary>
    /// 視圖的 ViewModel
    /// </summary>
    T ViewModel { get; }
}

這樣我們在Xaml視圖裡面,就可以綁定屬性,也可以綁定相應的Command命令了,可以是類的,也可以是ViewModel的內容。

例如我們在綁定查詢條件處理數據查詢的操作的時候,

 其中界面查詢條件定義了一個文本框名稱的控制項,如下所示。

<TextBox
    Margin="5"
    hc:TitleElement.Title="用戶賬號"
    hc:TitleElement.TitlePlacement="Left"
    Style="{StaticResource TextBoxExtend}"
    Text="{Binding ViewModel.PageDto.Name, UpdateSourceTrigger=PropertyChanged}">
    <TextBox.InputBindings>
        <KeyBinding Key="Enter" Command="{Binding ViewModel.SearchCommand}" />
    </TextBox.InputBindings>
</TextBox>

我們通過 {Binding ViewModel.PageDto.Name, UpdateSourceTrigger=PropertyChanged} 的方式綁定文本的界面顯示內容和後端模型屬性的,並且在綁定的控制項屬性變化的時候觸發更新。這樣只要界面上輸入的內部發生變化,後端綁定的查詢屬性馬上得到更新,我們就可以即時的通過查詢觸發獲得相應條件的記錄了。

另外通過 InputBindings 的方式,接收Enter內容後馬上觸發查詢處理的命令。

查詢的時候,我們在視圖模型上定義一個RelayCommand的特性標註,聲明這個生成的Command為視圖提供處理命令的。

    /// <summary>
    /// 觸發查詢處理命令
    /// </summary>
    /// <returns></returns>
    [RelayCommand]
    private async Task Search()
    {
        //切換第一頁
        this.PagerInfo.CurrentPageIndex = 1;

        //轉換下分頁信息
        ConvertPagingInfo();
        //查詢更新
        await GetData();
    }

然後我們通過轉換分頁條件的信息,獲得查詢條件後進行服務介面的調用,獲取相應條件的數據即可。

這個介面的後端就是SqlSugar框架的標準請求介面了。

    /// <summary>
    /// 根據分頁和查詢條件查詢,請求數據
    /// </summary>
    /// <returns></returns>
    public virtual async Task GetData()
    {
        var result = await service.GetListAsync(this.PageDto);
        if (result != null)
        {
            this.Items = result.Items?.ToList();
            this.PagerInfo.RecordCount = result.TotalCount;
        }
    }

我們看到,由於框架的通用性抽象處理,因此上面的介面應該是可以實現更高層次的抽象處理的,因此我們設計了幾個視圖基類,用於減少代碼的處理。

 其中 ObservableObject 基類是CommunityToolkit.Mvvm模塊裡面定義的視圖模型基類,我們定義的通用基類也是基於它繼承過來即可。

其中BaseViewModel用來處理一些通用的視圖模型操作方式,如跳轉或者返回等。

而BaseListViewModel則是根據查詢列表處理的操作界面所需要的視圖模型處理封裝。

我們看到類定義和我們SqlSugar很多基類定義類似,都是需要通過泛型傳入一些相關的參數,實現更加通用的控制的。

public abstract partial class BaseListViewModel<TEntity, TKey, TGetListInput> : BaseViewModel, INavigationAware
        where TEntity : class, IEntity<TKey>, new()
        where TGetListInput : IPagedAndSortedResultRequest, new()

由於.net的開發方式,現在基本上都是以介面註入方式來處理,這個也是一樣,我們通過註入一個常規服務介面的類,來實現一些常規的請求處理。視圖模型類的介面註入如下所示。

    /// <summary>
    /// 通用基礎操作介面
    /// </summary>
    protected IMyCrudService<TEntity, TKey, TGetListInput> service { get; set; }

    /// <summary>
    /// 構造函數
    /// </summary>
    /// <param name="service">操作介面</param>
    /// <param name="navigationService">視圖導航服務介面</param>
    public BaseListViewModel(IMyCrudService<TEntity, TKey, TGetListInput> service)
    {
        this.service = service;
    }

這樣我們在分頁控制項的頁碼變化的時候,就可以觸發這個基類的命令處理了。

    /// <summary>
    /// 觸發的分頁處理命令
    /// </summary>
    /// <param name="info"></param>
    /// <returns></returns>
    [RelayCommand]
    private async Task PageUpdated(FunctionEventArgs<int> info)
    {
        //根據分頁頁碼展示
        this.PagerInfo.CurrentPageIndex = info.Info;

        //轉換下分頁信息
        ConvertPagingInfo();
        //查詢更新
        await GetData();
    }

它的分頁控制項部分的視圖界面的代碼如下所示。

<hc:Pagination
    Margin="0,10,0,10"
    DataCountPerPage="{Binding ViewModel.PagerInfo.PageSize}"
    IsJumpEnabled="True"
    MaxPageCount="{Binding ViewModel.PagerInfo.MaxPageCount}"
    MaxPageInterval="5"
    PageIndex="{Binding ViewModel.PagerInfo.CurrentPageIndex}">
    <hc:Interaction.Triggers>
        <hc:EventTrigger EventName="PageUpdated">
            <hc:EventToCommand Command="{Binding ViewModel.PageUpdatedCommand}" PassEventArgsToCommand="True" />
        </hc:EventTrigger>
    </hc:Interaction.Triggers>
</hc:Pagination>

而我們查詢命令,也可以通過下麵的基類函數來處理了。

    /// <summary>
    /// 觸發查詢處理命令
    /// </summary>
    /// <returns></returns>
    [RelayCommand]
    private async Task Search()
    {
        //切換第一頁
        this.PagerInfo.CurrentPageIndex = 1;

        //轉換下分頁信息
        ConvertPagingInfo();
        //查詢更新
        await GetData();
    }

這樣我們在前面介紹的文本框的TextBox的InputBindings處理就可以直接使用這個RelayCommand聲明的命令函數了。

    <TextBox.InputBindings>
        <KeyBinding Key="Enter" Command="{Binding ViewModel.SearchCommand}" />
    </TextBox.InputBindings>

當然,這個基類裡面還可以定義其他通用用到的一些常規處理,如導入導出、刪除和批量刪除等常規的介面。

而我們在這個用戶業務模型裡面所需要做的就是做一下繼承關係的處理即可,如下代碼所示。

namespace WHC.SugarProject.WpfUI.ViewModels;

/// <summary>
/// 用戶列表-視圖模型對象
/// </summary>
public partial class UserListViewModel : BaseListViewModel<UserInfo, int, UserPagedDto>
{
    /// <summary>
    /// 構造函數
    /// </summary>
    /// <param name="service">業務服務介面</param>
    public UserListViewModel(IUserService service) : base(service)
    {
    }
}

這樣 UserListViewModel 就具有了一些通用的業務處理對象和命令了,包括常規的查詢、刪除、批量刪除、導入、導出的處理,都可以了。

同理,對於編輯具體單個記錄的處理,我們也可以使用通用的抽象業務類封裝來實現,如下代碼所示。

/// <summary>
/// 用戶新增、編輯-視圖模型
/// </summary>
public partial class UserEditViewModel : BaseEditViewModel<UserInfo, int, UserPagedDto>
{
    /// <summary>
    /// 構造函數
    /// </summary>
    /// <param name="service"></param>
    public UserEditViewModel(IUserService service) : base(service)
    {
        this.Title = "用戶信息";
    }
}

至此,整個視圖模型ViewModel的繼承關係如下所示。對於不同的業務類,我們也只需要根據實際情況,生成對應的業務視圖模型類即可。

DataGrid列表展示的處理代碼,主要就是基類觸發查詢更新後,屬性ViewModel.Items 的記錄獲得更新,這樣DataGrid就可以順利的顯示對應的數據了,如下界面所示。

 

3、界面的統一處理和代碼生成處理

對於界面上的處理,我們常規的列表和編輯/新增界面,基本上可以滿足大多數的要求,如下是列表界面的效果,包括查詢條件、常規處理按鈕、列表展示、分頁信息展示等幾個部分,如下所示。

 對於一些編輯界面,也是類似下麵的佈局即可。

 當然可以根據資料庫欄位進行設置展示那些輸入信息最好了。如對於系統參數設置模塊的信息,我們界面如下所示。

 這些內容相對比較標準和統一,可以結合資料庫表信息使用統一的方式快速生成即可,因此我把它的生成規則結合到我們的代碼生成工具(Database2Sharp)中進行生成處理,通過選擇那些欄位來實現更加精確的界面處理。

 視圖界面代碼如下所示,包括Xaml和後端類的代碼。

 對應有兩個視圖模型ViewModel的繼承類。

 這樣實現,可以極大程度的減少子類的代碼,以及通過代碼生成工具快速定義生成的方式,又可以極大的提高開發效率,雙管齊下,可以提高整個項目的開發效率。

專註於代碼生成工具、.Net/.NetCore 框架架構及軟體開發,以及各種Vue.js的前端技術應用。著有Winform開發框架/混合式開發框架、微信開發框架、Bootstrap開發框架、ABP開發框架、SqlSugar開發框架等框架產品。
  轉載請註明出處:撰寫人:伍華聰  http://www.iqidi.com 
    

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.網關介紹 如果沒有網關,難道不行嗎?功能上是可以的,我們直接調用提供的介面就可以了。那為什麼還需要網關? 因為網關的作用不僅僅是轉發請求而已。我們可以試想一下,如果需要做一個請求認證功能,我們可以接入到 API 服務中。但是倘若後續又有服務需要接入,我們又需要重覆接入。這樣我們不僅代碼要重覆編寫 ...
  • 開心一刻 昨晚和一個朋友聊天 我:處對象嗎,咱倆試試? 朋友:我有對象 我:我不信,有對象不公開? 朋友:不好公開,我當的小三 問題背景 程式在生產環境穩定的跑著 直到有一天,公司執行組件漏洞掃描,有漏洞的 jar 要進行升級修複 然後我就按著掃描報告將有漏洞的 jar 修複到指定的版本 自己在開發 ...
  • Unity 性能優化Shader分析處理函數:ShaderUtil.GetShaderGlobalKeywords用法 點擊封面跳轉下載頁面 簡介 Unity 性能優化Shader分析處理函數:ShaderUtil.GetShaderGlobalKeywords用法 在Unity開發中,性能優化是一 ...
  • try catch使用場景: 1. 一般線上程,委托中使用, 線上程與委托中使用是因為,如果線程和委托中出現異常在程式外部是捕獲不到的,需要在內部做單獨處理。 2. 程式的外層使用,比如程式的入口處加一個全局異常捕獲,這樣整個程式發生的異常都可以捕獲到。 3. 在事件或者主體方法中使用,一些小的公共 ...
  • 1. 什麼是IDisposable? IDisposable介面是一個用於約定可進行釋放資源操作的介面,一個類實現該介面則意味著可以使用介面約定的方法Dispose來釋放資源。其定義如下: public interface IDisposable { void Dispose(); } 上述描述中可 ...
  • 針對工業來講,特殊是流程行業,視圖建模(Web組態)是必不可少應用場景,因為有很多工序要直觀的展示工藝流程圖。該功能也廣泛應用在:鋼鐵、煤炭、水務、中醫葯、能源、航天、加工製造、風電、儲能等領域。 ...
  • 前言 組合模式,英文名稱是:Composite Pattern。當我們談到這個模式的時候,有一個物件和這個模式很像,也符合這個模式要表達的意思,那就是“俄羅斯套娃”。“俄羅斯套娃”就是大的瓷器娃娃裡面裝著一個小的瓷器娃娃,小的瓷器娃娃裡面再裝著更小的瓷器娃娃,直到最後一個不能再裝更小的瓷器娃娃的那個 ...
  • 引子 "某呼"網友提問“C#為什麼非要把函數叫方法?”,看到這個問題還真不知道怎麼回答。要想知道C#為什麼叫方法,還得從傳統面向過程語言說起。 方法的由來 函數(function)是面向過程編程語言里,對可以獨立調用代碼段的另一種叫法,因為通常有數個輸入和一個輸出,很像數學里的那個函數。比如C語言中 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 微服務架構已經成為搭建高效、可擴展系統的關鍵技術之一,然而,現有許多微服務框架往往過於複雜,使得我們普通開發者難以快速上手並體驗到微服務帶了的便利。為瞭解決這一問題,於是作者精心打造了一款最接地氣的 .NET 微服務框架,幫助我們輕鬆構建和管理微服務應用。 本框架不僅支持 Consul 服務註 ...
  • 先看一下效果吧: 如果不會寫動畫或者懶得寫動畫,就直接交給Blend來做吧; 其實Blend操作起來很簡單,有點類似於在操作PS,我們只需要設置關鍵幀,滑鼠點來點去就可以了,Blend會自動幫我們生成我們想要的動畫效果. 第一步:要創建一個空的WPF項目 第二步:右鍵我們的項目,在最下方有一個,在B ...
  • Prism:框架介紹與安裝 什麼是Prism? Prism是一個用於在 WPF、Xamarin Form、Uno 平臺和 WinUI 中構建鬆散耦合、可維護和可測試的 XAML 應用程式框架 Github https://github.com/PrismLibrary/Prism NuGet htt ...
  • 在WPF中,屏幕上的所有內容,都是通過畫筆(Brush)畫上去的。如按鈕的背景色,邊框,文本框的前景和形狀填充。藉助畫筆,可以繪製頁面上的所有UI對象。不同畫筆具有不同類型的輸出( 如:某些畫筆使用純色繪製區域,其他畫筆使用漸變、圖案、圖像或繪圖)。 ...
  • 前言 嗨,大家好!推薦一個基於 .NET 8 的高併發微服務電商系統,涵蓋了商品、訂單、會員、服務、財務等50多種實用功能。 項目不僅使用了 .NET 8 的最新特性,還集成了AutoFac、DotLiquid、HangFire、Nlog、Jwt、LayUIAdmin、SqlSugar、MySQL、 ...
  • 本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...