DataAnnotations支持WinForms

时间:2015-08-14 17:10:36

标签: winforms data-annotations

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

这非常有用,因为我可能有几个显示此类的网格,而不是配置每个网格以显示相应的标题文本,我只需设置一个数据注释。

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

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

示例

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

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 | $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 |
+-------+---------------+--------+-----------------+

然而,在.Net 4.5.2 WinForms中使用DataGridViews的实践中,datagrid实际上显示如下:

+----+-------+-------+----------------+--------------------+
| 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

  

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

问题
似乎这些类在Windows窗体环境中尚未被采用/支持。这是真的吗?

是否有一种简单的方法可以在WinForms中实现对数据注释的支持?

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

2 个答案:

答案 0 :(得分:7)

Windows窗体中DataGridView的

DataAnnotations属性

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

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

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

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

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

enter image description here

虽然模型是这样的:

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验证属性

另外,为了支持数据注释验证,您可以使用IDataErrorInfo类来实现Validator接口,就像在DataAnnotations Validation attributes for Windows Forms中一样。

注意

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

您还可以创建一个元数据类,并在元数据上应用某些属性,例如UrlDataType。查看这篇文章将给您一些想法:Combining multiple Attributes to a single Attribute - Merge Attributes

答案 1 :(得分:2)

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

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