目前,SourceGrid 仅有少量的设计时刻支持,因此通常你必须人工编写代码操作网格。
假定你已经有一个名为 grid1 的 Grid 控件,你可以在 Form.Load 事件中编写如下代码:
grid1.BorderStyle = BorderStyle.FixedSingle;
grid1.ColumnsCount = 3;
grid1.FixedRows = 1;
grid1.Rows.Insert(0);
grid1[0,0] = new SourceGrid.Cells.ColumnHeader("String");
grid1[0,1] = new SourceGrid.Cells.ColumnHeader("DateTime");
grid1[0,2] = new SourceGrid.Cells.ColumnHeader("CheckBox");
for (int r = 1; r < 10; r++)
{
grid1.Rows.Insert(r);
grid1[r,0] = new SourceGrid.Cells.Cell("Hello " + r.ToString(), typeof(string));
grid1[r,1] = new SourceGrid.Cells.Cell(DateTime.Today, typeof(DateTime));
grid1[r,2] = new SourceGrid.Cells.CheckBox(null, true);
}
grid1.AutoSizeCells();
如果你想改变某些单元格的可视属性,你必须使用 View 类。
让我们看看下一个示例:
grid1.BorderStyle = BorderStyle.FixedSingle;
grid1.ColumnsCount = 3;
grid1.FixedRows = 1;
grid1.Rows.Insert(0);
SourceGrid.Cells.Views.ColumnHeader boldHeader = new SourceGrid.Cells.Views.ColumnHeader();
boldHeader.Font = new Font(grid1.Font, FontStyle.Bold | FontStyle.Underline);
SourceGrid.Cells.Views.Cell yellowView = new SourceGrid.Cells.Views.Cell();
yellowView.BackColor = Color.Yellow;
SourceGrid.Cells.Views.CheckBox yellowViewCheck = new SourceGrid.Cells.Views.CheckBox();
yellowViewCheck.BackColor = Color.Yellow;
grid1[0, 0] = new SourceGrid.Cells.ColumnHeader("String");
grid1[0, 0].View = boldHeader;
grid1[0, 1] = new SourceGrid.Cells.ColumnHeader("DateTime");
grid1[0, 1].View = boldHeader;
grid1[0, 2] = new SourceGrid.Cells.ColumnHeader("CheckBox");
grid1[0, 2].View = boldHeader;
for (int r = 1; r < 10; r++)
{
grid1.Rows.Insert(r);
grid1[r, 0] = new SourceGrid.Cells.Cell("Hello " + r.ToString(), typeof(string));
grid1[r, 0].View = yellowView;
grid1[r, 1] = new SourceGrid.Cells.Cell(DateTime.Today, typeof(DateTime));
grid1[r, 1].View = yellowView;
grid1[r, 2] = new SourceGrid.Cells.CheckBox(null, true);
grid1[r, 2].View = yellowViewCheck;
}
我使用 FontStyle.Bold | FontStyle.Underline 建立了一个列标头外观,其中标准单元格的外观为黄色背景,复选框单元格的外观为黄色背景。然后我把它们的实例指派到了每个单元格的 View 属性中。
窗体看起来应该像下图所示的一样:
你可以注意到,我已经为许多单元格指派了 View 类的同一个实例。这对于优化所耗用的系统资源是有用的。
每个单元格可以有一个编辑器(Editor 属性) 相关。编辑器用于编辑单元格的值。你可以人工建立一个编辑类(参阅 SourceGrid.Cells.Editors 命名空间),或者使用 SourceGrid.Cells.Editors.Factory 类建立基于某种类型(Type)的编辑器。如果指定了Type 参数, 你也可以使用 Cell 构造函数自动调用 SourceGrid.Cells.Editors.Factory 。
下面是一个示例,建立一些单元格,并且使用上面所述的某一种方法把单元格与编辑器相关联。
//A DateTime editor
grid1[r, c] = new SourceGrid.Cells.Cell(DateTime.Today, typeof(DateTime));
//A string editor
grid1[r, c] = new SourceGrid.Cells.Cell("Ciao", typeof(string));
//A double editor
grid1[r, c] = new SourceGrid.Cells.Cell(58.4);
grid1[r, c].Editor = SourceGrid.Cells.Editors.Factory.Create(typeof(double));
就像 View 类一样,编辑器也可以在一个或数个单元格之间被共用。
现在,你可以开始使用 SourceGrid 工作了,更进一步的信息请参阅下面的章节。
如果你需要最有弹性的, 简易而没有太多单元格的网格, Grid 控件是理想的选择. 事实上, 此控件中每个单元格都作为一个 .NET 类来描述, 因此也占用一定数量的资源. 此外, 这是唯一支持 RowSpan 和 ColumnSpan(单元格合并)功能的网格.
在一个 Windows 窗体中使用此控件是最普通不过的事. 这正如增加其他控件(如按钮, DataGrid)一样. 首先,建立或者打开一个Windows 应用程序项目, 然后从设计器打开一个 Windows 窗体。 现在你要准备好使定制工具箱: 以鼠标右键单击“工具箱 — .NET Framework 组件 — 浏览”,选中“DevAge.SourceGrid.dll”。现在,网格控件被添加到工具箱中,可以像其他控件一样增加到 Windows 窗体中。
在增加控件到窗体后,我们就可以开始编写代码使用网格控件. 例如,在窗体的 Load 事件中,编写如下代码:
grid1.Redim(2, 2);
grid1[0,0] = new SourceGrid.Cells.Cell("Hello from Cell 0,0");
grid1[1,0] = new SourceGrid.Cells.Cell("Hello from Cell 1,0");
grid1[0,1] = new SourceGrid.Cells.Cell("Hello from Cell 0,1");
grid1[1,1] = new SourceGrid.Cells.Cell("Hello from Cell 1,1");
上述代码建立一个 2 行 2 列(Redim 方法) 的表,以单元格装入各个位置。我已经使用了包含实体单元格的 SourceGrid.Cells 命令空间。
可以使用 Value 属性读取特定单元格的值,就像这样: object val = grid1[1,0].Value;.
参阅其它章节了解更多信息。
当需要显示大量的单元格, 并且已经有可用的结构化数据(例如数据集, 数组, XML文档或其他数据结构)时, 使用 GridVirtual 控件是理想的选择.
除了自动排序(这是因为在复制任何外部数据结构的内容之前, grid 不能进行自动排列), 以及 RowSpan 和 ColumnSpan 这类允许单元格交叉跨越其它邻近单元格的功能(例如合并单元格功能)以外, 这种 GridVirtual 网格控件与 Grid 控件有同样的功能.
另一个缺点是建立虚网格稍显困难.
虚网格主要的概念是, 每个单元格从一个外部的数据结构读取和写入值, 以及网格不持有所有行和列, 而通常是直接从数据源中读取. 此观点以一个抽象的 GridVirtual 类实现, 并且有抽象的方法: CreateRowsObject, CreateColumnsObject 和 GetCell. 你可以使用特定的 IValueModel (接口)直接从数据源中读取值.
因此对于 GridVirtual , 需要建立一个派生于 GridVirtual 的类, 并且定制 CreateRowsObject, CreateColumnsObject 和 GetCell 方法读取数据源是首要的.
GetCell 方法的主要目的是返回一个给定的位置(行或列), 选定的单元格, 以及用于建立列和行对象 CreateRowsObject 和 CreateColumnsObject 方法使用的数据源。这样允许很大的弹性, 因为你可以返回任何 ICellVirtual 给一个特定的类型; 举例来说, 当行号为 0 时, 你可以返回单元格的标头类型.
通常, 你不需要直接使用 GridVirtual , 而是需要使用一个从它派生的控件. 目前, 我已经完成两个可以直接使用虚网格功能的控件:
如果你需要建立自定义控件, 从特定数据源读取数据, 你可以参阅 ArrayGrid 类的示例.
每个单元格由 4 个基本部分组成, 它们基于改进的“模式-外观-控制器(Model-View-Controller)”模型:
grid1.Redim(2,2);
grid1.RowsCount = 2; grid1.ColumnsCount = 2;
grid1.Rows.Insert(0); grid1.Rows.Insert(1); grid1.Columns.Insert(0); grid1.Columns.Insert(1);
grid1.Rows[0].Height = 100; grid1.Columns[0].Width = 100;
grid1.Redim(2,2); grid1[0, 0] = new SourceGrid.Cells.Cell("Cell 0, 0"); grid1[1, 0] = new SourceGrid.Cells.Cell("Cell 1, 0"); grid1[0, 1] = new SourceGrid.Cells.Cell("Cell 0, 1"); grid1[1, 1] = new SourceGrid.Cells.Cell("Cell 1, 1");
SourceGrid.Cells.Views.Cell view = new SourceGrid.Cells.Views.Cell(); view.BackColor = Color.Khaki; grid1[0,0].View = view; grid1[2,0].View = view;
public class MyView : SourceGrid.Cells.Views.Cell { protected override void DrawCell_Background(SourceGrid.Cells.ICellVirtual p_Cell, SourceGrid.Position p_CellPosition, PaintEventArgs e, Rectangle p_ClientRectangle) { base.DrawCell_Background (p_Cell, p_CellPosition, e, p_ClientRectangle); e.Graphics.DrawEllipse(Pens.Red, p_ClientRectangle); } }
MyView myView = new MyView(); //...... code to populate the grid grid1[r, c].View = myView;
编写一些代码行,你可以建立定制的控制器(custom Controller),在此情况下,建议类从已经拥有某些默认方法的 ControllerBase 派生。 在下列示例中,当用户在单元格之上移动鼠标时,建立一个改变单元格背景色的一个控制器:
public class MyController : SourceGrid.Cells.Controllers.ControllerBase
{
private SourceGrid.Cells.Views.Cell MouseEnterView = new SourceGrid.Cells.Views.Cell();
private SourceGrid.Cells.Views.Cell MouseLeaveView = new SourceGrid.Cells.Views.Cell();
public MyController()
{
MouseEnterView.BackColor = Color.Green;
}
public override void OnMouseEnter(SourceGrid.CellContext sender, EventArgs e)
{
base.OnMouseEnter (sender, e);
sender.Cell.View = MouseEnterView;
sender.Grid.InvalidateCell(sender.Position);
}
public override void OnMouseLeave(SourceGrid.CellContext sender, EventArgs e)
{
base.OnMouseLeave (sender, e);
sender.Cell.View = MouseLeaveView;
sender.Grid.InvalidateCell(sender.Position);
}
}
MyController myController = new MyController(); //...... code to populate the grid grid1[r, c].AddController(myController);
grid1.Controller.AddController(new MyController());
class ClickController : SourceGrid.Cells.Controllers.ControllerBase { public override void OnClick(SourceGrid.CellContext sender, EventArgs e) { base.OnClick(sender, e); object val = sender.Value; if (val != null) MessageBox.Show(sender.Grid, val.ToString()); } }
grid1.Controller.AddController(new ClickController());
public class ValueChangedEvent : SourceGrid.Cells.Controllers.ControllerBase { public override void OnValueChanged(SourceGrid.CellContext sender, EventArgs e) { base.OnValueChanged(sender, e); string val = "Value of cell {0} is '{1}'"; MessageBox.Show(sender.Grid, string.Format(val, sender.Position, sender.Value)); } }
grid1.Controller.AddController(new ValueChangedEvent());
//String cell grid1[0, 0] = new SourceGrid.Cells.Cell("Hello", typeof(string)); //Double cell grid1[0, 1] = new SourceGrid.Cells.Cell(0.7, typeof(double));
//String editor SourceGrid.Cells.Editors.IEditor editorString = SourceGrid.Cells.Editor.Factory.Create(typeof(string)); //Double editor SourceGrid.Cells.Editors.IEditor editorDouble = SourceGrid.Cells.Editor.Factory.Create(typeof(double)); //String cell grid1[0, 0] = new SourceGrid.Cells.Cell("Hello"); grid1[0, 0].Editor = editorString; //Double cell grid1[0, 1] = new SourceGrid.Cells.Cell(0.7); grid1[0, 1].Editor = editorDouble;当你需要为多个单元格使用相同的编辑器时, 推荐使用此方法。
SourceGrid.Cells.Editors.TextBox txtBox = new SourceGrid.Cells.Editors.TextBox(typeof(string)); grid1[2,0].Editor = txtBox;
//String 型编辑器 SourceGrid.Cells.Editors.TextBox editorString = new SourceGrid.Cells.Editors.TextBox(typeof(string)); editorString.Control.MaxLength = 10; //字符串单元格 grid1[0, 0] = new SourceGrid.Cells.Cell("Hello"); grid1[0, 0].Editor = editorString;
编辑器也可以被用作定制单元格的格式, 在下面的代码中举例来说明(使用一种定制的数值格式):
//Double 型编辑器
SourceGrid.Cells.Editors.TextBoxNumeric editorDouble = new SourceGrid.Cells.Editors.TextBoxNumeric(typeof(double));
editorDouble.TypeConverter = new DevAge.ComponentModel.Converter.NumberTypeConverter(typeof(double), "#,###.00");
//字符串单元格
grid1[0, 0] = new SourceGrid.Cells.Cell(9419.3894);
grid1[0, 0].Editor = editorDouble;
下面是建立定制格式的 DateTime 编辑器的另一个实例:
//有定制格式的 DateTime 编辑器
string[] dtParseFormats = new string[] { dtFormat2 };
System.Globalization.DateTimeStyles dtStyles = System.Globalization.DateTimeStyles.AllowInnerWhite |
System.Globalization.DateTimeStyles.AllowLeadingWhite |
System.Globalization.DateTimeStyles.AllowTrailingWhite |
System.Globalization.DateTimeStyles.AllowWhiteSpaces;
TypeConverter dtConverter = new DevAge.ComponentModel.Converter.DateTimeTypeConverter(dtFormat2, dtParseFormats, dtStyles);
SourceGrid.Cells.Editors.TextBoxUITypeEditor editorDt2 = new SourceGrid.Cells.Editors.TextBoxUITypeEditor(typeof(DateTime));
editorDt2.TypeConverter = dtConverter;
grid[currentRow, 1] = new SourceGrid.Cells.Cell(DateTime.Today);
grid[currentRow, 1].Editor = editorDt2;
下图表示大部份可用的编辑器和一些特定单元格(图片从例 3 取得):
以用户控件或特定行为的少量代码行, 来建立一个定制的编辑器(Editor)是可行的。
你可以从 EditorControlBase 派生自定义类, 并建立任何 Windows 窗体控件。下面是使用 DateTimePicker 控件的一个编辑器的示例:
public class DateTimePicker : EditorControlBase
{
public DateTimePicker():base(typeof(System.DateTime))
{
}
protected override Control CreateControl()
{
System.Windows.Forms.DateTimePicker dtPicker =
new System.Windows.Forms.DateTimePicker();
dtPicker.Format = DateTimePickerFormat.Short;
dtPicker.ShowCheckBox = AllowNull;
return dtPicker;
}
protected override void OnChanged(EventArgs e)
{
base.OnChanged(e);
if (Control != null)
Control.ShowCheckBox = AllowNull;
}
public new System.Windows.Forms.DateTimePicker Control
{
get
{
return (System.Windows.Forms.DateTimePicker)base.Control;
}
}
protected override void OnStartingEdit(CellContext cellContext,
Control editorControl)
{
base.OnStartingEdit(cellContext, editorControl);
System.Windows.Forms.DateTimePicker dtPicker =
(System.Windows.Forms.DateTimePicker)editorControl;
dtPicker.Font = cellContext.Cell.View.Font;
}
public override void SetEditValue(object editValue)
{
if (editValue is DateTime)
Control.Value = (DateTime)editValue;
else if (editValue == null)
Control.Checked = false;
else
throw new SourceGridException
("Invalid edit value, expected DateTime");
}
public override object GetEditedValue()
{
if (Control.Checked)
return Control.Value;
else
return null;
}
protected override void OnSendCharToEditor(char key)
{
}
}
SourceGrid.Cells.Virtual —— 此命名空间包含可以与 GridVirtual 控件一起使用的所有的虚单元格。
举例来说,SourceGrid.Cells.CheckBox 单元格的代码如下:
public class CheckBox : Cell
{
public CheckBox(string caption, bool checkValue):base(checkValue)
{
if (caption != null && caption.Length > 0)
View = Views.CheckBox.MiddleLeftAlign;
else
View = Views.CheckBox.Default;
Model.AddModel(new Models.CheckBox());
AddController(Controllers.CheckBox.Default);
AddController(Controllers.MouseInvalidate.Default);
Editor = new Editors.EditorBase(typeof(bool));
Caption = caption;
}
private Models.CheckBox CheckBoxModel
{
get{return (Models.CheckBox)Model.FindModel(typeof(Models.CheckBox));}
}
public bool Checked
{
get{return CheckBoxModel.GetCheckBoxStatus(this, Range.Start).Checked;}
set{CheckBoxModel.SetCheckedValue(this, Range.Start, value);}
}
public string Caption
{
get{return CheckBoxModel.Caption;}
set{CheckBoxModel.Caption = value;}
}
}
SourceGrid.CellContext context = new SourceGrid.CellContext(grid, new SourceGrid.Position(r, c)); context.Value = "hello"; context.StartEdit();
下面是一个改变单元格边框的示例:
DevAge.Drawing.Border border = new DevAge.Drawing.Border(Color.Red, 1);
DevAge.Drawing.RectangleBorder cellBorder = new DevAge.Drawing.RectangleBorder(border, border);
SourceGrid.Cells.Views.Cell view = new SourceGrid.Cells.Views.Cell();
view.Border = cellBorder;
grid[r, c].View = view;
你可以在每个单元格上绑定一个提示条(ToolTip). 必须建立一个SourceGrid.Cells.Controllers.ToolTipText 控制器 , 并把它关联到单元格中.示例如下:
SourceGrid.Cells.Controllers.ToolTipText toolTipController = new SourceGrid.Cells.Controllers.ToolTipText();
toolTipController.ToolTipTitle = "ToolTip example";
toolTipController.ToolTipIcon = ToolTipIcon.Info;
toolTipController.IsBalloon = true;
grid1[r, c] = new SourceGrid.Cells.Cell("Hello");
grid1[r, c].ToolTipText = "Example of tooltip, bla bla bla ....";
grid1[r, c].AddController(toolTipController);
单元格的 ToolTipText 属性自动把一个 SourceGrid.Cells.Models.IToolTipText 接口绑定到标准单元格上.
使用下列代码, 你可以为单元格建立一个上下文菜单(快捷菜单) . 首先以 ContextMenu 定义一个 控制器(controller) :
//以 ContextMenu 定义一个 controller
public class PopupMenu : SourceGrid.Cells.Controllers.ControllerBase
{
ContextMenu menu = new ContextMenu();
public PopupMenu()
{
menu.MenuItems.Add("Menu 1", new EventHandler(Menu1_Click));
menu.MenuItems.Add("Menu 2", new EventHandler(Menu2_Click));
}
public override void OnMouseUp(SourceGrid.CellContext sender, MouseEventArgs e)
{
base.OnMouseUp (sender, e);
if (e.Button == MouseButtons.Right)
menu.Show(sender.Grid, new Point(e.X, e.Y));
}
private void Menu1_Click(object sender, EventArgs e)
{
//TODO Your code here
}
private void Menu2_Click(object sender, EventArgs e)
{
//TODO Your code here
}
}
PopupMenu menuController = new PopupMenu(); ... grid1[r, c] = new SourceGrid.Cells.Cell("Hello"); grid1[r, c].AddController(menuController);
grid1.ClipboardMode = SourceGrid.ClipboardMode.All;
如果你需要定制某些单元格的外观, 建议建立一个新类, 并在此类编写定制代码. 以这种习惯, 你可以在所有应用程序和窗体中重用类. 举例来说, 假如你需要指定所有单元格的背景色为灰色, 可以新建下面的类:
public class GrayView : SourceGrid.Cells.Views.Cell
{
public new static readonly GrayView Default = new GrayView();
public GrayView()
{
BackColor = Color.LightGray;
}
}
public class GrayCell : SourceGrid.Cells.Cell
{
public GrayCell(object val):base(val)
{
View = GrayView.Default;
}
}
SourceGrid.Cells.Button bt = new SourceGrid.Cells.Button();
using Cells = SourceGrid.Cells; ................. Cells.Button bt = new Cells.Button();
基本上, DataGrid 控件使用一个类型为 IValueModel 的特殊 Model 类, 直接从数据源中读取数据。 SourceGrid.DataGrid 有一个 DataSource 属性用于存储 BoundListBase 对象. 下面是如何使用该控件的一个简单示例:
//建立实例 DataTable
DataTable table = new DataTable();
table.Columns.Add("A", typeof(string));
table.Columns.Add("B", typeof(bool));
table.Rows.Add(new object[]{"Row 1", false});
table.Rows.Add(new object[]{"Row 2", true});
table.Rows.Add(new object[]{"Row 3", false});
dataGrid1.DataSource = new DevAge.ComponentModel.BoundDataView(table.DefaultView);
在上面的代码中, 我已经建立一个 2 列 n 行的 DataTable , 接下来你可以使用 DefaultView 属性取一个 DataView 类, 并通过建立一个 BoundDataView 类的实例指派到 DataGrid 控件. 如果你需要可定制的列, 可使用 Columns 属性. 该属性返回一个DataGridColumn 对象的集合, 你可以自定义每列来建立定制单元格.下面是实例:
//建立一个自定义 View 类
SourceGrid.Cells.Views.Cell view = new SourceGrid.Cells.Views.Cell();
view.BackColor = Color.LightBlue;
//人工添加列
SourceGrid.DataGridColumn gridColumn;
gridColumn = dataGrid.Columns.Add("Country", "Country", typeof(string));
gridColumn.DataCell.View = view;
SourceGrid.Conditions.ICondition condition = SourceGrid.Conditions.ConditionBuilder.AlternateView(gridColumn.DataCell.View, Color.LightGray, Color.Black); gridColumn.Conditions.Add(condition);
在下面其他示例中, 我建立一些条件, 当特定列为 true 时实现粗体绿色前景的外观.
SourceGrid.Conditions.ConditionView selectedConditionBold =
new SourceGrid.Conditions.ConditionView(viewSelected);
selectedConditionBold.EvaluateFunction = delegate(SourceGrid.DataGridColumn column,
int gridRow, object itemRow)
{
DataRowView row = (DataRowView)itemRow;
return row["Selected"] is bool && (bool)row["Selected"] == true;
};
gridColumn.Conditions.Add(selectedConditionBold);