在我們開發(fā)的前端項目中,往往為了方便,都需對一些控件進行自定義的處理,以便實現(xiàn)快速的數(shù)據(jù)綁定以及便捷的使用,本篇隨筆介紹通過抽取常見字典列表,實現(xiàn)通用的字典類型綁定;以及通過自定義控件的屬性處理,實現(xiàn)系統(tǒng)字典內(nèi)容的快捷綁定的操作。
1、下拉列表的數(shù)據(jù)綁定
在我們創(chuàng)建下拉列表的時候,我們一般處理方式,是在對應的數(shù)據(jù)模型中添加對應的下拉列表的集合對象,然后在控件綁定對應的ItemSource,如下所示是視圖模型,我們增加一個性別的列表參考。
/// <summary> /// 用戶列表-視圖模型對象 /// </summary> public partial class UserListViewModel : BaseListViewModel<UserInfo, int, UserPagedDto> { /// <summary> /// 性別 /// </summary> [ObservableProperty] private List<CListItem> genderItems; /// <summary> /// 構(gòu)造函數(shù) /// </summary> /// <param name="service">業(yè)務服務接口</param> public UserListViewModel(IUserService service) : base(service) { //初始化性別的列表 this.GenderItems = new List<CListItem>() { new CListItem("男"), new CListItem("女") }; }
然后初始化后,就可以在界面上進行數(shù)據(jù)的綁定了,如下是對應控件的界面代碼。
<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" />
?這種方式可能是經(jīng)常用到的方式,隨著不同界面代碼的編寫,我們發(fā)現(xiàn)很多這樣下拉列表,如機構(gòu)可能有一些類別(來自枚舉對象)需要處理,其他頁面也有類似的需求。
/// <summary> /// 機構(gòu)(部門)信息 列表-視圖模型對象 /// </summary> public partial class OuListViewModel : BaseListViewModel<OuInfo, int, OuPagedDto> { /// <summary> /// 機構(gòu)分類 /// </summary> [ObservableProperty] private List<CListItem> categoryItems = new(); /// <summary> /// 構(gòu)造函數(shù) /// </summary> /// <param name="service">業(yè)務服務接口</param> public OuListViewModel(IOuService service) : base(service) { //機構(gòu)分類 string[] enumNames = EnumHelper.GetMemberNames<OUCategoryEnum>(); this.CategoryItems.Clear(); this.CategoryItems.AddRange(enumNames.Select(s => new CListItem(s))); }
如果每次都需要在對應的視圖模型上創(chuàng)建這些列表,則顯得累贅、臃腫。因為這些下拉列表的內(nèi)容,是界面中常用到的列表,我們是否可以把它作為一個公用的對象模型來使用呢。
為了方便,我們來創(chuàng)建一個對象DictItemsModel ,用來初始化系統(tǒng)用到的所有參考列表對象,如下代碼所示。
/// <summary> /// 定義一些系統(tǒng)常用的字典項目,供頁面參考引用 /// </summary> public partial class DictItemsModel : ObservableObject { /// <summary> /// 性別 /// </summary> [ObservableProperty] private List<CListItem> genderItems = new(); /// <summary> /// 機構(gòu)分類 /// </summary> [ObservableProperty] private List<CListItem> ouCategoryItems = new(); //******更多列表處理********** /// <summary> /// 構(gòu)造函數(shù) /// </summary> public DictItemsModel() { InitDictItem(); // 初始化字典 } /// <summary> /// 初始化字典 /// </summary> /// <returns></returns> public async Task InitDictItem() { //初始化性別的列表 this.GenderItems = new List<CListItem>() { new(""), new("男"), new("女") }; //機構(gòu)分類 this.OuCategoryItems = EnumHelper.GetMemberNames<OUCategoryEnum>().Select(s => new CListItem(s)).ToList(); this.OuCategoryItems.Insert(0, new CListItem("")); //********************* } }
然后,我們在應用程序的XAML代碼中,引入對應的靜態(tài)資源,相當于每次使用這些的時候,是使用該對象的實例。
<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> <!-- 整合所有用到的轉(zhuǎn)義輔助類,減少頁面中添加的處理代碼 --> <helpers:IntToBooleanConverter x:Key="IntToBooleanConverter" /> <helpers:DictItemsModel x:Key="DictItemsModel" /> </ResourceDictionary> </Application.Resources> </Application>
有了這些定義,我們的下拉列表的數(shù)據(jù)源ItemSource的屬性改動一下就可以實現(xiàn)一致的效果了,相當于抽取了字典列表到獨立的類中處理了。
<!-- 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="機構(gòu)分類" 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}來指定數(shù)據(jù)源的位置來自靜態(tài)資源即可。如機構(gòu)列表,通過枚舉進行解析到的集合如下所示,當然其他數(shù)據(jù)也可以通過相應的處理實現(xiàn)。
??
通過這種把常見的字典類別集中到一個類中進行維護,除了統(tǒng)一處理列表的初始化外,也方便我們在界面代碼中的統(tǒng)一使用。
2、自定義系統(tǒng)字典列表控件
我們框架一般都維護一個通用的字典類型和字典項目的信息,通過維護這些常見的系統(tǒng)字典信息,可以為我們的界面的一些下拉類列表提供數(shù)據(jù)支持,是指實現(xiàn)通用、統(tǒng)一的字典處理。
以前在Winform中綁定字典列表的時候,一般通過擴展函數(shù)BindDictItems就可以實現(xiàn)類型綁定了,有興趣可以了解下隨筆《在Winform開發(fā)框架中下拉列表綁定字典以及使用緩存提高界面顯示速度》、《使用擴展函數(shù)方式,在Winform界面中快捷的綁定樹形列表TreeList控件和TreeListLookUpEdit控件》、《在Winform開發(fā)中,我們使用的幾種下拉列表展示字典數(shù)據(jù)的方式》、《在各種開發(fā)項目中使用公用類庫的擴展方法,通過上下文方式快速調(diào)用處理函數(shù)》。
對WPF來說,我們需要改變下思路,和Vue3的BS的控件的處理方式類似,我們通過給他指定一個字典類型的名稱,讓它自己取得對應列表,進行綁定處理即可,因此我們自定義字典列表控件即可。
/// <summary> /// 自定義下拉列表,方便綁定字典類型 /// </summary> public class ComboBox : HandyControl.Controls.ComboBox
創(chuàng)建一個繼承自所需下拉列表控件,可以使用原生控件繼承,不過我這里偏向于UI更好的HandyControl的ComboBox。
然后給它指定對應的字典類型屬性,對應我們系統(tǒng)的字典大類名稱。
/// <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的回調(diào)處理,實現(xiàn)設(shè)置字典類型值后觸發(fā)控件內(nèi)部數(shù)據(jù)的處理邏輯,也就是需要從字典服務中獲取下拉類別數(shù)據(jù),變?yōu)榭丶腎temSource集合即可。
一般情況下,我們實現(xiàn)下面的代碼邏輯,獲得數(shù)據(jù)源就差不過可以了。
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; // 更新的新的值 //更新下拉列表的數(shù)據(jù)源 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="客戶類型"?的類似寫法即可??梢詷O大的減輕我們綁定常見系統(tǒng)字典的下拉列表的復雜度。
界面效果如下所示。
文章來源:http://www.zghlxwxcb.cn/news/detail-711112.html
?文章來源地址http://www.zghlxwxcb.cn/news/detail-711112.html
到了這里,關(guān)于循序漸進介紹基于CommunityToolkit.Mvvm 和HandyControl的WPF應用端開發(fā)(11) -- 下拉列表的數(shù)據(jù)綁定以及自定義系統(tǒng)字典列表控件的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!