65.9K
CodeProject 正在变化。 阅读更多。
Home

XPTable - .NET ListView 遇上 Java 的 JTable

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (441投票s)

2005 年 9 月 11 日

12分钟阅读

viewsIcon

7082045

downloadIcon

55356

一个基于 Java 的 JTable 的完全可定制的 ListView 风格控件。

目录

引言

在我正在进行的一个项目中,我需要一个高度定制的 ListView —— 一个能够允许在任何列中包含复选框和图像,提供 ComboBoxNumericUpDown 用于编辑,并且能够轻松地进行数据传入和传出的控件。任何尝试过定制 ListView 的人都知道,要想把它按照自己的意愿进行修改是多么痛苦,所以我决定从头开始创建一个。我来自 Java 背景,所以决定在一定程度上松散地参考 Java 的 JTable

特点

  • 完全可定制的视觉外观——从列到行和单元格。
  • 支持 Windows XP 视觉样式。
  • 强大的渲染器,使单元格能够像控件一样工作。
  • 轻松添加您自己的自定义渲染器和编辑器。
  • 列可以被隐藏。
  • 行、列或单个单元格可以被禁用。
  • 列和单元格的 ToolTips (工具提示)。
  • 还有更多……

XPTable

XPTable 由以下组件组成:

  1. 一个 Table (表)
  2. 一个 ColumnModel (列模型) 及其 Columns (列)
  3. 一个 TableModel (表模型) 及其 Rows (行) 和 Cells (单元格)
  4. Renderers (渲染器) 和
  5. Editors (编辑器)

我不会详细介绍前三个方面,只展示第四和第五点的一些基本知识,否则本文会比现在更长。如果您想了解任何一个主题的更多细节,您应该阅读随文档提供的用户指南。

使用 XPTable

在使用 XPTable 之前,您需要在项目的“引用”部分添加对 XPTable.dll 的引用。

要将 XPTable.dll 添加到工具箱,您可以

  1. 从菜单中选择 工具 -> 添加/删除工具箱项,或
  2. 右键单击工具箱,选择 添加/删除项

然后浏览 XPTable.dll 并按 OK。然后您可以将控件拖到您的窗体上。

注意: 如果您重新编译源代码,您将需要重新签名 XPTable.dll,否则 Visual Studio 在您尝试将其添加到工具箱时可能会抛出异常。

  1. 打开 VS .NET 命令提示符,并将目录更改为指向 XPTable\bin\Release 目录。
  2. 然后输入 "sn -R XPTable.dll ..\..\XPTable.snk" (当然,不包括引号)。

之后,您应该能够将其添加到工具箱。

之后,您只需将 TableColumnModelTableModel 拖到您的窗体上,设置 TableColumnModelTableModel 属性,并在 ColumnModel 中添加 Columns (列),在 TableModel 中添加 Rows (行) 和 Cells (单元格)。

或者,如果您喜欢代码

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"));

表格

Table 是一个“简单”的对象,因为它实际上不包含或不知道如何绘制它将要显示的数据。相反,它使用 ColumnModel 来跟踪它的 Columns (列),使用 TableModel 来跟踪它的 Rows (行) 和 Cells (单元格),以及使用 Renderers (渲染器) 和 Editors (编辑器) 来绘制和编辑它的数据。Table 的主要作用是管理绘制操作,并将事件传递给 Renderers (渲染器) 和 Editors (编辑器),以便它们可以采取适当的操作。

ColumnModel (列模型)

ColumnModel 包含一个 Columns (列) 的集合,这些列将在 Table 中显示。它还跟踪是否为特定 Column 创建了 CellRenderer (单元格渲染器) 或 CellEditor (单元格编辑器)。

Columns

