Windows 窗体中没有对 Data Annotation 属性(包括 UIHint)的内置支持,但考虑到这些属性的工作方式,您可以扩展框架和控件以在 Windows 窗体中使用这些属性。
-
创建一个 Windows 窗体应用程序。
-
在 Form1 上放置一个 DataGridView 的实例(并将其设置为停靠在父容器中)
-
在项目中添加Person.cs文件,并将以下代码粘贴到文件中:
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public class Person
{
[Display(Name = "Id")]
[Browsable(false)]
public int? Id { get; set; }
[Display(Name = "First Name", Description = "First name.", Order = 1)]
[UIHint("TextBox")]
public string FirstName { get; set; }
[Display(Name = "Last Name", Description = "Last name", Order = 2)]
[UIHint("TextBox")]
public string LastName { get; set; }
[Display(Name = "Birth Date", Description = "Date of birth.", Order = 4)]
[DisplayFormat(DataFormatString = "yyyy-MM-dd")]
[UIHint("Calendar")]
public DateTime BirthDate { get; set; }
[Display(Name = "Homepage", Description = "Url of homepage.", Order = 5)]
[UIHint("Link")]
public string Url { get; set; }
[Display(Name = "Member", Description = "Is member?", Order = 3)]
[UIHint("CheckBox")]
public bool IsMember { get; set; }
}
-
创建一个名为 DataGridViewCalendarColumn.cs 的代码文件并将以下代码粘贴到文件中(这是基于MS Docs example,只是为了尊重更改格式而稍作改动):
using System;
using System.Windows.Forms;
public class DataGridViewCalendarColumn : DataGridViewColumn
{
public DataGridViewCalendarColumn() : base(new DataGridViewCalendarCell())
{
}
public override DataGridViewCell CellTemplate
{
get
{
return base.CellTemplate;
}
set
{
// Ensure that the cell used for the template is a CalendarCell.
if (value != null &&
!value.GetType().IsAssignableFrom(typeof(DataGridViewCalendarCell)))
{
throw new InvalidCastException("Must be a CalendarCell");
}
base.CellTemplate = value;
}
}
}
public class DataGridViewCalendarCell : DataGridViewTextBoxCell
{
public DataGridViewCalendarCell()
: base()
{
// Use the short date format.
// this.Style.Format = "d";
}
public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
// Set the value of the editing control to the current cell value.
base.InitializeEditingControl(rowIndex, initialFormattedValue,
dataGridViewCellStyle);
DataGridViewCalendarEditingControl ctl =
DataGridView.EditingControl as DataGridViewCalendarEditingControl;
// Use the default row value when Value property is null.
if (this.Value == null)
{
ctl.Value = (DateTime)this.DefaultNewRowValue;
}
else
{
ctl.Value = (DateTime)this.Value;
}
}
public override Type EditType
{
get
{
// Return the type of the editing control that CalendarCell uses.
return typeof(DataGridViewCalendarEditingControl);
}
}
public override Type ValueType
{
get
{
// Return the type of the value that CalendarCell contains.
return typeof(DateTime);
}
}
public override object DefaultNewRowValue
{
get
{
// Use the current date and time as the default value.
return DateTime.Now;
}
}
}
class DataGridViewCalendarEditingControl : DateTimePicker,
IDataGridViewEditingControl
{
DataGridView dataGridView;
private bool valueChanged = false;
int rowIndex;
public DataGridViewCalendarEditingControl()
{
//this.Format = DateTimePickerFormat.Short;
}
// Implements the IDataGridViewEditingControl.EditingControlFormattedValue
// property.
public object EditingControlFormattedValue
{
get
{
return this.Value.ToShortDateString();
}
set
{
if (value is String)
{
try
{
// This will throw an exception of the string is
// null, empty, or not in the format of a date.
this.Value = DateTime.Parse((String)value);
}
catch
{
// In the case of an exception, just use the
// default value so we're not left with a null
// value.
this.Value = DateTime.Now;
}
}
}
}
// Implements the
// IDataGridViewEditingControl.GetEditingControlFormattedValue method.
public object GetEditingControlFormattedValue(
DataGridViewDataErrorContexts context)
{
return EditingControlFormattedValue;
}
// Implements the
// IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
public void ApplyCellStyleToEditingControl(
DataGridViewCellStyle dataGridViewCellStyle)
{
this.Font = dataGridViewCellStyle.Font;
this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
if (!string.IsNullOrEmpty(dataGridViewCellStyle.Format))
{
this.Format = DateTimePickerFormat.Custom;
this.CustomFormat = dataGridViewCellStyle.Format;
}
else
{
this.Format = DateTimePickerFormat.Short;
}
}
// Implements the IDataGridViewEditingControl.EditingControlRowIndex
// property.
public int EditingControlRowIndex
{
get
{
return rowIndex;
}
set
{
rowIndex = value;
}
}
// Implements the IDataGridViewEditingControl.EditingControlWantsInputKey
// method.
public bool EditingControlWantsInputKey(
Keys key, bool dataGridViewWantsInputKey)
{
// Let the DateTimePicker handle the keys listed.
switch (key & Keys.KeyCode)
{
case Keys.Left:
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.PageDown:
case Keys.PageUp:
return true;
default:
return !dataGridViewWantsInputKey;
}
}
// Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit
// method.
public void PrepareEditingControlForEdit(bool selectAll)
{
// No preparation needs to be done.
}
// Implements the IDataGridViewEditingControl
// .RepositionEditingControlOnValueChange property.
public bool RepositionEditingControlOnValueChange
{
get
{
return false;
}
}
// Implements the IDataGridViewEditingControl
// .EditingControlDataGridView property.
public DataGridView EditingControlDataGridView
{
get
{
return dataGridView;
}
set
{
dataGridView = value;
}
}
// Implements the IDataGridViewEditingControl
// .EditingControlValueChanged property.
public bool EditingControlValueChanged
{
get
{
return valueChanged;
}
set
{
valueChanged = value;
}
}
// Implements the IDataGridViewEditingControl
// .EditingPanelCursor property.
public Cursor EditingPanelCursor
{
get
{
return base.Cursor;
}
}
protected override void OnValueChanged(EventArgs eventargs)
{
// Notify the DataGridView that the contents of the cell
// have changed.
valueChanged = true;
this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
base.OnValueChanged(eventargs);
}
}
-
在项目中添加UIHintMappings.cs文件,并将以下代码粘贴到文件中:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
public class UIHintMappings
{
public static Dictionary<string, Func<DataGridViewColumn>> DataGridViewColumns
{
get;
}
static UIHintMappings()
{
DataGridViewColumns = new Dictionary<string, Func<DataGridViewColumn>>();
DataGridViewColumns.Add("TextBox",
() => new DataGridViewTextBoxColumn());
DataGridViewColumns.Add("CheckBox",
() => new DataGridViewCheckBoxColumn(false));
DataGridViewColumns.Add("TreeStateCheckBox",
() => new DataGridViewCheckBoxColumn(true));
DataGridViewColumns.Add("Link",
() => new DataGridViewLinkColumn());
DataGridViewColumns.Add("Calendar",
() => new DataGridViewCalendarColumn());
}
}
-
将DataGridViewExtensions.cs文件添加到项目中,并将以下代码粘贴到文件中:
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,
UIHint = p.Attributes.OfType<UIHintAttribute>()
.FirstOrDefault()?.UIHint
});
var columns = metedata.OrderBy(m => m.Order).Select(m =>
{
DataGridViewColumn c;
if(!string.IsNullOrEmpty( m.UIHint) &&
UIHintMappings.DataGridViewColumns.ContainsKey(m.UIHint))
{
c = UIHintMappings.DataGridViewColumns[m.UIHint].Invoke();
}
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;
}
}
-
在设计模式下双击Form1并使用以下代码处理Load事件:
private void Form1_Load(object sender, EventArgs e)
{
var list = new List<Person>()
{
new Person()
{
Id= 1, FirstName= "Mario", LastName= "Speedwagon",
BirthDate = DateTime.Now.AddYears(-30).AddMonths(2).AddDays(5),
IsMember = true, Url ="https://Mario.example.com"
},
new Person()
{
Id= 1, FirstName= "Petey", LastName= "Cruiser",
BirthDate = DateTime.Now.AddYears(-20).AddMonths(5).AddDays(1),
IsMember = false, Url ="https://Petey.example.com"
},
new Person()
{
Id= 1, FirstName= "Anna", LastName= "Sthesia",
BirthDate = DateTime.Now.AddYears(-40).AddMonths(3).AddDays(8),
IsMember = true, Url ="https://Anna.example.com"
},
};
this.dataGridView1.Bind(list, true);
}