首页 > 其他分享 >一个可以让你有更多时间摸鱼的WPF控件(一)

一个可以让你有更多时间摸鱼的WPF控件(一)

时间:2024-03-29 10:44:58浏览次数:14  
标签:控件 set string get 摸鱼 WPF ref public

前言

我们平时在开发软件的过程中,有这样一类比较常见的功能,它没什么技术含量,开发起来也没有什么成就感,但是你又不得不花大量的时间来处理它,它就是对数据的增删改查。当我们每增加一个需求就需要对应若干个页面来处理数据的添加、修改、删除、查询,每个页面因为数据字段的差异需要单独处理布局及排列,在处理数据录入或修改的过程中还需要校验数据的准确性。那么有没有什么方法可以简化这个过程,提升我们的工作效率?今天这篇文章我们来讨论这个问题。

一、务分析

    我们分析一下传统的开发流程,看看有哪些地方可以优化。

1.1 添加数据

    数据录入的时候我们需要先确定要录入哪些数据,每条数据都是什么类型,然后在新增数据界面上设计布局,确认参数的排列方式,还需要做必要的数据校验工作,比如判断输入是否为空,判断电子邮箱格式是否正确,判断电话号码格式是否正确等,有时还需要在输入前提示一些必要的信息,比如告知用户正确的输入格式,限定输入的内容必须为某些字符等。

    优化方案:

        1)使用反射读取实体类属性,根据实体类的属性类型生成不同的数据录入控件。

        2)实体类实现IDataErrorInfo接口,实现数据校验功能。

1.2 修改数据

    基本与添加数据一致。

1.3 删除数据

    选中数据后执行删除操作,基本无优化空间。

1.4 查询数据

    查询数据一般使用DataGrid或ListView作为数据列表使用,DataGrid对很多表格功能做了封装,它可以对每行的数据进行编辑,可以自动生成列,如果不是特别复杂的需求使用DataGrid的自动列,确实可以节省很多工作,只要简单的绑定一下就可以使用这些功能。但是真实的业务场景需求千变万化,我们来看看会碰到哪些问题。

      1) 设置自动列的时候,DataGrid列显示的是属性名,而属性名往往都是英文的,中文环境中基本都是使用中文列名。

      2) 设置自动列的时候无法对数据进行格式化操作,无法使用转换器。

      3) 设置自动列时无法对列的顺序做自定义排列。

     4) 设置自动列时无法控制自定义列的排列,比如在第一列设置一个CheckBox复选框,在列尾设置编辑、删除按钮等。

      5)设置自动列时无法单独指定某列的宽度。

      6)设置自动列时无法单独隐藏某些列。

    虽然以上问题也有解决方案,但是实现起来略显繁琐。

    优化方案:

        1)为实体类开发一个特性类(ColumnAttribute),添加列名、排序、宽度、是否可见、转换器、格式化字符串等属性。

         2)添加一个ListView控件附加属性,读取以上特性,在ListView控件附加属性上实现以上功能。

二、例代码

    通过对业务需求的分析,我们总结出了几点优化方案,下面展示根据优化方案开发出来的Form控件,该控件可以大大节省开发期间枯燥的重复工作,提升工作效率。

 

View

<Window
    x:Class="QuShi.Controls.Samples.Views.EntityEditorView1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
    xmlns:prism="http://prismlibrary.com/"
    Width="450"
    prism:ViewModelLocator.AutoWireViewModel="True"
    SizeToContent="Height"
    WindowStartupLocation="CenterScreen">

    <StackPanel Margin="20" Orientation="Vertical">
        <Form Source="{Binding Person}" />
        <Button Margin="10" Padding="20,5" HorizontalAlignment="Center" Command="{Binding ConfirmCommand}" Content="提交" />
    </StackPanel>
</Window>

ViewModel

public class EntityEditorView1ViewModel : BindableBase
{
    public event Action<Person> Completed;

    private Person _person;
    public Person Person
    {
        get => _person;
        set => this.SetProperty(ref _person, value);
    }

    public DelegateCommand<object> ConfirmCommand
    {
        get
        {
            return new DelegateCommand<object>(parameter =>
            {
                if (!string.IsNullOrEmpty(Person.Error))
                {
                    MessageBox.Show(Person.Error);
                }
                else
                {
                    Completed?.Invoke(Person);
                }
            });
        }
    }
}

 

Model

public class Person : ValidationObject
{
    private string _name;
    private DateTime _dateOfBirth = DateTime.Now;
    private int _age;
    private int _gender = 1;
    private string _phoneNumber;
    private string _email;
    private string _address;
    private string _idCardNumber;
    private EducationInfo _education;
    private MaritalStatus _maritalStatus;