在仔细考虑了实现 Columns (列) 的最佳方法后,我决定采用与 DataGrid 相同的方法——即根据其 Cells (单元格) 将包含的数据类型来定义不同类型的 Columns (列)。以下 Column (列) 类型可用:

  • Column - 所有 Columns (列) 的基类。
  • TextColumn - 一个 Column (列),其 Cells (单元格) 显示为字符串。
  • ButtonColumn - 一个 Column (列),其 Cells (单元格) 显示为 Buttons (按钮)。
  • CheckBoxColumn - 一个 Column (列),其 Cells (单元格) 显示为 CheckBoxes (复选框)。
  • ImageColumn - 一个 Column (列),其 Cells (单元格) 显示为 Images (图像)。
  • NumberColumn - 一个 Column (列),其 Cells (单元格) 显示为数字。
  • ProgressBarColumn - 一个 Column (列),其 Cells (单元格) 显示为 ProgressBars (进度条)。
  • DropDownColumn - 用于显示下拉框进行编辑的 Columns (列) 的基类。
  • ComboBoxColumn - 表示一个 Column (列),其 Cells (单元格) 显示为 ComboBoxes (组合框)。
  • DateTimeColumn - 表示一个 Column (列),其 Cells (单元格) 包含 DateTimes (日期时间)。
  • ColorColumn - 表示一个 Column (列),其 Cells (单元格) 包含 Colors (颜色)。

TableModel (表模型)

TableModel 包含一个 Rows (行) 的集合,这些行将在 Table 中显示。

Rows

Row 表示 Table 中的一行,并包含一个 Cells (单元格) 的集合,这些单元格将在 Row 中显示。

格子

Cell 包含将在 Table 中显示的数据。

Renderers (渲染器)

如前所述,Table 不知道如何绘制 Cells (单元格) 或 Column (列) 的标题。相反,它使用称为 Renderers (渲染器) 的对象来完成所有绘制工作。Java 网站将渲染器描述为“一个表用于将适当格式化数据的可配置印章,盖在每个单元格上”。

Table 使用两种不同类型的 Renderers (渲染器):CellRenderers (单元格渲染器),它们绘制 Cells (单元格),以及 HeaderRenderers (表头渲染器),它们绘制 Column (列) 的标题。

CellRenderers (单元格渲染器)

CellRenderers (单元格渲染器) 是强大的对象,因为它们允许 Cells (单元格) 看起来和行为都像 Windows 控件,而不会消耗任何额外的资源。

下面的列表显示了 XPTable 提供的所有 CellRenderers (单元格渲染器):

  • ICellRenderer - 公开 Cell (单元格) 渲染器提供的通用方法。
  • CellRenderer - 所有 Cell (单元格) 渲染器的基类。
  • TextCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为字符串。
  • ButtonCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为 Buttons (按钮)。
  • CheckBoxCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为 CheckBoxes (复选框)。
  • ImageCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为 Images (图像)。
  • NumberCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为数字。
  • ProgressBarCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为 ProgressBar (进度条)。
  • DropDownCellRenderer - 用于绘制 Cell (单元格) 内容如同 ComboBoxes (组合框) 的 CellRenderers (单元格渲染器) 的基类。
  • ComboBoxCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为 ComboBox (组合框)。
  • ColorCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为 Colors (颜色)。
  • DateTimeCellRenderer - 一个 CellRenderer (单元格渲染器),它将 Cell (单元格) 内容绘制为 DateTime (日期时间)。

下图显示了每个 CellRenderer (单元格渲染器) 的默认输出。

创建自定义 CellRenderer (单元格渲染器)

如果您想创建自定义 CellRenderer (单元格渲染器),您有两个选择:继承 CellRenderer (单元格渲染器) 并重写 (至少) OnPaintOnPaintBackground 方法 (最简单且推荐的方法),或者实现 ICellRenderer (工作量很大)。

下面是 Table 内置 TextCellRenderer (文本单元格渲染器) 的代码。

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 (表头渲染器)

与用于每个列的 CellRenderers (单元格渲染器) 不同,Table 使用单个 HeaderRenderer (表头渲染器) 来绘制所有 Column (列) 的标题。

