一个可以让你有更多时间摸鱼的WPF控件(二)
前言
上文介绍了如何通过一个Form自定义控件来简化数据的录入,并自动实现数据校验,自动布局排列等功能。本文继续介绍如何优化表格控件的使用,缩减代码量,实现工作效率的提升。
一、功能实现
上文中分析了DataGrid跟ListView两种表格控件的优劣,在这里我们选择ListView来实现我们的表格功能,如有疑问请看上一篇文章,这里不再赘述。
下面我们定义一个ListViewExtensions静态类来实现ListView的附加属性,并在附加属性更改后实现ListView的优化功能。
public static class ListViewExtensions
{
#region AutoGenerateColumns
public static readonly DependencyProperty AutoGenerateColumnsProperty =
DependencyProperty.RegisterAttached("AutoGenerateColumns", typeof(bool), typeof(ListViewExtensions), new PropertyMetadata(false, AutoGenerateColumnsChanged)); public static bool GetAutoGenerateColumns(DependencyObject obj)
{
return (bool)obj.GetValue(AutoGenerateColumnsProperty);
} public static void SetAutoGenerateColumns(DependencyObject obj, bool value)
{
obj.SetValue(AutoGenerateColumnsProperty, value);
} private static void AutoGenerateColumnsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
ListView listView = obj as ListView;
bool autoGenerateColumns = (bool)e.NewValue; if (listView != null)
{
if (autoGenerateColumns)
{
listView.Loaded += ListViewLoadedHandler; }
else
{
listView.Loaded -= ListViewLoadedHandler;
}
}
} private static void ListViewLoadedHandler(object sender, RoutedEventArgs e)
{
var listView = (ListView)sender;
if (listView.ItemsSource == null) return; var itemType = listView.ItemsSource.GetType().GetGenericArguments().FirstOrDefault();
if (itemType != null)
{
// 获取数据源的属性信息
var properties = itemType.GetProperties();
GridView gridView = null;
if (listView.View is GridView gv)
{
gridView = gv;
}
else
{
listView.View = gridView = new GridView();
} // 根据属性信息创建列并添加到 GridView 中
var optionMappingConverter = new OptionMappingConverter();
var enumDescriptionConverter = new EnumDescriptionConverter();
foreach (PropertyInfo property in properties)
{
//排除无用属性
var name = property.Name;
if (PropertyHelper.IgnoredProperties.Contains(property.Name)) continue; //读取列参数
var columnAttribute = property.GetCustomAttributes().OfType<ColumnAttribute>().FirstOrDefault();
if (columnAttribute == null || !columnAttribute.IsVisible) continue; //排除重复属性
var targetColumn = gridView.Columns.FirstOrDefault(item => GetColumnName(item) == property.Name);
if (targetColumn != null)
{
if (targetColumn.Header == null)
targetColumn.Header = columnAttribute.Name;
SetColumnIndex(targetColumn, columnAttribute.Order);
continue;
} var column = new GridViewColumn() { Header = !string.IsNullOrEmpty(columnAttribute.Name) ? columnAttribute.Name : name };
SetColumnName(column, property.Name);
//分组
if (columnAttribute is GroupColumnAttribute group) ListViewExtensions.SetGroup(column, group.Group);
//宽度
if (columnAttribute.Width > 0) column.Width = columnAttribute.Width;
//转换器
if (columnAttribute.Converter != null)
{
column.DisplayMemberBinding = new Binding(property.Name)
{
Converter = Activator.CreateInstance(columnAttribute.Converter) as IValueConverter,
ConverterParameter = columnAttribute.ConverterParameter,
StringFormat = columnAttribute.StringFormat
};
}
else
{
//指定转换器
var optionMappingAttribute = property.GetCustomAttributes().OfType<OptionMappingAttribute>().FirstOrDefault();
if (optionMappingAttribute != null)
{
column.DisplayMemberBinding = new Binding(property.Name)
{
Converter = optionMappingConverter,
ConverterParameter = optionMappingAttribute.Mapping
};
}
else if (property.PropertyType.IsEnum)
{
column.DisplayMemberBinding = new Binding(property.Name)
{
Converter = enumDescriptionConverter,
StringFormat = columnAttribute.StringFormat
};
}
else
{
column.DisplayMemberBinding = new Binding(property.Name) { StringFormat = columnAttribute.StringFormat };
}
} SetColumnIndex(column, columnAttribute.Order);
gridView.Columns.Add(column);
} var cols = gridView.Columns.OrderBy(item => GetColumnIndex(item)).ToArray();
gridView.Columns.Clear();
foreach (var col in cols)
{
gridView.Columns.Add(col);
}
}
}
#endregion #region ColumnIndex
public static int GetColumnIndex(DependencyObject obj)
{
return (int)obj.GetValue(ColumnIndexProperty);
} public static void SetColumnIndex(DependencyObject obj, int value)
{
obj.SetValue(ColumnIndexProperty, value);
} public static readonly DependencyProperty ColumnIndexProperty =
DependencyProperty.RegisterAttached("ColumnIndex", typeof(int), typeof(ListViewExtensions), new PropertyMetadata(0)); #endregion #region ColumnName
public static string GetColumnName(DependencyObject obj)
{
return (string)obj.GetValue(ColumnNameProperty);
} public static void SetColumnName(DependencyObject obj, string value)
{
obj.SetValue(ColumnNameProperty, value);
} public static readonly DependencyProperty ColumnNameProperty =
DependencyProperty.RegisterAttached("ColumnName", typeof(string), typeof(ListViewExtensions));
#endregion #region Group
public static string GetGroup(DependencyObject obj)
{
return (string)obj.GetValue(GroupProperty);
} public static void SetGroup(DependencyObject obj, string value)
{
obj.SetValue(GroupProperty, value);
} public static readonly DependencyProperty GroupProperty =
DependencyProperty.RegisterAttached("Group", typeof(string), typeof(ListViewExtensions)); #endregion
}
二、示例代码
View
<ListView extensions:ListViewExtensions.AutoGenerateColumns="True" ItemsSource="{Binding Collection}"/>
Model
public class Person : ValidationObjectBase
{
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>
[Column(Name = "姓名", Order = 1)]
public string Name
{
get => _name;
set => this.SetProperty(ref _name, value);
} /// <summary>
/// 出生日期
/// </summary>
[Column(Name = "出生日期", StringFormat = "{0:yyyy年MM月dd日}", Order = 2)]
public DateTime DateOfBirth
{
get => _dateOfBirth;
set => this.SetProperty(ref _dateOfBirth, value);
} /// <summary>
/// 年龄
/// </summary>
[Column(Name = "年龄", Order = 3)]
public int Age
{
get => _age;
set => this.SetProperty(ref _age, value);
} /// <summary>
/// // 性别
/// </summary>
[Column(Name = "性别", Order = 4)]
public int Gender
{
get => _gender;
set => this.SetProperty(ref _gender, value);
} /// <summary>
/// 手机号码
/// </summary>
[Column(Name = "电话号码", Order = 5)]
public string PhoneNumber
{
get => _phoneNumber;
set => this.SetProperty(ref _phoneNumber, value);
} /// <summary>
/// 电子邮箱
/// </summary>
[Column(Name = "电子邮箱", Order = 6)]
public string Email
{
get => _email;
set => this.SetProperty(ref _email, value);
} /// <summary>
/// 地址信息
/// </summary>
[Column(Name = "地址", Order = 7)]
public string Address
{
get => _address;
set => this.SetProperty(ref _address, value);
} /// <summary>
/// 身份证号码
/// </summary>
[Column(Name = "身份证号码", Order = 8)]
public string IdCardNumber
{
get => _idCardNumber;
set => this.SetProperty(ref _idCardNumber, value);
} /// <summary>
/// 教育信息
/// </summary>
//[Column(Name = "教育信息", IsVisible = false, Order = 9)]
public EducationInfo Education
{
get => _education;
set => this.SetProperty(ref _education, value);
} /// <summary>
/// 婚姻状况
/// </summary>
[Column(Name = "婚姻状况", Order = 10)]
public MaritalStatus MaritalStatus
{
get => _maritalStatus;
set => this.SetProperty(ref _maritalStatus, value);
}
}
ViewModel
public class FormDemoViewModel : BindableBase
{
public ObservableCollection<Person> Collection
{
get { return _collection; }
set { this.SetProperty(ref _collection, value); }
}
}