    /// <summary>
    /// 姓名
    /// </summary>
    [DisplayOrder(1)]
    [DisplayHint("请填写姓名")]
    [DisplayName("姓名")]
    [StringLength(15, MinimumLength = 2, ErrorMessage = "姓名的有效长度为2-15个字符.")]
    [Required(ErrorMessage = "姓名为必填项.")]
    [Column(Name = "姓名", Order = 1)]
    public string Name
    {
        get => _name;
        set => this.SetProperty(ref _name, value);
    }

    /// <summary>
    /// 出生日期
    /// </summary>
    [DisplayOrder(2)]
    [DisplayHint("请选择出生日期")]
    [DisplayName("出生日期")]
    [Required(ErrorMessage = "出生日期为必填项.")]
    [Column(Name = "出生日期", StringFormat = "{0:yyyy年MM月dd日}", Order = 2)]
    public DateTime DateOfBirth
    {
        get => _dateOfBirth;
        set => this.SetProperty(ref _dateOfBirth, value);
    }

    /// <summary>
    /// 年龄
    /// </summary>
    [DisplayOrder(3)]
    [DisplayHint("请填写年龄")]
    [DisplayName("年龄")]
    [Range(1, 120, ErrorMessage = "年龄的有效值为1-120.")]
    [Required(ErrorMessage = "年龄为必填项.")]
    [Column(Name = "年龄", Order = 3)]
    public int Age
    {
        get => _age;
        set => this.SetProperty(ref _age, value);
    }

    /// <summary>
    /// // 性别
    /// </summary>
    [DisplayOrder(4)]
    [DisplayHint("请选择性别")]
    [DisplayName("性别")]
    [OverwriteType(HandleMethod.RadioButton, "1=男", "2=女", "3=其他")]
    [Column(Name = "性别", Order = 4)]
    public int Gender
    {
        get => _gender;
        set => this.SetProperty(ref _gender, value);
    }

    /// <summary>
    /// 手机号码
    /// </summary>
    [DisplayOrder(5)]
    [DisplayHint("请填写电话号码")]
    [DisplayName("电话号码")]
    [Phone(ErrorMessage = "电话号码格式不正确.")]
    [Column(Name = "电话号码", Order = 5)]
    public string PhoneNumber
    {
        get => _phoneNumber;
        set => this.SetProperty(ref _phoneNumber, value);
    }

    /// <summary>
    /// 电子邮箱
    /// </summary>
    [DisplayOrder(6)]
    [DisplayHint("请填写电子邮箱")]
    [DisplayName("电子邮箱")]
    [EmailAddress(ErrorMessage = "电子邮箱格式不正确.")]
    [Column(Name = "电子邮箱", Order = 6)]
    public string Email
    {
        get => _email;
        set => this.SetProperty(ref _email, value);
    }

    /// <summary>
    /// 地址信息
    /// </summary>
    [DisplayOrder(7)]
    [DisplayHint("请填写地址")]
    [DisplayName("地址")]
    [StringLength(50, ErrorMessage = "地址的最大长度为50个字符.")]
    [Column(Name = "地址", Order = 7)]
    public string Address
    {
        get => _address;
        set => this.SetProperty(ref _address, value);
    }

    /// <summary>
    /// 身份证号码
    /// </summary>
    [DisplayOrder(8)]
    [DisplayName("身份证号码")]
    [DisplayHint("请填写身份证号码")]
    [RegularExpression(RegexHelper.IdCardNumber, ErrorMessage = "身份证号码格式不正确.")]
    [Column(Name = "身份证号码", Order = 8)]
    public string IdCardNumber
    {
        get => _idCardNumber;
        set => this.SetProperty(ref _idCardNumber, value);
    }

    /// <summary>
    /// 教育信息
    /// </summary>
    [DisplayOrder(9)]
    [DisplayHint("请填写教育信息")]
    [DisplayName("教育信息")]
    [Browsable(false)]
    public EducationInfo Education
    {
        get => _education;
        set => this.SetProperty(ref _education, value);
    }

    /// <summary>
    /// 婚姻状况
    /// </summary>
    [DisplayOrder(10)]
    [DisplayHint("请选择婚姻状况")]
    [DisplayName("婚姻状况")]
    [Column(Name = "婚姻状况", Order = 10)]
    public MaritalStatus MaritalStatus
    {
        get => _maritalStatus;
        set => this.SetProperty(ref _maritalStatus, value);
    }
}

public class EducationInfo
{
    public string Degree { get; set; } // 学位
    public string Major { get; set; } // 专业
    public string School { get; set; } // 学校
    public DateTime GraduationDate { get; set; } // 毕业日期
}

