循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件

简介: 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件

在我们开发的前端项目中,往往为了方便,都需对一些控件进行自定义的处理,以便实现快速的数据绑定以及便捷的使用,本篇随笔介绍通过抽取常见字典列表,实现通用的字典类型绑定;以及通过自定义控件的属性处理,实现系统字典内容的快捷绑定的操作。

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="客户类型" 的类似写法即可。可以极大的减轻我们绑定常见系统字典的下拉列表的复杂度。

界面效果如下所示。

 

链接附注

如对我们的代码生成工具有兴趣,可以到官网下载使用《代码生成工具Database2Sharp》。

如需了解我们官网对《SqlSugar开发框架》的介绍,可以参考《SqlSugar开发框架》了解。

如需阅读我们对于《SqlSugar开发框架》文章介绍,可以参考博客园的随笔标签《SqlSugar随笔 , WPF随笔》学习了解。

 

专注于代码生成工具、.Net/.NetCore 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架等框架产品。
 转载请注明出处:撰写人:伍华聪  http://www.iqidi.com
   

相关文章
|
4月前
|
C# UED 开发者
WPF与性能优化:掌握这些核心技巧,让你的应用从卡顿到丝滑,彻底告别延迟,实现响应速度质的飞跃——从布局到动画全面剖析与实例演示
【8月更文挑战第31天】本文通过对比优化前后的方法,详细探讨了提升WPF应用响应速度的策略。文章首先分析了常见的性能瓶颈,如复杂的XAML布局、耗时的事件处理、不当的数据绑定及繁重的动画效果。接着,通过具体示例展示了如何简化XAML结构、使用后台线程处理事件、调整数据绑定设置以及利用DirectX优化动画,从而有效提升应用性能。通过这些优化措施,WPF应用将更加流畅,用户体验也将得到显著改善。
305 1
|
4月前
|
容器 C# Docker
WPF与容器技术的碰撞:手把手教你Docker化WPF应用,实现跨环境一致性的开发与部署
【8月更文挑战第31天】容器技术简化了软件开发、测试和部署流程,尤其对Windows Presentation Foundation(WPF)应用程序而言,利用Docker能显著提升其可移植性和可维护性。本文通过具体示例代码,详细介绍了如何将WPF应用Docker化的过程,包括创建Dockerfile及构建和运行Docker镜像的步骤。借助容器技术,WPF应用能在任何支持Docker的环境下一致运行,极大地提升了开发效率和部署灵活性。
151 1
|
4月前
|
安全 C# 数据安全/隐私保护
WPF安全加固全攻略:从数据绑定到网络通信,多维度防范让你的应用固若金汤,抵御各类攻击
【8月更文挑战第31天】安全性是WPF应用程序开发中不可或缺的一部分。本文从技术角度探讨了WPF应用面临的多种安全威胁及防护措施。通过严格验证绑定数据、限制资源加载来源、实施基于角色的权限管理和使用加密技术保障网络通信安全,可有效提升应用安全性,增强用户信任。例如,使用HTML编码防止XSS攻击、检查资源签名确保其可信度、定义安全策略限制文件访问权限,以及采用HTTPS和加密算法保护数据传输。这些措施有助于全面保障WPF应用的安全性。
62 0
|
4月前
|
C# 开发者 Windows
全面指南:WPF无障碍设计从入门到精通——让每一个用户都能无障碍地享受你的应用,从自动化属性到焦点导航的最佳实践
【8月更文挑战第31天】为了确保Windows Presentation Foundation (WPF) 应用程序对所有用户都具备无障碍性,开发者需关注无障碍设计原则。这不仅是法律要求,更是社会责任,旨在让技术更人性化,惠及包括视障、听障及行动受限等用户群体。
95 0
|
4月前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
118 0
|
4月前
|
存储 C# 关系型数据库
“云端融合:WPF应用无缝对接Azure与AWS——从Blob存储到RDS数据库,全面解析跨平台云服务集成的最佳实践”
【8月更文挑战第31天】本文探讨了如何将Windows Presentation Foundation(WPF)应用与Microsoft Azure和Amazon Web Services(AWS)两大主流云平台无缝集成。通过具体示例代码展示了如何利用Azure Blob Storage存储非结构化数据、Azure Cosmos DB进行分布式数据库操作;同时介绍了如何借助Amazon S3实现大规模数据存储及通过Amazon RDS简化数据库管理。这不仅提升了WPF应用的可扩展性和可用性,还降低了基础设施成本。
95 0
|
4月前
|
vr&ar C# 图形学
WPF与AR/VR的激情碰撞:解锁Windows Presentation Foundation应用新维度,探索增强现实与虚拟现实技术在现代UI设计中的无限可能与实战应用详解
【8月更文挑战第31天】增强现实(AR)与虚拟现实(VR)技术正迅速改变生活和工作方式,在游戏、教育及工业等领域展现出广泛应用前景。本文探讨如何在Windows Presentation Foundation(WPF)环境中实现AR/VR功能,通过具体示例代码展示整合过程。尽管WPF本身不直接支持AR/VR,但借助第三方库如Unity、Vuforia或OpenVR,可实现沉浸式体验。例如,通过Unity和Vuforia在WPF中创建AR应用,或利用OpenVR在WPF中集成VR功能,从而提升用户体验并拓展应用功能边界。
86 0
|
4月前
|
区块链 C# 存储
链动未来:WPF与区块链的创新融合——从智能合约到去中心化应用,全方位解析开发安全可靠DApp的最佳路径
【8月更文挑战第31天】本文以问答形式详细介绍了区块链技术的特点及其在Windows Presentation Foundation(WPF)中的集成方法。通过示例代码展示了如何选择合适的区块链平台、创建智能合约,并在WPF应用中与其交互,实现安全可靠的消息存储和检索功能。希望这能为WPF开发者提供区块链技术应用的参考与灵感。
68 0
|
4月前
|
C# 机器学习/深度学习 搜索推荐
WPF与机器学习的完美邂逅:手把手教你打造一个具有智能推荐功能的现代桌面应用——从理论到实践的全方位指南,让你的应用瞬间变得高大上且智能无比
【8月更文挑战第31天】本文详细介绍如何在Windows Presentation Foundation(WPF)应用中集成机器学习功能,以开发具备智能化特性的桌面应用。通过使用Microsoft的ML.NET框架,本文演示了从安装NuGet包、准备数据集、训练推荐系统模型到最终将模型集成到WPF应用中的全过程。具体示例代码展示了如何基于用户行为数据训练模型,并实现实时推荐功能。这为WPF开发者提供了宝贵的实践指导。
55 0
|
4月前
|
开发者 C# UED
WPF与多媒体:解锁音频视频播放新姿势——从界面设计到代码实践,全方位教你如何在WPF应用中集成流畅的多媒体功能
【8月更文挑战第31天】本文以随笔形式介绍了如何在WPF应用中集成音频和视频播放功能。通过使用MediaElement控件,开发者能轻松创建多媒体应用程序。文章详细展示了从创建WPF项目到设计UI及实现媒体控制逻辑的过程,并提供了完整的示例代码。此外,还介绍了如何添加进度条等额外功能以增强用户体验。希望本文能为WPF开发者提供实用的技术指导与灵感。
175 0