下面的列表显示了 XPTable 提供的所有 HeaderRenderers (表头渲染器):

  • IHeaderRenderer - 公开 Column (列) 标题渲染器提供的通用方法。
  • HeaderRenderer - 用于绘制 Column (列) 标题的 Renderers (渲染器) 的基类。
  • XPHeaderRenderer - 一个 HeaderRenderer (表头渲染器),它绘制 Windows XP 主题的 Column (列) 标题。
  • GradientHeaderRenderer - 一个 HeaderRenderer (表头渲染器),它绘制渐变色的 Column (列) 标题。
  • FlatHeaderRenderer - 一个 HeaderRenderer (表头渲染器),它绘制平坦的 Column (列) 标题。

下图展示了内置 HeaderRenderers (表头渲染器) 的实际效果。

您可以通过设置 TableHeaderRenderer 属性来指定 Table 将使用的 HeaderRenderer (表头渲染器)。

// get the table to use a FlatHeaderRenderer
// to draw the column headers
table.HeaderRenderer = new FlatHeaderRenderer();

创建自定义 HeaderRenderer (表头渲染器)

如果您想创建自定义 HeaderRenderer (表头渲染器),您有两个选择:继承 HeaderRenderer (表头渲染器) 并重写 (至少) OnPaintOnPaintBackground 方法 (最简单且推荐的方法),或者实现 IHeaderRenderer (工作量很大)。

下面是 Table 内置 XPHeaderRenderer (XP 表头渲染器) 的代码。

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);
            }
         }
      }
   }
}

Editors (编辑器)

XPTable 包含五种内置编辑器:

  • ICellEditor - 公开 Cell (单元格) 编辑器提供的通用方法。
  • CellEditor - Cell (单元格) 编辑器的基类。
  • TextCellEditor - 用于编辑包含字符串的 Cells (单元格) 的类。
  • NumberCellEditor - 用于编辑包含数字的 Cells (单元格) 的类。
  • DropDownCellEditor - 用于编辑包含下拉按钮的 Cells (单元格) 的基类。
  • ComboBoxCellEditor - 用于编辑看起来像 ComboBox (组合框) 的 Cells (单元格) 的类。
  • ColorCellEditor - 用于编辑包含 Colors (颜色) 的 Cells (单元格) 的类。
  • DateTimeCellEditor - 用于编辑包含 DateTimes (日期时间) 的 Cells (单元格) 的类。
  • IEditorUsesRendererButtons - 指示 CellEditor (单元格编辑器) 在编辑期间使用其对应的 CellRenderer (单元格渲染器) 提供的按钮。

注意: 有关 IEditorUsesRendererButtons 的更多信息,请参阅随文档提供的用户指南。

下图展示了使用下拉控件编辑 Cell (单元格) 内容的编辑器。

您可以使用 tableEditCell 方法以编程方式编辑 Cell (单元格)。

// 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();

注意: 如果您想停止或取消编辑,请始终使用 tableStopEditingCancelEditing 方法 (即使在实现自定义 CellEditor (单元格编辑器) 时也是如此)。这会给 table 一个机会来执行任何必要的预处理,然后再调用 CellEditor (单元格编辑器) 的 StopEditingCancelEditing 方法。

创建自定义 CellEditor (单元格编辑器)

如果您想创建自定义 CellEditor (单元格编辑器),您有两个选择:继承 CellEditor (单元格编辑器) 并重写 (至少) SetEditValueSetCellValueSetEditLocation 方法 (最简单且推荐的方法),或者实现 ICellEditor (工作量很大)。

下面是 Table 内置 TextCellEditor (文本单元格编辑器) 的代码:

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 中,视觉样式是可继承的——也就是说,Rows (行) 和 Cells (单元格) 将使用其父容器的视觉设置 (除非另有规定)。XPTable 还提供了可以由 Rows (行) 和 Cells (单元格) 共享的样式对象,从而节省系统资源。下图展示了一个此类示例。

CellStyles (单元格样式)

