Windows 表单中的 DataAnnotations 支持
DataAnnotations Support in Windows Forms
我想在我的 classes 和 Windows Forms 应用程序 (WinForms) 中的实体上使用数据注释。我使用 windows DataGridViews 和 Infragistics UltraGrids。我以前成功地使用 [DisplayName("Name to Display")]
属性在 DataGridView/UltraGrid.
上设置列 header 文本
这非常有用,因为我可能有多个网格显示此 class,而不是配置每个网格以显示适当的 header 文本,我可以简单地设置一个数据注释。
我也想使用以下数据注释:
- 显示
- [显示(AutoGenerateField=false)]
- 不显示此列
- [显示(订单=N)]
- 将此列显示为网格中的第 n 列
- 显示列
- [DisplayColumn("ColumnName")]
- 如果此 object 是另一个 object 的 属性,则显示此列值而不是 object 类型
- 显示格式
- [DisplayFormat(DataFormatString="{0:formatstring}")]
- 使用指定的格式字符串格式化数据
- 数据类型
- [数据类型(DataType.Currency)]
- 以默认本地货币格式将数据显示为货币
例子
给定以下带注释的数据 classes:
public class Item
{
//Specifies that the column should not be displayed
[Display(AutoGenerateField = false)]
public int ItemID { get; set; }
//Specifies that the column should be the 1st column in the datagridview
[Display(Order = 1)]
public int Name { get; set; }
//Specifies that the column should be the 3rd column in the datagridview
//Specifies that the column header text should display "Cost" instead of "Price"
[Display(Order = 3, Name="Cost")]
//Specifies that the column should be rendered using the default local currency format string
[DataType(DataType.Currency)]
public int Price { get; set; }
//Specifies that the column should be the 4th column in the datagridview
[Display(Order = 4)]
//specifies that the column should be rendered using the datetime format string "M/d/yy h:mm tt"
[DisplayFormat(DataFormatString = "{0:M/d/yy h:mm tt")]
public DateTime ExpirationDate { get; set; }
//Specifies that the column should be the 2nd column in the datagridview
[Display(Order = 2)]
public ItemCategory Category { get; set; }
}
//Specifies that the Name column should be displayed, if referenced in a containing object
[DisplayColumn("Name")]
public class ItemCategory
{
public int CategoryID { get; set; }
public string Name { get; set; }
}
我希望 DataGridView 像这样呈现:
+-------+---------------+--------+-----------------+
| Name | Category | Cost | ExpirationDate |
+-------+---------------+--------+-----------------+
| Item1 | Category1Name | .45 | 7/23/17 5:22 PM |
+-------+---------------+--------+-----------------+
| Item2 | Category1Name | .05 | 8/24/17 6:22 PM |
+-------+---------------+--------+-----------------+
| Item3 | Category2Name | .50 | 9/25/17 7:22 PM |
+-------+---------------+--------+-----------------+
然而,在 .Net 4.5.2 WinForms 中使用 DataGridViews 时,数据网格实际显示如下:
+----+-------+-------+----------------+--------------------+
| ID | Name | Price | ExpirationDate | Category |
+----+-------+-------+----------------+--------------------+
| 1 | Item1 | 30.45 | 7/23/17 | Namespace.Category |
+----+-------+-------+----------------+--------------------+
| 2 | Item2 | 45.05 | 8/24/17 | Namespace.Category |
+----+-------+-------+----------------+--------------------+
| 3 | Item3 | 35.50 | 9/25/17 | Namespace.Category |
+----+-------+-------+----------------+--------------------+
文档
文档声明它在 ASP.NET 和 ASP.NET MVC 中受支持。
System.ComponentModel.DataAnnotations Namespace
The System.ComponentModel.DataAnnotations namespace provides attribute classes that are used to define metadata for ASP.NET MVC and ASP.NET data controls.
问题
看来这些 classes 还没有在 windows 表单环境中 adopted/supported。这是真的吗?
有没有一种简单的方法来实现对 WinForms 中数据注释的支持?
是否有一种简单的方法来注释 classes/entities 可用于格式化 DataGridView and/or UltraGrid 的显示?
Infragistics UltraGrid 和 Windows DataGridView 都不支持这种方式的数据注释。从 15.1 开始,DevExpress 在其数据布局控件中有一些有限的 WinForms 数据注释支持 https://community.devexpress.com/blogs/thinking/archive/2015/06/08/winforms-data-layout-control-data-annotation-attributes.aspx。
我发现这个 link 有人在询问有关如何通过查看元数据来抑制 Infragistics Grid 中的列的类似问题:http://www.infragistics.com/community/forums/t/91864.aspx
Windows 表单中 DataGridView 的 DataAnnotations 属性
There is no built-in support for Data Annotation in Windows Forms, but
knowing how the attributes work and how windows forms work, we can
use them in windows forms.
在这个 post 中,我将展示 DataGridView
的扩展方法,它将 IList<T>
绑定到 DataGridView
和 auto-generate 列,基于数据注释属性,因此您可以通过调用 dataGridView1.Bind(list);
:
获得以下 DataGridView
查看以下来自数据注释属性的项目:
- 列可见性: Id 列不可见
- 列 header 文本:它们是不同于 属性 名称的自定义文本
- 列的顺序:列的顺序是自定义的,不同于属性顺序
- 工具提示:我们已经显示了列的自定义工具提示。
- 数据格式: 我们使用了自定义日期格式。
您还可以使用属性获得更多东西。
模型是这样的:
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
[TypeDescriptionProvider(typeof(MetadataTypeTypeDescriptionProvider))]
public class Person
{
[Display(Name = "Id")]
[Browsable(false)]
public int? Id { get; set; }
[Display(Name = "First Name", Description = "First name.", Order = 1)]
public string FirstName { get; set; }
[Display(Name = "Last Name", Description = "Last name", Order = 2)]
public string LastName { get; set; }
[Display(Name = "Birth Date", Description = "Date of birth.", Order = 4)]
[DisplayFormat(DataFormatString = "yyyy-MM-dd")]
public DateTime BirthDate { get; set; }
[Display(Name = "Homepage", Description = "Url of homepage.", Order = 5)]
public string Url { get; set; }
[Display(Name = "Member", Description = "Is member?", Order = 3)]
public bool IsMember { get; set; }
}
绑定扩展方法
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Windows.Forms;
public static class DataGridViewExtensions
{
public static void Bind<T>(this DataGridView grid, IList<T> data,
bool autoGenerateColumns = true)
{
if (autoGenerateColumns)
{
var properties = TypeDescriptor.GetProperties(typeof(T));
var metedata = properties.Cast<PropertyDescriptor>().Select(p => new
{
Name = p.Name,
HeaderText = p.Attributes.OfType<DisplayAttribute>()
.FirstOrDefault()?.Name ?? p.DisplayName,
ToolTipText = p.Attributes.OfType<DisplayAttribute>()
.FirstOrDefault()?.GetDescription() ?? p.Description,
Order = p.Attributes.OfType<DisplayAttribute>()
.FirstOrDefault()?.GetOrder() ?? int.MaxValue,
Visible = p.IsBrowsable,
ReadOnly = p.IsReadOnly,
Format = p.Attributes.OfType<DisplayFormatAttribute>()
.FirstOrDefault()?.DataFormatString,
Type = p.PropertyType
});
var columns = metedata.OrderBy(m => m.Order).Select(m =>
{
DataGridViewColumn c;
if (m.Type == typeof(bool)) {
c = new DataGridViewCheckBoxColumn(false); }
else if (m.Type == typeof(bool?)) {
c = new DataGridViewCheckBoxColumn(true); }
else { c = new DataGridViewTextBoxColumn(); }
c.DataPropertyName = m.Name;
c.Name = m.Name;
c.HeaderText = m.HeaderText;
c.ToolTipText = m.ToolTipText;
c.DefaultCellStyle.Format = m.Format;
c.ReadOnly = m.ReadOnly;
c.Visible = m.Visible;
return c;
});
grid.Columns.Clear();
grid.Columns.AddRange(columns.ToArray());
}
grid.DataSource = data;
}
}
Windows 表单的 DataAnnotations 验证属性
此外,为了支持数据注释验证,您可以使用 Validator
class 实现 IDataErrorInfo
接口,就像我在 DataAnnotations Validation attributes for Windows Forms 中所做的一样。
备注
为了增强答案,您可能需要创建一个关心元数据属性的类型描述符,然后使用该类型描述符装饰模型。您可以使用 AssociatedMetadataTypeTypeDescriptor
, MetadataPropertyDescriptorWrapper
, AssociatedMetadataTypeTypeDescriptionProvider
.
的代码开始
您还可以创建元数据 class 并在元数据上应用某些属性的效果,例如 Url
或 DataType
。查看这个 post 会给你一些想法:Combining multiple Attributes to a single Attribute - Merge Attributes.
我想在我的 classes 和 Windows Forms 应用程序 (WinForms) 中的实体上使用数据注释。我使用 windows DataGridViews 和 Infragistics UltraGrids。我以前成功地使用 [DisplayName("Name to Display")]
属性在 DataGridView/UltraGrid.
这非常有用,因为我可能有多个网格显示此 class,而不是配置每个网格以显示适当的 header 文本,我可以简单地设置一个数据注释。
我也想使用以下数据注释:
- 显示
- [显示(AutoGenerateField=false)]
- 不显示此列
- [显示(订单=N)]
- 将此列显示为网格中的第 n 列
- 显示列
- [DisplayColumn("ColumnName")]
- 如果此 object 是另一个 object 的 属性,则显示此列值而不是 object 类型
- 显示格式
- [DisplayFormat(DataFormatString="{0:formatstring}")]
- 使用指定的格式字符串格式化数据
- 数据类型
- [数据类型(DataType.Currency)]
- 以默认本地货币格式将数据显示为货币
例子
给定以下带注释的数据 classes:
public class Item
{
//Specifies that the column should not be displayed
[Display(AutoGenerateField = false)]
public int ItemID { get; set; }
//Specifies that the column should be the 1st column in the datagridview
[Display(Order = 1)]
public int Name { get; set; }
//Specifies that the column should be the 3rd column in the datagridview
//Specifies that the column header text should display "Cost" instead of "Price"
[Display(Order = 3, Name="Cost")]
//Specifies that the column should be rendered using the default local currency format string
[DataType(DataType.Currency)]
public int Price { get; set; }
//Specifies that the column should be the 4th column in the datagridview
[Display(Order = 4)]
//specifies that the column should be rendered using the datetime format string "M/d/yy h:mm tt"
[DisplayFormat(DataFormatString = "{0:M/d/yy h:mm tt")]
public DateTime ExpirationDate { get; set; }
//Specifies that the column should be the 2nd column in the datagridview
[Display(Order = 2)]
public ItemCategory Category { get; set; }
}
//Specifies that the Name column should be displayed, if referenced in a containing object
[DisplayColumn("Name")]
public class ItemCategory
{
public int CategoryID { get; set; }
public string Name { get; set; }
}
我希望 DataGridView 像这样呈现:
+-------+---------------+--------+-----------------+
| Name | Category | Cost | ExpirationDate |
+-------+---------------+--------+-----------------+
| Item1 | Category1Name | .45 | 7/23/17 5:22 PM |
+-------+---------------+--------+-----------------+
| Item2 | Category1Name | .05 | 8/24/17 6:22 PM |
+-------+---------------+--------+-----------------+
| Item3 | Category2Name | .50 | 9/25/17 7:22 PM |
+-------+---------------+--------+-----------------+
然而,在 .Net 4.5.2 WinForms 中使用 DataGridViews 时,数据网格实际显示如下:
+----+-------+-------+----------------+--------------------+
| ID | Name | Price | ExpirationDate | Category |
+----+-------+-------+----------------+--------------------+
| 1 | Item1 | 30.45 | 7/23/17 | Namespace.Category |
+----+-------+-------+----------------+--------------------+
| 2 | Item2 | 45.05 | 8/24/17 | Namespace.Category |
+----+-------+-------+----------------+--------------------+
| 3 | Item3 | 35.50 | 9/25/17 | Namespace.Category |
+----+-------+-------+----------------+--------------------+
文档
文档声明它在 ASP.NET 和 ASP.NET MVC 中受支持。
System.ComponentModel.DataAnnotations Namespace
The System.ComponentModel.DataAnnotations namespace provides attribute classes that are used to define metadata for ASP.NET MVC and ASP.NET data controls.
问题
看来这些 classes 还没有在 windows 表单环境中 adopted/supported。这是真的吗?
有没有一种简单的方法来实现对 WinForms 中数据注释的支持?
是否有一种简单的方法来注释 classes/entities 可用于格式化 DataGridView and/or UltraGrid 的显示?
Infragistics UltraGrid 和 Windows DataGridView 都不支持这种方式的数据注释。从 15.1 开始,DevExpress 在其数据布局控件中有一些有限的 WinForms 数据注释支持 https://community.devexpress.com/blogs/thinking/archive/2015/06/08/winforms-data-layout-control-data-annotation-attributes.aspx。
我发现这个 link 有人在询问有关如何通过查看元数据来抑制 Infragistics Grid 中的列的类似问题:http://www.infragistics.com/community/forums/t/91864.aspx
Windows 表单中 DataGridView 的 DataAnnotations 属性
There is no built-in support for Data Annotation in Windows Forms, but knowing how the attributes work and how windows forms work, we can use them in windows forms.
在这个 post 中,我将展示 DataGridView
的扩展方法,它将 IList<T>
绑定到 DataGridView
和 auto-generate 列,基于数据注释属性,因此您可以通过调用 dataGridView1.Bind(list);
:
DataGridView
查看以下来自数据注释属性的项目:
- 列可见性: Id 列不可见
- 列 header 文本:它们是不同于 属性 名称的自定义文本
- 列的顺序:列的顺序是自定义的,不同于属性顺序
- 工具提示:我们已经显示了列的自定义工具提示。
- 数据格式: 我们使用了自定义日期格式。
您还可以使用属性获得更多东西。
模型是这样的:
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
[TypeDescriptionProvider(typeof(MetadataTypeTypeDescriptionProvider))]
public class Person
{
[Display(Name = "Id")]
[Browsable(false)]
public int? Id { get; set; }
[Display(Name = "First Name", Description = "First name.", Order = 1)]
public string FirstName { get; set; }
[Display(Name = "Last Name", Description = "Last name", Order = 2)]
public string LastName { get; set; }
[Display(Name = "Birth Date", Description = "Date of birth.", Order = 4)]
[DisplayFormat(DataFormatString = "yyyy-MM-dd")]
public DateTime BirthDate { get; set; }
[Display(Name = "Homepage", Description = "Url of homepage.", Order = 5)]
public string Url { get; set; }
[Display(Name = "Member", Description = "Is member?", Order = 3)]
public bool IsMember { get; set; }
}
绑定
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Windows.Forms;
public static class DataGridViewExtensions
{
public static void Bind<T>(this DataGridView grid, IList<T> data,
bool autoGenerateColumns = true)
{
if (autoGenerateColumns)
{
var properties = TypeDescriptor.GetProperties(typeof(T));
var metedata = properties.Cast<PropertyDescriptor>().Select(p => new
{
Name = p.Name,
HeaderText = p.Attributes.OfType<DisplayAttribute>()
.FirstOrDefault()?.Name ?? p.DisplayName,
ToolTipText = p.Attributes.OfType<DisplayAttribute>()
.FirstOrDefault()?.GetDescription() ?? p.Description,
Order = p.Attributes.OfType<DisplayAttribute>()
.FirstOrDefault()?.GetOrder() ?? int.MaxValue,
Visible = p.IsBrowsable,
ReadOnly = p.IsReadOnly,
Format = p.Attributes.OfType<DisplayFormatAttribute>()
.FirstOrDefault()?.DataFormatString,
Type = p.PropertyType
});
var columns = metedata.OrderBy(m => m.Order).Select(m =>
{
DataGridViewColumn c;
if (m.Type == typeof(bool)) {
c = new DataGridViewCheckBoxColumn(false); }
else if (m.Type == typeof(bool?)) {
c = new DataGridViewCheckBoxColumn(true); }
else { c = new DataGridViewTextBoxColumn(); }
c.DataPropertyName = m.Name;
c.Name = m.Name;
c.HeaderText = m.HeaderText;
c.ToolTipText = m.ToolTipText;
c.DefaultCellStyle.Format = m.Format;
c.ReadOnly = m.ReadOnly;
c.Visible = m.Visible;
return c;
});
grid.Columns.Clear();
grid.Columns.AddRange(columns.ToArray());
}
grid.DataSource = data;
}
}
Windows 表单的 DataAnnotations 验证属性
此外,为了支持数据注释验证,您可以使用 Validator
class 实现 IDataErrorInfo
接口,就像我在 DataAnnotations Validation attributes for Windows Forms 中所做的一样。
备注
为了增强答案,您可能需要创建一个关心元数据属性的类型描述符,然后使用该类型描述符装饰模型。您可以使用 AssociatedMetadataTypeTypeDescriptor
, MetadataPropertyDescriptorWrapper
, AssociatedMetadataTypeTypeDescriptionProvider
.
您还可以创建元数据 class 并在元数据上应用某些属性的效果,例如 Url
或 DataType
。查看这个 post 会给你一些想法:Combining multiple Attributes to a single Attribute - Merge Attributes.