这是项目中用到的一个技术,很多情况下,客户需要大量录入数据,这是为了提高效率,需要在客户端的用户界面上采用DataGrid来处理,但是.NET提供的DataGrid控件的功能有限,一般只能处理文本,当某一列的数据类型是时间和日期型的时候,客户的输入相当麻烦,也不能有效处理用户误输入的错误格式,如果能够将时间日期控件绑定到Grid的单元格中,既加快了用户的录入,也能有效防止用户的错误录入。
进一步,如果我们自定义了一个控件,类似文本框,但是针对特定的应用做过调整,我们也想把它绑定到Grid中,这种情况下也可以采用这里提到的技术。
要想在DataGrid中绑定一个控件,首先需要了解的是在DataGrid中,外观和内容的关系,DataGrid把数据和外观分得很清楚,可以这么认为,DataGrid就是一个空架子,背后有一个非可视化的东西在支持它,这就是DataTableStyle,可以将这个类看作一个集合,是由DataGridColumnStyle组成的,而DataGridColumnStyle本身有外观和数据2个方面的属性,我们绑定一个控件实际上是改变DataGridColumnStyle的外观部分的属性,数据部分的属性等不用管它,这样就能够把需要绑定的控件与DataGridColumnStyle关联起来,进一步就可以和DataTableStyle关联起来,也就可以改变DataGrid的外观了。
因此所有的工作集中在一点上就是完成一个DataGridColumnStyle的改造。所谓的改造也就是从DataGridColumnStyle继承一下。实际上,DataGridTextBoxColumn就是从DataGridColumnStyle继承的。
下面的一个类是将DateTimPicker绑定到DataGridColumnStyle上的一段代码,绑定其它的控件都可以按照这个方法完成。
public class DataGridTimePickerColumn : DataGridColumnStyle
{
//这里增加需要被DataGridColumnStyle承载的控件,这里用的是一个
//DateTimePicker,日期控件,其他的控件类似
private DateTimePicker myDateTimePicker = new DateTimePicker();
//isEditing属性用于跟踪用户是否在宿主控件中编辑了其中的数据。
private bool isEditing;
//构造函数
public DataGridTimePickerColumn() : base()
{
myDateTimePicker.Visible = false;
myDateTimePicker.Format=DateTimePickerFormat.Time;
myDateTimePicker.ShowUpDown=true;
}
///
/// 此方法必须重载,Abort将启动一个请求来中断编辑过程。
///
/// 当前中断的行号
protected override void Abort(int rowNum)
{
isEditing = false;
myDateTimePicker.ValueChanged -=new EventHandler(TimePickerValueChanged);
Invalidate();
}
///
/// 此方法必须被重载,而且必须返回2个状态,也就是true或者false,返回false的时候
/// DataGrid就会用到Abort
///
/// 实际上是一个BindManagerBase
/// 当前提交的行号
///
protected override bool Commit(CurrencyManager dataSource, int rowNum)
{
myDateTimePicker.Bounds = Rectangle.Empty; //表示其属性未被初始化的 Rectangle 结构。
myDateTimePicker.ValueChanged -= new EventHandler(TimePickerValueChanged);
if (!isEditing)
return true;
isEditing = false;
try
{
DateTime dtValue = myDateTimePicker.Value;
SetColumnValueAtRow(dataSource, rowNum, dtValue);
}
catch (Exception)
{
Abort(rowNum);
return false;
}
Invalidate();
return true;
}
///
/// 在派生类中被重写时,将准备一个将要进行编辑的单元格。
/// 通常,Edit 方法将控件定位到网格上要编辑的单元格的位置。
///
/// DataGridColumnStyle的CurrencyManager,管理 Binding 对象的列表
/// 此列中所编辑的行的行号
/// 控件将被放置在其中
/// 指示该列是否为只读的列的值。如果该值是只读,则为 true;否则为 false
/// 控件中将显示的文本
/// 指示该单元格是否可见的值。如果该单元格可见,则为 true;否则为 false
protected override void Edit(
CurrencyManager source,
int rowNum,
Rectangle bounds,
bool readOnly,
string instantText,
bool cellIsVisible)
{
DateTime dtValue;
object obj = GetColumnValueAtRow(source, rowNum);
if (obj.GetType().ToString()=="System.DateTime")
{
dtValue = (DateTime)obj;
}
else
{
dtValue=DateTime.Now;
}
if (cellIsVisible)
{
myDateTimePicker.Bounds=new Rectangle(bounds.X+2,bounds.Y+2,bounds.Width-4,bounds.Height-4);
myDateTimePicker.Value = dtValue;
myDateTimePicker.Visible = true;
//挂接一个事件,就是承载的控件的变量发生变化的时候发生的事件
//在此事件的方法中主要是改变编辑的状态以及调用基类的方法ColumnStartedEditing
//这个方法用来通知DataGrid开始进入编辑状态
myDateTimePicker.ValueChanged +=new EventHandler(TimePickerValueChanged);
}
else
{
myDateTimePicker.Value = dtValue;
myDateTimePicker.Visible = false;
}
if (myDateTimePicker.Visible)
DataGridTableStyle.DataGrid.Invalidate(bounds);
}
protected override Size GetPreferredSize(Graphics g, object value)
{
return new Size(100, myDateTimePicker.PreferredHeight + 2);
}
protected override int GetMinimumHeight()
{
return myDateTimePicker.PreferredHeight + 2;
}
protected override int GetPreferredHeight(Graphics g,
object value)
{
return myDateTimePicker.PreferredHeight + 2;
}
//重载之一,一个简单的版本,其他没有涉及到的参数采用了默认值
protected override void Paint(Graphics g,
Rectangle bounds,
CurrencyManager source,
int rowNum)
{
Paint(g, bounds, source, rowNum, false);
}
//重载之二,一个简单的版本,其他没有涉及到的参数采用了默认值
protected override void Paint(
Graphics g,
Rectangle bounds,
CurrencyManager source,
int rowNum,
bool alignToRight)
{
Paint(
g,bounds,
source,
rowNum,
Brushes.Red,
Brushes.Blue,
alignToRight);
}
///
/// 此方法必须重载,而且,这个方法是一个全面的方法,上面2个方法都是重载的这个方法
/// 此方法应该是在所承载的控件离开DataGrid之后在DataGrid中写入其中内容的方法
///
///
///
///
///
///
///
///
protected override void Paint(
Graphics g,
Rectangle bounds,
CurrencyManager source,
int rowNum,
Brush backBrush,
Brush foreBrush,
bool alignToRight)
{
DateTime dtValue;
object obj = GetColumnValueAtRow(source, rowNum);
if (obj.GetType().ToString()=="System.DateTime")
{
dtValue = (DateTime)obj;
}
else
{
dtValue=DateTime.Now;
}
Rectangle rect = bounds;
g.FillRectangle(backBrush,rect);
rect.Offset(0, 2);
rect.Height -= 2;
g.DrawString(dtValue.ToString("d"),this.DataGridTableStyle.DataGrid.Font,foreBrush, rect);
}
///
/// 将要承载的控件关联到DataGrid上
///
///
protected override void SetDataGridInColumn(DataGrid dgValue)
{
base.SetDataGridInColumn(dgValue);
if (myDateTimePicker.Parent != null)
{
myDateTimePicker.Parent.Controls.Remove(myDateTimePicker);
}
if (dgValue != null)
{
dgValue.Controls.Add(myDateTimePicker);
}
}
///
/// 被承载的控件的内容发生变化的时候表明用户进行了编辑,此时执行这个事件处理函数
///
///
///
private void TimePickerValueChanged(object sender, EventArgs e)
{
this.isEditing = true;
//通知DataGrid已经开始编辑这一列,其中的参数是需要承载的控件
base.ColumnStartedEditing(myDateTimePicker);
}
}