public enum MaritalStatus
{
    /// <summary>
    /// 单身
    /// </summary>
    [Description("单身")]
    Single,
    /// <summary>
    /// 已婚
    /// </summary>
    [Description("已婚")]
    Married,
    /// <summary>
    /// 离异
    /// </summary>
    [Description("离异")]
    Divorced,
    /// <summary>
    /// 丧偶
    /// </summary>
    [Description("丧偶")]

运行效果

三、答疑解惑

3.1 如何实现每个属性的自定义布局?

答:控件提供了默认外观,如果无法满足要求,也可以编辑控件模板,在相应的数据类型模板中修改布局代码,也可以只编辑某种类型的控件模板,下面展示修改String类型模板,其它类型基本相似。

<Form Source="{Binding Person}">
    <Form.StringTemplate>
        <DataTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition SharedSizeGroup="RowHeight" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" SharedSizeGroup="Title" />
                    <ColumnDefinition Width="10" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <StackPanel
                            HorizontalAlignment="Right"
                            VerticalAlignment="Center"
                            Orientation="Horizontal">
                    <TextBlock VerticalAlignment="Center" Text="{Binding Name}" />
                    <TextBlock
                                VerticalAlignment="Center"
                                Foreground="Red"
                                Text="*"
                                Visibility="{Binding IsRequired, Converter={StaticResource BooleanToVisibilityConverter}}" />
                </StackPanel>
                <TextBox
                            Grid.Column="2"
                            MinWidth="150"
                            VerticalAlignment="Center"
                            extensions:BindingExtensions.BindingProperty="{x:Static TextBox.TextProperty}"
                            extensions:BindingExtensions.BindingSource="{Binding}" />
            </Grid>
        </DataTemplate>
    </Form.StringTemplate>
</Form>

3.2 如果控件模板中提供的基础数据类型没有我需要的属性类型,如何扩展新的属性类型?

答:控件提供了一个名为“CustomTypeTemplates”的属性来处理这个问题,以下为示例代码。

<Form Source="{Binding Person}">
    <Form.CustomTypeTemplates>
        <CustomTypeDataTemplateCollection>
            <CustomTypeDataTemplate CustomType="{x:Type model:EducationInfo}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition SharedSizeGroup="RowHeight" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" SharedSizeGroup="Title" />
                        <ColumnDefinition Width="10" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <StackPanel
                                HorizontalAlignment="Right"
                                VerticalAlignment="Center"
                                Orientation="Horizontal">
                        <TextBlock VerticalAlignment="Center" Text="{Binding Name}" />
                        <TextBlock
                                    VerticalAlignment="Center"
                                    Foreground="Red"
                                    Text="*"
                                    Visibility="{Binding IsRequired, Converter={StaticResource BooleanToVisibilityConverter}}" />
                    </StackPanel>
                    <TextBox
                                Grid.Column="2"
                                MinWidth="150"
                                VerticalAlignment="Center"
                                extensions:BindingExtensions.BindingProperty="{x:Static TextBox.TextProperty}"
                                extensions:BindingExtensions.BindingSource="{Binding}" />
                </Grid>
            </CustomTypeDataTemplate>
        </CustomTypeDataTemplateCollection>
    </Form.CustomTypeTemplates>
</Form>

3.3 如果我想自定义某个属性名的模板,如何实现?

答:控件提供了一个名为“CustomNameTemplates”的属性来处理这个问题,以下为示例代码。

<Form Source="{Binding Person}">
    <Form.CustomNameTemplates>
        <CustomNameDataTemplateCollection>
            <CustomNameDataTemplate CustomName="Name">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition SharedSizeGroup="RowHeight" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" SharedSizeGroup="Title" />
                        <ColumnDefinition Width="10" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <StackPanel
                                HorizontalAlignment="Right"
                                VerticalAlignment="Center"
                                Orientation="Horizontal">
                        <TextBlock VerticalAlignment="Center" Text="{Binding Name}" />
                        <TextBlock
                                    VerticalAlignment="Center"
                                    Foreground="Red"
                                    Text="*"
                                    Visibility="{Binding IsRequired, Converter={StaticResource BooleanToVisibilityConverter}}" />
                    </StackPanel>
                    <TextBox
                                Grid.Column="2"
                                MinWidth="150"
                                VerticalAlignment="Center"
                                extensions:BindingExtensions.BindingProperty="{x:Static TextBox.TextProperty}"
                                extensions:BindingExtensions.BindingSource="{Binding}" />
                </Grid>
            </CustomNameDataTemplate>
        </CustomNameDataTemplateCollection>
    </Form.CustomNameTemplates>
</Form>

 

3.4 如果我想隐藏某些属性应该怎么做?

答:对属性设置“Browsable”特性,以下为示例代码。

[Browsable(false)]
public EducationInfo Education
{
    get => _education;
    set => this.SetProperty(ref _education, value);
}

3.5 如何绑定枚举类型?

答:枚举在Form控件中默认显示为下拉框(ComboBox),只需要在枚举中设置"Description“特性就可以正常显示中文选项,如果不设置该属性则直接显示枚举名称。

public enum MaritalStatus
{
    /// <summary>
    /// 单身
    /// </summary>
    [Description("单身")]
    Single,
    /// <summary>
    /// 已婚
    /// </summary>
    [Description("已婚")]
    Married,
    /// <summary>
    /// 离异
    /// </summary>
    [Description("离异")]
    Divorced,
    /// <summary>
    /// 丧偶
    /// </summary>
    [Description("丧偶")]
    Widowed
}

3.6 如何绑定复杂属性,诸如单选框(RadioButton)、多选框(CheckBox)、下拉框(ComboBox)等?

答:控件读取一个名为”OverwriteType“的特性,特性中有一个名为“HandleMethod”的属性,该属性指明了覆盖当前类型的方式,并需要指定映射参数。

[OverwriteType(HandleMethod.RadioButton, "1=男", "2=女", "3=其他")]
public int Gender
{
    get => _gender;
    set => this.SetProperty(ref _gender, value);
}
public enum HandleMethod
{
    ComboBox,
    CheckBox,
    RadioButton
}

