内容 介绍 特性 XPTable 使用XPTable 表格 ColumnModel 列 TableModel 行 细胞 渲染器 CellRenderers HeaderRenderers 编辑器 视觉风格 排序 选择 未来的功能 历史 介绍 对于我正在工作的一个项目,我需要一个高度定制的ListView -将允许复选框和图像在任何列,组合框和数字向上的编辑,它必须很容易交换数据进出。任何尝试过自定义ListView的人都知道,按照自己的意愿来定制它是多么痛苦,所以我决定从头创建一个。由于具有Java背景,我决定将其松散地基于Java的JTable。 特性 完全可自定义的视觉外观-从列到行和单元格。 支持Windows XP视觉样式。 强大的渲染器,赋予细胞像控件一样的行为能力。 很容易添加您自己的自定义渲染器和编辑器。 可以隐藏列。 可以禁用行、列或单个单元格。 列和单元格的工具提示。 加更…… XPTable XPTable由以下组件组成: 一个表, 柱状模型和它的列, 一个TableModel,它的行和单元格, 渲染器和 编辑器 我不打算详细介绍前三点,只介绍第4点和第5点的基础知识,否则这篇文章将比现在的内容大得多。如果您想了解这些主题的更多细节,那么您应该阅读文档附带的用户指南。 使用XPTable 在使用XPTable之前,需要在项目的References部分添加对XPTable.dll的引用。 要将XPTable.dll添加到工具箱,您可以: 选择工具→从菜单中添加/删除工具箱项,或 右键单击工具箱,选择添加/删除项目。 然后浏览到XPTable.dll,然后按OK。然后可以将控件拖放到窗体上。 注意:如果重新编译源代码,则需要对XPTable重新签名。否则,当您试图将其添加到工具箱时,Visual Studio可能会抛出异常。 打开VS . net命令提示符,并将目录更改为指向XPTable\bin\发布目录。 然后键入“sn -R XPTable.dll .\..\XPTable”。snk”(当然没有引号)。 然后就可以将其添加到工具箱中了。 之后,您所需要做的就是将表格、ColumnModel和TableModel拖放到表单上,设置表格的ColumnModel和TableModel属性,并将列添加到ColumnModel中,将行和单元格添加到TableModel中。 或者如果你更喜欢密码:Hide 复制Code
Table table = new Table(); ColumnModel columnModel = new ColumnModel(); TableModel tableModel = new TableModel(); // set the Table's ColumModel and TableModel table.ColumnModel = columnModel; table.TableModel = tableModel; // add some Columns to the ColumnModel columnModel.Columns.Add(new TextColumn("Text")); columnModel.Columns.Add(new CheckBoxColumn("CheckBox")); columnModel.Columns.Add(new ButtonColumn("Button")); // add some Rows and Cells to the TableModel tableModel.Rows.Add(new Row()); tableModel.Rows[0].Cells.Add(new Cell("Text 1")); tableModel.Rows[0].Cells.Add(new Cell("CheckBox 1", true)); tableModel.Rows[0].Cells.Add(new Cell("Button 1")); tableModel.Rows.Add(new Row()); tableModel.Rows[1].Cells.Add(new Cell("Text 2")); tableModel.Rows[1].Cells.Add(new Cell("CheckBox 2", false)); tableModel.Rows[1].Cells.Add(new Cell("Button 2"));
表格 表是“简单”对象,因为它实际上不包含或不知道如何绘制它将显示的数据。相反,它使用ColumnModel来跟踪它的列,使用TableModel来跟踪它的行和单元格,使用呈现器和编辑器来绘制和编辑它的数据。表的主要作用是管理绘图操作,并将事件传递给渲染器和编辑器,以便它们能够采取适当的操作。 ColumnModel ColumnModel包含将在表中显示的列集合。它还跟踪是否为特定列创建了CellRenderer或CellEditor。 列 在考虑了实现列的最佳方式之后,我决定使用与DataGrid相同的方法——即根据其单元格将包含的数据类型使用不同类型的列。以下列类型可用: 列-所有列的基类。 TextColumn -单元格显示为字符串的列。 ButtonColumn—单元格显示为按钮的列。 将单元格显示为复选框的列。 ImageColumn -单元格显示为图像的列。 NumberColumn -单元格显示为数字的列。 ProgressBarColumn—单元格显示为ProgressBars的列。 用于显示用于编辑的下拉框的列的基类。 表示其单元格显示为组合框的列。 表示单元格中包含日期时间的列。 表示单元格包含颜色的列。 TableModel TableModel包含将在表中显示的行集合。 行 一行表示表中的一行,并包含将在行中显示的单元格集合。 细胞 单元格包含将在表格中显示的一段数据。 渲染器 如前所述,表格不知道如何绘制单元格或列标题。相反,它使用称为渲染器的对象来为它做所有的绘图。Java网站将渲染器描述为“一个可配置的墨水标记,表格使用它将适当格式化的数据标记到每个单元格上”。 表格使用两种不同类型的渲染器:绘制单元格的CellRenderers和绘制列标题的HeaderRenderers CellRenderers CellRenderers是功能强大的对象,因为它们可以实现llow单元格的外观和行为与Windows控件类似,而不消耗任何额外的资源。 下面的列表显示了XPTable提供的所有cellrenderer: ICellRenderer——公开单元格渲染器提供的常用方法。 所有单元格渲染器的基类。 一个绘制单元格内容为字符串的单元格渲染器。 一个绘制单元格内容为按钮的单元格渲染器。 CheckBoxCellRenderer -一个绘制单元格内容为复选框的单元格渲染器。 一个以图像形式绘制单元格内容的单元格渲染器。 一个绘制单元格内容为数字的单元格渲染器。 ProgressBarCellRenderer -一个将单元格内容绘制为ProgressBar的CellRenderer。 DropDownCellRenderer -用于绘制单元格内容(如组合框)的单元格渲染器的基类。 一个绘制单元格内容作为组合框的单元格渲染器。 一个以颜色绘制单元格内容的单元格渲染器。 DateTimeCellRenderer -一个绘制单元格内容作为日期时间的单元格渲染器。 下图显示了每个CellRenderer的默认输出: 创建自定义的CellRenderer 如果你想创建一个自定义的CellRenderer,你有两个选择-子类CellRenderer和覆盖(至少)OnPaint和OnPaintBackground方法(最简单和首选的方法)或者实现ICellRenderer(很多工作)。 下面是在TextCellRenderer中构建的表格代码:收缩,复制Code
public class TextCellRenderer : CellRenderer { protected override void OnPaint(PaintCellEventArgs e) { base.OnPaint(e); // don't bother going any further if the Cell is null if (e.Cell == null) { return; } // make sure we have some text to draw if (e.Cell.Text != null && e.Cell.Text.Length != 0) { // check whether the cell is enabled if (e.Enabled) { e.Graphics.DrawString(e.Cell.Text, base.Font, base.ForeBrush, base.ClientRectangle, base.StringFormat); } else { e.Graphics.DrawString(e.Cell.Text, base.Font, base.GrayTextBrush, base.ClientRectangle, base.StringFormat); } } // draw a focus rect around the cell if it is // enabled and has focus if (e.Focused && e.Enabled) { ControlPaint.DrawFocusRectangle(e.Graphics, base.ClientRectangle); } } }
有关更复杂的示例,请参阅文档提供的用户指南。 HeaderRenderers 与按列使用的cellrenderer不同,表使用单个HeaderRenderer来绘制它的所有列标题。 下面的列表显示了XPTable提供的所有标题渲染器: IHeaderRenderer——公开列标题渲染器提供的常用方法。 用于绘制列标题的渲染器的基类。 一个标题渲染器,用于绘制Windows XP主题的列标题。 一个标题渲染器,绘制梯度列标题。 一个绘制平面列标题的标题渲染器。 下面的图片显示了内建的HeaderRenderers的作用: 你可以通过设置它的HeaderRenderer属性来指定一个表将使用的HeaderRenderer:Hide 复制Code
// get the table to use a FlatHeaderRenderer // to draw the column headers table.HeaderRenderer = new FlatHeaderRenderer();
创建一个定制的HeaderRenderer 如果你想创建一个自定义的HeaderRenderer,你有两个选择-子类HeaderRenderer和覆盖(至少)OnPaint和OnPaintBackground方法(最简单和首选的方法)或实现IHeaderRenderer(很多工作)。 下面是在XPHeaderRenderer中构建的表格代码:收缩,复制Code
public class XPHeaderRenderer : HeaderRenderer { protected override void OnPaintBackground(PaintHeaderEventArgs e) { base.OnPaintBackground(e); if (e.Column == null) { ThemeManager.DrawColumnHeader(e.Graphics, e.HeaderRect, ColumnHeaderStates.Normal); } else { ThemeManager.DrawColumnHeader(e.Graphics, e.HeaderRect, (ColumnHeaderStates) e.Column.ColumnState); } } protected override void OnPaint(PaintHeaderEventArgs e) { base.OnPaint(e); // don't bother if we don't have a column if (e.Column == null) { return; } Rectangle textRect = base.ClientRectangle; Rectangle imageRect = Rectangle.Empty; // check whether we can draw an image on the column header if (e.Column.Image != null) { imageRect = base.CalcImageRect(); textRect.Width -= imageRect.Width; textRect.X += imageRect.Width; if (e.Column.ImageOnRight) { imageRect.X = base.ClientRectangle.Right - imageRect.Width; textRect.X = base.ClientRectangle.X; } // column headers that aren't themed and are pressed need // their contents shifted down and to the right by 1 pixel if (!ThemeManager.VisualStylesEnabled && e.Column.ColumnState == ColumnState.Pressed) { imageRect.X += 1; imageRect.Y += 1; } base.DrawColumnHeaderImage(e.Graphics, e.Column.Image, imageRect, e.Column.Enabled); } // column headers that aren't themed and are pressed need // their contents shifted down and to the right by 1 pixel if (!ThemeManager.VisualStylesEnabled && e.Column.ColumnState == ColumnState.Pressed) { textRect.X += 1; textRect.Y += 1; } // check whether we need to draw a sort arrow if (e.Column.SortOrder != SortOrder.None) { // work out where to draw it Rectangle arrowRect = base.CalcSortArrowRect(); // adjust the textRect to take the arrow into account arrowRect.X = textRect.Right - arrowRect.Width; textRect.Width -= arrowRect.Width; base.DrawSortArrow(e.Graphics, arrowRect, e.Column.SortOrder, e.Column.Enabled); } // check whether we have any text to draw if (e.Column.Text == null) { return; } if (e.Column.Text.Length > 0 && textRect.Width > 0) { if (e.Column.Enabled) { e.Graphics.DrawString(e.Column.Text, base.Font, base.ForeBrush, textRect, base.StringFormat); } else { using (SolidBrush brush = new SolidBrush(SystemPens.GrayText.Color)) { e.Graphics.DrawString(e.Column.Text, base.Font, brush, textRect, base.StringFormat); } } } } }
编辑器 XPTable包含5个内置编辑器: 冰岛人-公开细胞编辑提供的常见方法。 单元格编辑器的基类。 用于编辑包含字符串的单元格的类。 用于编辑包含数字的单元格的类。 用于编辑包含下拉按钮的单元格的基类。 用于编辑看起来像ComboBoxCellEditor的单元格的类。 ColorCellEditor -用于编辑包含颜色的单元格的类。 用于编辑包含日期时间的单元格的类。 IEditorUsesRendererButtons—指定CellEditor在编辑时使用它的对手CellRenderer提供的按钮。 注意:有关IEditorUsesRendererButtons的更多信息,请参阅文档提供的用户指南。 下图显示了使用下拉控件编辑单元格内容的编辑器: 您可以通过使用表格的EditCell方法来编程编辑单元格:Hide复制Code
// start editing the cell at (0, 0) table.EditCell(0, 0); // stop editing the cell and commit any changes table.StopEditing(); // or cancel editing and ignore any changes table.CancelEditing();
注意:如果你想停止或取消编辑,总是使用表格的StopEditing或CancelEditing方法(即使在实现自定义CellEditor时也是如此)。这给了表在调用CellEditor的StopEditing或CancelEditing方法之前做它需要做的任何工作的机会。 创建一个定制的CellEditor 如果你想创建一个自定义的CellEditor,你有两个选择-子类CellEditor和覆盖(至少)SetEditValue, SetCellValue和SetEditLocation方法(最简单和首选的方法)或实现冰岛itor(很多工作)。 下面是在TextCellEditor中内置的表格代码:收缩,复制Code
public class TextCellEditor : CellEditor { public TextCellEditor() : base() { TextBox textbox = new TextBox(); textbox.AutoSize = false; textbox.BorderStyle = BorderStyle.None; base.Control = textbox; } // Sets the location and size of the CellEditor protected override void SetEditLocation(Rectangle cellRect) { this.TextBox.Location = cellRect.Location; this.TextBox.Size = new Size(cellRect.Width-1, cellRect.Height-1); } // Sets the initial value of the // editor based on the contents of // the Cell being edited protected override void SetEditValue() { this.TextBox.Text = base.EditingCell.Text; } // Sets the contents of the Cell // being edited based on the value // in the editor protected override void SetCellValue() { base.EditingCell.Text = this.TextBox.Text; } // Starts editing the Cell public override void StartEditing() { this.TextBox.KeyPress += new KeyPressEventHandler(OnKeyPress); this.TextBox.LostFocus += new EventHandler(OnLostFocus); base.StartEditing(); this.TextBox.Focus(); } // Stops editing the Cell and commits any changes public override void StopEditing() { this.TextBox.KeyPress -= new KeyPressEventHandler(OnKeyPress); this.TextBox.LostFocus -= new EventHandler(OnLostFocus); base.StopEditing(); } // Stops editing the Cell and ignores any changes public override void CancelEditing() { this.TextBox.KeyPress -= new KeyPressEventHandler(OnKeyPress); this.TextBox.LostFocus -= new EventHandler(OnLostFocus); base.CancelEditing(); } // Gets the TextBox used to edit the Cells contents public TextBox TextBox { get { return base.Control as TextBox; } } // Handler for the editors TextBox.KeyPress event protected virtual void OnKeyPress(object sender, KeyPressEventArgs e) { // check whether we nned to stop or cancel editing if (e.KeyChar == AsciiChars.CarriageReturn /*Enter*/) { if (base.EditingTable != null) { base.EditingTable.StopEditing(); } } else if (e.KeyChar == AsciiChars.Escape) { if (this.EditingTable != null) { base.EditingTable.CancelEditing(); } } } // Handler for the editors TextBox.LostFocus event protected virtual void OnLostFocus(object sender, EventArgs e) { // if the textbox loses focus // we should stop editing if (base.EditingTable != null) { base.EditingTable.StopEditing(); } } }
视觉风格 对于XPTable,视觉样式是可继承的——也就是说,行和单元格将使用其父容器的视觉设置(除非另有说明)。XPTable还提供了可以在行和单元格之间共享的样式对象,从而节省系统资源。下面的图片展示了一个例子: CellStyles 单元格有一个CellStyle属性,它允许您跨多个单元格提供一致的外观和感觉,同时节省系统资源。CellStyle对象提供了四个控制单元格外观的属性: BackColor -指定背景单元格的颜色。 前景色-指定单元格的前景色。 字体-指定单元格使用的字体。 CellPadding -指定单元格边框与其内容之间的间距。 注意:在单元格上设置其中一个值将覆盖从其父行继承的相同值。单元格还有使用CellStyle属性存储其值的背景色、前面板、字体和单元格填充属性。在与其他单元格共享其单元格样式的单元格上设置这些属性之一也将影响所有其他单元格。 RowStyles rowstyle和cellstyle是一样的,除了它们在行之间共享并且没有CellPadding属性。 表格样式 在这个版本中,表没有TableStyle属性(将来的版本会有)。相反,表有以下属性来控制它的外观: 指定表格的背景颜色。 前景色-指定表格的前景色。 字体-指定表格使用的字体。 AlternatingRowColor—指定表格的交替行背景颜色。 指定所选行和单元格的背景颜色。 指定所选行的前景色和单元格的前景色。 UnfocusedSelectionBackColor -指定表格没有焦点时所选行的背景颜色和单元格。 UnfocusedSelectionForeColor -指定表格没有焦点时所选行和单元格的前景色。 指定用于在列标题中绘制文本的字体。 指定网格线的颜色。 GridLineStyle—指定网格线的线条样式。 指定已排序列的背景的颜色。 注意:除非显式设置,否则行和单元格将继承这些值。 下面的例子展示了如何共享单元格样式和行样式:复制Code
// create a new CellStyle object CellStyle cellStyle = new CellStyle(); cellStyle.BackColor = Color.Blue; cellStyle.ForeColor = Color.Red; cellStyle.Font = new Font("Tahoma", 8.25f, FontStyle.Bold); // create a new RowStyle object RowStyle rowStyle = new RowStyle(); rowStyle.BackColor = Color.Yello; rowStyle.ForeColor = Color.Green; rowStyle.Font = new Font("Arial", 8.25f, FontStyle.Italics); for (int i=0; i<3; i++) { tableModel.Rows[i].RowStyle = rowStyle; // only set the cellstyle for cells in the 3rd column tableModel[i, 2].CellStyle = cellStyle; }
排序 对表进行排序是按列执行的,可以通过单击列的标题或通过代码启动。 有六个内置的比较器: 单元格比较器的基类。 TextComparer -用于基于文本属性比较单元格。 CheckBoxComparer—用于基于选中的属性比较单元格。 NumberComparer—用于比较Data属性中包含数字的单元格。 用于基于Image属性比较单元格。 ColorComparer—用于比较Data属性中包含颜色的单元格。 DateTimeComparer—用于比较Data属性中包含日期时间的单元格。 还有四个内置的分类器: InsertionSorter 归并排序 ShellSorter 堆排序 插入排序和归并排序被认为是稳定排序,而壳排序和堆排序是不稳定的。另外,在较小的链表上,InsertionSort和ShellSort比归并排序和堆排序快,而在大链表上则慢。用于对列进行排序的实际算法取决于表中的行数以及是否需要稳定的排序。 有关排序方法和稳定/不稳定排序的更多信息,请参考本网站。 通过调用表的排序方法之一,可以通过编程对列进行排序:Hide收缩,复制Code
// sort the currently sorted column in the opposite direction // to its currnent sort order, or if no columns are sorted, the // column that has focus in ascending order table.Sort(); // sort the currently sorted column in the opposite direction // to its currnent sort order, or if no columns are sorted, the // column that has focus in ascending order using an unstable // sort method table.Sort(false); // sort the column at index 3 in the table's ColumnModel // opposite to its current sort order, or in ascending order // if the column is not sorted table.Sort(3); // sort the column at index 3 in the table's ColumnModel // opposite to its current sort order, or in ascending order //if the column is not sorted using a stable sort method table.Sort(3, true); // sort the column at index 3 in the table's ColumnModel // in descending order table.Sort(3, SortOrder.Descending); // sort the column at index 3 in the table's ColumnModel // in ascending order using an unstable sort method table.Sort(3, SortOrder.Ascending, false);
注意:不提供指定稳定或不稳定排序选项的排序方法将自动使用稳定排序。 你可以通过将列的可排序属性设置为false来禁用列排序:Hide复制Code
// disable sorting for a column column.Sortable = false;
注意:设置表格的HeaderStyle属性为NonClickable或None将停止对列标题的点击,但是列仍然可以通过编程进行排序。 创建自定义比较器 也可以通过子类化ComparerBase并重写Compare方法来创建一个用于列的自定义比较器。收缩,复制Code
public class TextComparer : ComparerBase { // Compares two objects and returns a // value indicating whether one is less // than, equal to or greater than the other public override int Compare(object a, object b) { Cell cell1 = (Cell) a; Cell cell2 = (Cell) b; // check for null cells if (cell1 == null && cell2 == null) { return 0; } else if (cell1 == null) { return -1; } else if (cell2 == null) { return 1; } // check for null data if (cell1.Text == null && cell2.Text == null) { return 0; } else if (cell1.Text == null) { return -1; } // now that we know both cells contain valid data, // use the frameworks built in string comparer return cell1.Text.CompareTo(cell2.Text); } }
选择 表格提供了两种方式来显示选中的单元格——网格样式突出显示单个选中的单元格,或者ListView样式只突出显示第一个可见列中的单元格。下面的图片展示了一个例子: 列表视图样式选择 底部:网格样式选择 这可以用表格的SelectionStyle属性来设置:Hide复制Code
// use grid style selection table.SelectionStyle = SelectionStyle.Grid;
注意:在ListView样式选择中,突出显示的单元格实际上可能没有被选中。 TableModel还提供了一个选择对象,可以使用该对象以编程方式选择或取消选择单元格。 未来的功能 下面是我想添加到未来版本的功能列表: 单元格和列标题的换行 自动调整行和列的大小 变量高度行 LinkLabel细胞 RichTextFormat细胞 基于对话框的CellEditors ListView样式图标模式 RightToLeft支持 剪切粘贴支持 拖放支持 数据绑定 列重新排序 打印支持 导出到HTML和XML 序列化 还有一些我忘记或没有忘记的东西t(但 历史 2005年9月11日-首次发布。 2005年9月13日-版本1.0.1。 固定表在应用程序最小化时导致崩溃。 更新的未来功能列表。 2005年9月17日- 1.0.2版本 修正使用DropDownCellEditor导致崩溃时,下拉部分显示。 修正了从TableModel删除行时抛出的异常。 固定表格/行不更新行/单元格索引时,添加/删除行/单元格造成绘图问题。 修正隐藏选举的错误,选择的项目没有绘制作为选择,即使当表有焦点。 固定由单元渲染器设置的表覆盖游标。 为方便起见,添加了InvalidateCell和InvalidateRow实用方法到表中。 本文转载于:http://www.diyabc.com/frontweb/news250.html