Cells (单元格) 有一个 CellStyle (单元格样式) 属性,该属性允许您在节省系统资源的同时,为多个 Cells (单元格) 提供一致的外观和感觉。CellStyle (单元格样式) 对象提供了四个属性来控制 Cell (单元格) 的外观:

  • BackColor (背景色) - 指定 Cell (单元格) 的背景颜色。
  • ForeColor (前景颜色) - 指定 Cell (单元格) 的前景色。
  • Font (字体) - 指定 Cell (单元格) 使用的字体。
  • CellPadding (单元格内边距) - 指定 Cell (单元格) 边框与其内容之间的空间量。

注意:Cell (单元格) 上设置这些值中的任何一个都将覆盖从其父 Row (行) 继承的相同值。Cells (单元格) 还有 BackColor (背景色)、ForeColor (前景颜色)、Font (字体) 和 CellPadding (单元格内边距) 属性,它们使用 CellStyle (单元格样式) 属性来存储其值。在与其它 Cells (单元格) 共享其 CellStyle (单元格样式) 的 Cell (单元格) 上设置这些属性中的任何一个都会影响所有其他 Cells (单元格)。

RowStyles (行样式)

RowStyles (行样式) 与 CellStyles (单元格样式) 相同,只是它们由 Rows (行) 共享,并且没有 CellPadding (单元格内边距) 属性。

Table styles (表样式)

在此版本中,Tables (表) 没有 TableStyle (表样式) 属性 (尽管未来版本会有)。相反,Table (表) 具有以下属性来控制其外观:

  • BackColor (背景色) - 指定 Table (表) 的背景颜色。
  • ForeColor (前景颜色) - 指定 Table (表) 的前景色。
  • Font (字体) - 指定 Table (表) 使用的字体。
  • AlternatingRowColor (交替行颜色) - 指定 Table (表) 的交替行背景色。
  • SelectionBackColor (选择背景色) - 指定所选 Rows (行) 和 Cells (单元格) 的背景颜色。
  • SelectionForeColor (选择前景色) - 指定所选 Rows (行) 和 Cells (单元格) 的前景色。
  • UnfocusedSelectionBackColor (非焦点选择背景色) - 指定当 Table (表) 没有焦点时,所选 Rows (行) 和 Cells (单元格) 的背景颜色。
  • UnfocusedSelectionForeColor (非焦点选择前景色) - 指定当 Table (表) 没有焦点时,所选 Rows (行) 和 Cells (单元格) 的前景色。
  • HeaderFont (表头字体) - 指定用于绘制 Column (列) 标题中文本的字体。
  • GridColor (网格颜色) - 指定网格线的颜色。
  • GridLineStyle (网格线样式) - 指定网格线的样式。
  • SortedColumnBackColor (已排序列背景色) - 指定已排序 Column (列) 的背景颜色。

注意: Rows (行) 和 Cells (单元格) 将继承这些值,除非被显式设置。

下面的示例展示了如何共享 CellStyles (单元格样式) 和 RowStyles (行样式)。

// 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;
}

排序

表排序是按列进行的,可以通过单击 Column (列) 的标题或通过代码来启动。

有六种内置的比较器:

  • ComparerBase - Cell (单元格) 比较器的基类。
  • TextComparer - 用于基于 Text 属性比较 Cells (单元格)。
  • CheckBoxComparer - 用于基于 Checked 属性比较 Cells (单元格)。
  • NumberComparer - 用于比较 Data 属性中包含数字的 Cells (单元格)。
  • ImageComparer - 用于基于 Image 属性比较 Cells (单元格)。
  • ColorComparer - 用于比较 Data 属性中包含 Colors (颜色) 的 Cells (单元格)。
  • DateTimeComparer - 用于比较 Data 属性中包含 DateTimes (日期时间) 的 Cells (单元格)。

还有四种内置的排序器:

  • InsertionSorter (插入排序器)
  • MergeSorter (归并排序器)
  • ShellSorter (希尔排序器)
  • HeapSorter (堆排序器)