三、答疑解惑
2.1 如何添加自定义列?
答:可以按常规方式添加自定义列,用ListViewExtensions.ColumnIndex来指定位的位置,以下为示例代码。
<ListView extensions:ListViewExtensions.AutoGenerateColumns="True" ItemsSource="{Binding Collection}">
<GridView>
<GridViewColumn extensions:ListViewExtensions.ColumnIndex="0">
<GridViewColumn.Header>
<CheckBox Content="全选" />
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView>
2.2 如何自动生成的列不满足需求,可不可以自己定义列?
答:可以,使用ListViewExtensions.ColumnName属性可以覆盖某一个列的自动生成的操作。
<ListView extensions:ListViewExtensions.AutoGenerateColumns="True" ItemsSource="{Binding Collection}">
<GridView>
<GridViewColumn
extensions:ListViewExtensions.ColumnIndex="1"
extensions:ListViewExtensions.ColumnName="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView>
2.3 如何给列设置顺序?
答:在属性上给ColumnAttribute特性设置参数Order。
[Column(Name = "出生日期", Order = 2)]
public DateTime DateOfBirth
{
get => _dateOfBirth;
set => this.SetProperty(ref _dateOfBirth, value);
}
2.4 如何设置列的宽度?
答:在属性上给ColumnAttribute特性设置参数Width。
[Column(Name = "出生日期", Width =100)]
public DateTime DateOfBirth
{
get => _dateOfBirth;
set => this.SetProperty(ref _dateOfBirth, value);
}
2.5 如何将列设为不可见?
答:在属性上给ColumnAttribute特性设置参数IsVisible。
[Column(Name = "出生日期", IsVisible = false)]
public DateTime DateOfBirth
{
get => _dateOfBirth;
set => this.SetProperty(ref _dateOfBirth, value);
}
2.6 如何给绑定设置格式化字符串?
答:在属性上给ColumnAttribute特性设置参数StringFormat。
[Column(Name = "出生日期", StringFormat = "{0:yyyy年MM月dd日}")]
public DateTime DateOfBirth
{
get => _dateOfBirth;
set => this.SetProperty(ref _dateOfBirth, value);
}
2.7 如何给绑定设置转换器及参数?
答:在属性上给ColumnAttribute特性设置Converter和ConverterParameter参数。
[Column(Name = "出生日期", Converter = typeof(TestConverter), ConverterParameter=1)]
public DateTime DateOfBirth
{
get => _dateOfBirth;
set => this.SetProperty(ref _dateOfBirth, value);
}
一个可以让你有更多时间摸鱼的WPF控件(二)的更多相关文章
- MFC_VC++_时间获取与保存列表控件内容到文件操作方法
MFC_VC++_时间获取与保存列表控件内容到excel文件操作方法 void CDataView::OnBnClickedBtnExporttoexcel() { CTime time = CTim ...
- Android一个炫酷的树状图组织架构图开源控件实现过程
Android一个炫酷的树状图组织架构图开源控件 文章目录 [1 简介] [2 效果展示] [3 使用步骤] [4 实现基本布局流程] [5 实现自由放缩及拖动] [6 实现添加删除及节点动画] [7 ...
- 使用 WebView2 封装一个生成 PDF 的 WPF 控件
使用 WebView2 封装一个生成 PDF 的 WPF 控件 最近在迁移项目到 .net6,发现项目中用的 PDF 库不支持 .net6,于是想着换一个库.结果找了一大圈,发现不是版本不支持,就是收 ...
- [MFC] 梳理一个简单的图片处理桌面软件中用到的MFC控件技巧
前言 前些天应好友之拖,帮忙设计一个简单的图像处理的小软件.朋友把核心算法封装好了,但是是用openCV类似于console的编程环境,要我在此基础上改成MFC桌面程序.下图是做成之后的效果: 我是 ...
- 一个WPF控件 诡异的MouseEvent 。
背景: private System.Windows.Controls.Border _borderTouch; private bool _mouseDown = false; private S ...
- Birt时间参数添加My97日历控件
首先,思路: 引用My97.js然后为时间参数的textbox添加onclick事件 1.将My97添加到项目中的webcontent目录下(如图:) 2.添加My97引用 在项目路径下找到该文件\w ...
- 一个jsqlparse+git做的小工具帮我节省时间摸鱼
背景 前些时间做了个小工具解决了团队内数据库脚本检验&多测试环境自动执行的问题,感觉挺有意思,在这跟大家分享一下. 工具诞生之前的流程是这样: 1.开发人员先在开发环境编写脚本&执行: ...
- WPF 控件事件的一个小坑…
最近想判断一下 Slider 是由鼠标点击而改变值,还是由程序内部代码改变的值,发现鼠标的各种事件比如 MouseDown.MouseUp.MouseLeftButtonDown 什么的,都没有任何反 ...
- [日常摸鱼]bzoj1218[HNOI2003]激光炸弹-二维前缀
题意:二维网格一些格子有权值,求用边长为$r$的正方形能覆盖到格子权值和的最大值,格子大小$ \leq 5000$ 非常裸的二维前缀,然而 题目下标从0开始! QAQ 要是比赛就要爆零啦- #incl ...
- 分享一下我封装iOS自定义控件的体会,附上三个好用的控件Demo <时间选择器&多行输入框&日期选择器>
前段时间有小伙伴问到我:"这样的控件该怎么做呢?",我感觉是个比较简单的控件,可能对于入行不久的同志思路没有很清晰吧.趁着最近工作不忙,就来这里分享一下我封装自定义控件的几点体会吧 ...
随机推荐
- Python之初级RPG小游戏
在国外网站上找到一个练习Python的小游戏感觉不错,自己实现了一下. 通过该练习你能学到: 元组 字典 简单定义函数和封装 条件控制语句 游戏说明 以下是3个房间和1个花园: Hall 客厅 有一把 ...
- RCE代码执行漏和命令执行漏洞
前置知识: 漏洞检测: 在了解漏洞概念前,应该先知道一下这个漏洞如何检测的,我们应该或多或少听过白盒测试(白盒),黑盒测试(黑盒). 白盒测试: 白盒测试是对源代码和内部结构的测试,测试人员是可以知道 ...
- Java 中拼接 String 的 N 种方式
文章持续更新,可以关注公众号程序猿阿朗或访问未读代码博客. 本文 Github.com/niumoo/JavaNotes 已经收录,欢迎Star. 1. 前言 Java 提供了拼接 String 字符 ...
- 记一次 QT VS Tools 无法配置 version 的问题
问题概述: 在 QT VS Tools 拓展工具中添加多个 qt 版本的静态库时,发现输入完 Name 和 Path 之后点击 OK,新输入的 version 路径并没有保存成功 测试的 QT VS ...
- windows下redis安装与使用
下载 redis下载地址 步骤,一直下一步按就完事了,记得勾选添加到环境配置 其中建议自定义路径吧,文件夹名字Redis,一定要大写,小写不来事. 安装完后 启动命令 找到你安装redis的文件夹,在 ...
- vim创建sh文件自动生成头信息
>>> vim /etc/vimrc 或 vim ~/.vimrc " 这几个加不加都行 set tabstop=4 set softtabstop=4 set shift ...
- 阿尔萨斯(Arthas)入门
目录 简介 Arthas(阿尔萨斯) 能为你做什么 安装 快速安装 全量安装 卸载 使用 启动arthas 查看dashboard 通过thread命令来获取到arthas-demo进程的Main C ...
- 【Azure Developer】Springboot 集成 中国区的Key Vault 报错 AADSTS90002: Tenant 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' not found
问题描述 Springboot 集成azure keyvault 报错,代码参考的官方文档:https://docs.microsoft.com/en-us/azure/developer/java/ ...
- 【Azure Redis 缓存】在Azure Redis中,如何限制只允许Azure App Service访问?
问题描述 在Azure Redis服务中,如何实现只允许Azure App Service访问呢? 问题解答 Azure Redis 开启 防火墙的功能,并在防火墙中添加上App Service的出口 ...
- 从实测出发,掌握 NebulaGraph Exchange 性能最大化的秘密
自从开发完 NebulaGraph Exchange,混迹在各个 NebulaGraph 微信群的我经常会看到一类提问是:NebulaGraph Exchange 的性能如何?哪些参数调整下可以有更好 ...