    以上为数据添加及修改部分的优化实现,下一节讲解如何在查询列表中优化。

标签:控件,set,string,get,摸鱼,WPF,ref,public
From: https://www.cnblogs.com/qushi2020/p/18103287

相关文章

  • 抢先看!界面控件DevExpress WPF 2024产品路线图预览(一)
    DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。本文将介绍2024年DevExpressWPF第一个主要更新(v2......
  • 浅谈WPF之属性系统
    在WPF开发中,经常听到各种属性,如:依赖属性,附加属性,CLR属性,那这些不同类型的属性,具体又有什么作用呢?今天以一些简单的小例子,简述一下WPF开发中,各种属性的相关概念和应用,仅供学习分享使用,如有不足之处,还请指正。 CLR属性 CLR属性(CommonLanguageRuntime),又称为.Net标准属性,是......
  • WPF绑定之道:为何选择属性而非字段,提升灵活性与可控性
     概述:WPF支持绑定到对象的属性而不是字段,主要因为属性提供了更多控制和扩展性。属性包含get和set方法,支持数据验证和通知属性更改,而字段通常被认为是内部实现。使用属性使WPF能够更灵活、可控地与数据交互,提高代码的可读性和可维护性。WPF(WindowsPresentationFoundation)支......
  • wpf draw rectangle with mouse
    usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows;usingSystem.Windows.Controls;usingSystem.Windows.Data;usingSystem.Windows.Documents;usingSystem.Windows.Input;......
  • 可能是迄今为止最好用的WPF加载动画功能(没有之一)
    前言当我们在开发应用程序时,用户体验往往是至关重要的一环。在应用程序加载大量数据或执行复杂操作时,为用户提供一个良好的加载体验变得至关重要。加载动画是其中一个有效的方式,它不仅能够告知用户应用程序正在进行工作,还能够缓解用户在等待过程中的焦虑感。一.需求分析 ......
  • [C#] [WPF] MVVMToolkit入门案例心得
    跟着做的第一个MVVM项目,学到一点基础的东西,记下来;有些用词不准确假设我们要做一个页面,通过按钮来控制上方文本框的文字,通过勾选框来控制按钮的激活状态⬇️一般流程需要3个属性,2个私有属性,1个RelayCommand属性代表按钮点击后事件,并配有相应的getter/setter文本......
  • 记一次WPF的DataGrid绑定数据
    之前一直在用winform,但是感觉界面不好看,然后就自己在网上学习WPF。一开始看到DataGrid的时候,还以为它是DataGridView,然后用winform的方法绑定数据发现不行,在不断的查找之后,终于学会了怎么简单的绑定数据。工具:VStudio2022框架:.netframework4.8新建一个WPF窗体,再把DataGrid拖......
  • 【wpf】ListBoxItemIndexConverter转换器listbox序号自更新
    publicclassListBoxItemIndexConverter:IMultiValueConverter{publicobjectConvert(object[]values,TypetargetType,objectparameter,CultureInfoculture){stringindexPrefix=null;if(parameter!=null&&parameter......
  • 遇到时间控件怎么办?不要慌,教你轻松拿下
    此文章来源于项目官方公众号:“AirtestProject”版权声明:允许转载,但转载必须保留原链接;请勿用作商业或者非法用途1.前言很多同学在测试场景中总会遇到各种各样的小控件需要进行测试的,包括在Android端,web端等等都有,那么今天我们来看看当我们在遇到时间控件的时候,我们应该如何进......
  • 4.列表控件的总结(ListView)
    ListView常用的属性android:divider分割线的颜色数据适配器三个BaseAdapterimplementsListAdapter,SpinnerAdapterpublicclassSimpleAdapterextendsBaseAdapterpublicclassArrayAdapter<T>extendsBaseAdapterSimpleAdapter主要是显示图片加文字的东西例如:......