InsertionSort 和 MergeSort 被认为是稳定的排序,而 ShellSort 和 HeapSort 是不稳定的。此外,InsertionSort 和 ShellSort 在小列表上比 MergeSort 和 HeapSort 快,在大列表上则慢。实际用于排序 Column (列) 的算法取决于 Table (表) 中的 Row (行) 数量以及是否需要稳定排序。

有关排序方法和稳定/不稳定排序的更多信息,请参阅 此网站

您可以通过调用 tableSort 方法之一来以编程方式排序 Column (列)。

// 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);

注意: 不提供稳定或不稳定排序选项的 Sort 方法会自动使用稳定排序。

您可以将 Column (列) 的 Sortable 属性设置为 false 来禁用 Column (列) 排序。

// disable sorting for a column
column.Sortable = false;

注意:Table (表) 的 HeaderStyle (表头样式) 属性设置为 NonClickable (不可点击) 或 None (无) 将阻止通过单击列标题进行列排序,但 Column (列) 仍然可以以编程方式排序。

创建自定义比较器

还可以通过继承 ComparerBase (比较器基类) 并重写 Compare 方法来为 Column (列) 创建自定义比较器。

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);
   }
}

选择

Table 提供了两种可视化选定 Cells (单元格) 的方式:Grid (网格) 样式,其中突出显示单个选定的 Cells (单元格);或 ListView (列表视图) 样式,其中只突出显示第一个可见 Column (列) 中的 Cell (单元格)。下图展示了一个示例。

顶部:ListView (列表视图) 样式选择

底部:Grid (网格) 样式选择

可以使用 tableSelectionStyle (选择样式) 属性来设置此选项。

// use grid style selection
table.SelectionStyle = SelectionStyle.Grid;

注意:ListView (列表视图) 样式选择中,突出显示的 Cell (单元格) 可能实际上并未被选中。

TableModel (表模型) 还提供了一个 Selection (选择) 对象,您可以使用它来以编程方式选择或取消选择 Cells (单元格)。

未来功能

下面是我想在未来版本中添加的功能列表:

  • 单元格和列标题的自动换行
  • 自动调整行高和列宽
  • 可变行高
  • LinkLabel (链接标签) 单元格
  • RichTextFormat (富文本格式) 单元格
  • 基于对话框的 CellEditors (单元格编辑器)
  • ListView (列表视图) 风格的图标模式
  • RightToLeft (从右到左) 支持
  • 剪切和粘贴支持
  • 拖放支持
  • 数据绑定
  • 列重排序
  • 打印支持
  • 导出到 HTML 和 XML
  • 序列化
  • 其他我忘记或还没想到的东西

历史

  • 2005 年 9 月 11 日 - 初始发布。
  • 2005 年 9 月 13 日 - 版本 1.0.1。
    • 修复了 Table (表) 在应用程序最小化时导致崩溃的问题。
    • 更新了未来功能列表。
  • 2005 年 9 月 17 日 - 版本 1.0.2
    • 修复了使用 DropDownCellEditor (下拉单元格编辑器) 在显示下拉部分时导致崩溃的问题。
    • 修复了删除 Rows (行) 时从 TableModel (表模型) 抛出的异常。
    • 修复了 TableModels (表模型)/Rows (行) 在添加/删除 Row/Cells (行/单元格) 时未更新 Row/Cell (行/单元格) 索引,导致绘图问题。
    • 修复了 HideSelection (隐藏选择) 错误,即即使 Table (表) 有焦点,选定的项目也不会显示为选中状态。
    • 修复了 Table (表) 覆盖 CellRenderer (单元格渲染器) 设置的光标。
    • 为方便起见,向 Table (表) 添加了实用方法 InvalidateCell (使单元格无效) 和 InvalidateRow (使行无效)。
XPTable - .NET ListView 遇上 Java 的 JTable - CodeProject - 代码之家
© . All rights reserved.