在我們開發的前端項目中,往往為了方便,都需對一些控制項進行自定義的處理,以便實現快速的數據綁定以及便捷的使用,本篇隨筆介紹通過抽取常見字典列表,實現通用的字典類型綁定;以及通過自定義控制項的屬性處理,實現系統字典內容的快捷綁定的操作。 ...
在我們開發的前端項目中,往往為了方便,都需對一些控制項進行自定義的處理,以便實現快速的數據綁定以及便捷的使用,本篇隨筆介紹通過抽取常見字典列表,實現通用的字典類型綁定;以及通過自定義控制項的屬性處理,實現系統字典內容的快捷綁定的操作。
1、下拉列表的數據綁定
在我們創建下拉列表的時候,我們一般處理方式,是在對應的數據模型中添加對應的下拉列表的集合對象,然後在控制項綁定對應的ItemSource,如下所示是視圖模型,我們增加一個性別的列表參考。
/// <summary> /// 用戶列表-視圖模型對象 /// </summary> public partial class UserListViewModel : BaseListViewModel<UserInfo, int, UserPagedDto> { /// <summary> /// 性別 /// </summary> [ObservableProperty] private List<CListItem> genderItems; /// <summary> /// 構造函數 /// </summary> /// <param name="service">業務服務介面</param> public UserListViewModel(IUserService service) : base(service) { //初始化性別的列表 this.GenderItems = new List<CListItem>() { new CListItem("男"), new CListItem("女") }; }
然後初始化後,就可以在界面上進行數據的綁定了,如下是對應控制項的界面代碼。
<hc:ComboBox Margin="5" hc:TitleElement.Title="性別" hc:TitleElement.TitlePlacement="Left" DisplayMemberPath="Text" ItemsSource="{Binding ViewModel.GenderItems}" SelectedValue="{Binding ViewModel.PageDto.Gender, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Value" ShowClearButton="True" />
這種方式可能是經常用到的方式,隨著不同界面代碼的編寫,我們發現很多這樣下拉列表,如機構可能有一些類別(來自枚舉對象)需要處理,其他頁面也有類似的需求。
/// <summary> /// 機構(部門)信息 列表-視圖模型對象 /// </summary> public partial class OuListViewModel : BaseListViewModel<OuInfo, int, OuPagedDto> { /// <summary> /// 機構分類 /// </summary> [ObservableProperty] private List<CListItem> categoryItems = new(); /// <summary> /// 構造函數 /// </summary> /// <param name="service">業務服務介面</param> public OuListViewModel(IOuService service) : base(service) { //機構分類 string[] enumNames = EnumHelper.GetMemberNames<OUCategoryEnum>(); this.CategoryItems.Clear(); this.CategoryItems.AddRange(enumNames.Select(s => new CListItem(s))); }
如果每次都需要在對應的視圖模型上創建這些列表,則顯得累贅、臃腫。因為這些下拉列表的內容,是界面中常用到的列表,我們是否可以把它作為一個公用的對象模型來使用呢。
為了方便,我們來創建一個對象DictItemsModel ,用來初始化系統用到的所有參考列表對象,如下代碼所示。
/// <summary> /// 定義一些系統常用的字典項目,供頁面參考引用 /// </summary> public partial class DictItemsModel : ObservableObject { /// <summary> /// 性別 /// </summary> [ObservableProperty] private List<CListItem> genderItems = new(); /// <summary> /// 機構分類 /// </summary> [ObservableProperty] private List<CListItem> ouCategoryItems = new(); //******更多列表處理********** /// <summary> /// 構造函數 /// </summary> public DictItemsModel() { InitDictItem(); // 初始化字典 } /// <summary> /// 初始化字典 /// </summary> /// <returns></returns> public async Task InitDictItem() { //初始化性別的列表 this.GenderItems = new List<CListItem>() { new(""), new("男"), new("女") }; //機構分類 this.OuCategoryItems = EnumHelper.GetMemberNames<OUCategoryEnum>().Select(s => new CListItem(s)).ToList(); this.OuCategoryItems.Insert(0, new CListItem("")); //********************* } }
然後,我們在應用程式的XAML代碼中,引入對應的靜態資源,相當於每次使用這些的時候,是使用該對象的實例。
<Application x:Class="WHC.SugarProject.WpfUI.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:helpers="clr-namespace:WHC.SugarProject.WpfUI.Helpers" xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" DispatcherUnhandledException="OnDispatcherUnhandledException" Exit="OnExit" Startup="OnStartup"> <Application.Resources> <ResourceDictionary> <!-- 整合所有用到的轉義輔助類,減少頁面中添加的處理代碼 --> <helpers:IntToBooleanConverter x:Key="IntToBooleanConverter" /> <helpers:DictItemsModel x:Key="DictItemsModel" /> </ResourceDictionary> </Application.Resources> </Application>
有了這些定義,我們的下拉列表的數據源ItemSource的屬性改動一下就可以實現一致的效果了,相當於抽取了字典列表到獨立的類中處理了。
<!-- ItemsSource="{Binding ViewModel.GenderItems}" --> <hc:ComboBox Margin="5" hc:TitleElement.Title="性別" hc:TitleElement.TitlePlacement="Left" DisplayMemberPath="Text" ItemsSource="{Binding Path=GenderItems, Source={StaticResource DictItemsModel}}" SelectedValue="{Binding ViewModel.PageDto.Gender, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Value" ShowClearButton="True" />
<!-- ItemsSource="{Binding ViewModel.CategoryItems}" --> <hc:ComboBox Margin="5" hc:TitleElement.Title="機構分類" hc:TitleElement.TitlePlacement="Left" DisplayMemberPath="Text" ItemsSource="{Binding Path=OuCategoryItems, Source={StaticResource DictItemsModel}}" SelectedValue="{Binding ViewModel.PageDto.Category, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Value" ShowClearButton="True" />
上面代碼中,我們通過Source={StaticResource DictItemsModel}來指定數據源的位置來自靜態資源即可。如機構列表,通過枚舉進行解析到的集合如下所示,當然其他數據也可以通過相應的處理實現。
通過這種把常見的字典類別集中到一個類中進行維護,除了統一處理列表的初始化外,也方便我們在界面代碼中的統一使用。
2、自定義系統字典列表控制項
我們框架一般都維護一個通用的字典類型和字典項目的信息,通過維護這些常見的系統字典信息,可以為我們的界面的一些下拉類列表提供數據支持,是指實現通用、統一的字典處理。
以前在Winform中綁定字典列表的時候,一般通過擴展函數BindDictItems就可以實現類型綁定了,有興趣可以瞭解下隨筆《在Winform開發框架中下拉列表綁定字典以及使用緩存提高界面顯示速度》、《使用擴展函數方式,在Winform界面中快捷的綁定樹形列表TreeList控制項和TreeListLookUpEdit控制項》、《在Winform開發中,我們使用的幾種下拉列表展示字典數據的方式》、《在各種開發項目中使用公用類庫的擴展方法,通過上下文方式快速調用處理函數》。
對WPF來說,我們需要改變下思路,和Vue3的BS的控制項的處理方式類似,我們通過給他指定一個字典類型的名稱,讓它自己取得對應列表,進行綁定處理即可,因此我們自定義字典列表控制項即可。
/// <summary> /// 自定義下拉列表,方便綁定字典類型 /// </summary> public class ComboBox : HandyControl.Controls.ComboBox
創建一個繼承自所需下拉列表控制項,可以使用原生控制項繼承,不過我這裡偏向於UI更好的HandyControl的ComboBox。
然後給它指定對應的字典類型屬性,對應我們系統的字典大類名稱。
/// <summary> /// 自定義下拉列表,方便綁定字典類型 /// </summary> public class ComboBox : HandyControl.Controls.ComboBox { /// <summary> /// 字典類型名稱 /// </summary> public string? DictTypeName { get { return (string?)GetValue(DictTypeNameProperty); } set { SetValue(DictTypeNameProperty, value); } } public static readonly DependencyProperty DictTypeNameProperty = DependencyProperty.Register( nameof(DictTypeName), typeof(string), typeof(ComboBox), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnDictTypeNamePropertyChanged)));
封裝過自定義的WPF控制項的話,我們知道,增加一個自定義屬性,就需要同時增加一個自定義屬性+Property的 DependencyProperty 屬性對象,如上代碼所示。
通過PropertyChangedCallback的回調處理,實現設置字典類型值後觸發控制項內部數據的處理邏輯,也就是需要從字典服務中獲取下拉類別數據,變為控制項的ItemSource集合即可。
一般情況下,我們實現下麵的代碼邏輯,獲得數據源就差不過可以了。
private static async void OnDictTypeNamePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not ComboBox control) return; if (control != null) { var oldValue = (string?)e.OldValue; // 舊的值 var newValue = (string?)e.NewValue; // 更新的新的值 //更新下拉列表的數據源 if(!newValue.IsNullOrEmpty() && !control.IsInDesignMode()) { var itemList = await BLLFactory<IDictDataService>.Instance.GetListItemByDictType(newValue); if (itemList != null) { itemList.Insert(0, new CListItem(""));//CListItem具有Text、Value兩個屬性 control.ItemsSource = itemList; control.ShowClearButton = true; control.SelectedValuePath = control.SelectedValuePath.IsNullOrEmpty() ? "Value" : control.SelectedValuePath; } } } }
同理,我們按照同樣的處理方式,做一個覆選框的下拉列表CheckComboBox。
/// <summary> /// 自定義下拉列表,方便綁定字典類型 /// </summary> public class CheckComboBox : HandyControl.Controls.CheckComboBox { /// <summary> /// 字典類型名稱 /// </summary> public string? DictTypeName { get { return (string?)GetValue(DictTypeNameProperty); } set { SetValue(DictTypeNameProperty, value); } } public static readonly DependencyProperty DictTypeNameProperty = DependencyProperty.Register( nameof(DictTypeName), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnDictTypeNamePropertyChanged)));
完成上面的自定義控制項編寫,我們需要在UI上放置控制項,指定它的指定類型就可以了。
<control:ComboBox Width="250" Height="32" VerticalAlignment="Center" hc:InfoElement.Title="客戶類型" hc:InfoElement.TitlePlacement="Left" DictTypeName="客戶類型" SelectedValue="{Binding ViewModel.PageDto.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ShowClearButton="True" Style="{StaticResource ComboBoxPlusBaseStyle}" /> <control:CheckComboBox Width="500" Height="32" VerticalAlignment="Center" hc:InfoElement.Title="客戶類型" hc:InfoElement.TitlePlacement="Left" DictTypeName="客戶類型" SelectedValue="{Binding ViewModel.PageDto.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Value" ShowClearButton="True" ShowSelectAllButton="True" Style="{StaticResource CheckComboBoxPlus}" />
完成後,我們測試下自定義控制項的處理效果。
效果符合實際的期望。而且代碼和普通WPF控制項的使用類似,只需要增加一個 DictTypeName="客戶類型" 的類似寫法即可。可以極大的減輕我們綁定常見系統字典的下拉列表的複雜度。
界面效果如下所示。
專註於代碼生成工具、.Net/.NetCore 框架架構及軟體開發,以及各種Vue.js的前端技術應用。著有Winform開發框架/混合式開發框架、微信開發框架、Bootstrap開發框架、ABP開發框架、SqlSugar開發框架等框架產品。
轉載請註明出處:撰寫人:伍華聰 http://www.iqidi.com