Windows 窗体中的数据注释支持

Bri*_*ino 9 .net c# winforms data-annotations

我想在 Windows 窗体应用程序 (WinForms) 中的类和实体上使用数据注释。我使用 windows DataGridViews 和 Infragistics UltraGrids。我之前已经成功使用该[DisplayName("Name to Display")]属性在 DataGridView/UltraGrid 上设置列标题文本。

这是非常有益的,因为我可能有几个网格显示这个类,而不是配置每个网格来显示适当的标题文本,我可以简单地设置一个数据注释。

我还想使用以下数据注释:

  • 展示
  • [显示(自动生成字段=假)]
    • 不显示此列
  • [显示(订单=N)]
    • 将此列显示为网格中的第 n 列
  • 显示列
  • [DisplayColumn("ColumnName")]
    • 如果此对象是另一个对象的属性,则显示此列值而不是对象类型
  • 显示格式
  • [DisplayFormat(DataFormatString="{0:formatstring}")]
    • 使用指定的格式字符串格式化数据
  • 数据类型
  • [数据类型(数据类型.货币)]
    • 以默认本地货币格式将数据显示为货币

例子

给定以下带注释的数据类:

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; }
}
Run Code Online (Sandbox Code Playgroud)

我希望 DataGridView 呈现如下:

+-------+---------------+--------+-----------------+
|  Name |    Category   |  Cost  |  ExpirationDate |
+-------+---------------+--------+-----------------+
| Item1 | Category1Name | $30.45 | 7/23/17 5:22 PM |
+-------+---------------+--------+-----------------+
| Item2 | Category1Name | $45.05 | 8/24/17 6:22 PM |
+-------+---------------+--------+-----------------+
| Item3 | Category2Name | $35.50 | 9/25/17 7:22 PM |
+-------+---------------+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)

然而在 .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 |
+----+-------+-------+----------------+--------------------+
Run Code Online (Sandbox Code Playgroud)

文档

该文档声明它在 ASP.NET 和 ASP.NET MVC 中受支持。

System.ComponentModel.DataAnnotations 命名空间

System.ComponentModel.DataAnnotations 命名空间提供用于定义 ASP.NET MVC 和 ASP.NET 数据控件的元数据的属性类。

问题
似乎在 windows 窗体环境中没有采用/支持这些类。这是真的?

有没有一种简单的方法来实现对 WinForms 中数据注释的支持?

是否有一种简单的方法来注释可用于格式化 DataGridView 和/或 UltraGrid 的显示的类/实体?

Rez*_*aei 7

Windows 窗体中 DataGridView 的 DataAnnotations 属性

Windows 窗体中没有对数据批注的内置支持,但是知道属性如何工作以及 Windows 窗体如何工作,我们可以在 Windows 窗体中使用它们。

在这篇文章中,我将展示一个扩展方法,DataGridView该方法根据数据注释属性绑定IList<T>DataGridView和自动生成列,因此您可以DataGridView通过调用获得以下内容dataGridView1.Bind(list);

查看以下来自数据注释属性的项目:

  • 列可见性:Id 列不可见
  • 列标题文本:它们是与属性名称不同的自定义文本
  • 的顺序:列的顺序是自定义的,不同于属性顺序
  • 工具提示:我们已经为列显示了自定义工具提示。
  • 数据格式:我们对日期使用了自定义格式。

还有更多的东西你可以使用属性。

在此处输入图片说明

虽然模型是这样的:

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; }
}
Run Code Online (Sandbox Code Playgroud)

Bind<T> 扩展方法

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Windows 窗体的 DataAnnotations 验证属性

同样为了支持数据注释验证,您可以IDataErrorInfo使用Validator类来实现接口,就像我在Windows Forms 的 DataAnnotations Validation 属性中所做的那样。

笔记

为了增强答案,您可能需要创建一个关心元数据属性的类型描述符,然后用该类型描述符装饰模型。您可以从使用AssociatedMetadataTypeTypeDescriptor, MetadataPropertyDescriptorWrapper,的代码开始AssociatedMetadataTypeTypeDescriptionProvider

您也可以创建一个 Metadata 类并应用某些属性的效果,如UrlDataType对元数据。查看这篇文章会给你一些想法:将多个属性组合到一个属性 - 合并属性


LSU*_*Net 6

Infragistics UltraGrid 和 Windows DataGridView 都不支持这种方式的数据注释。从 15.1 开始,DevExpress 在其数据布局控件中具有一些有限的 WinForms 数据注释支持https://community.devexpress.com/blogs/thinking/archive/2015/06/08/winforms-data-layout-control-data-annotation-属性.aspx

我找到了这个链接,其中有人通过查看元数据询问有关如何抑制 Infragistics Grid 中的列的类似问题:http : //www.infragistics.com/community/forums/t/91864.aspx