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

带良好工具提示的DataGrid

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (8投票s)

2005年9月29日

4分钟阅读

viewsIcon

91392

downloadIcon

1502

一个 DataGrid,可在排序后在每个单元格上显示正确的 ToolTip,并在单元格正在编辑时仍显示 ToolTip。

Screenshot showing sorted columns and tooltip on edited cell

目录

引言

在显示 DataGrid 单元格内容时,在显示 ToolTip 方面存在一些问题

  • 没有明显的方法可以显示 DataGrid 单元格内容的 ToolTip
  • DataGrid 被排序时,ToolTip 需要在排序后与正确的单元格相关联。
  • 当选择一个 DataGridTextBoxColumn 单元格时,该单元格的事件可能不会触发,因为该单元格被 DataGridTextBox 覆盖。
  • DataGridBoolColumn 中的单元格正在修改时,很难获取修改后的值。

这些问题会影响 .NET 1.1 和 2.0 中的 DataGrid。2.0 的 DataGridView 使得显示 ToolTip 变得容易得多,但一些开发人员出于历史原因必须继续使用 1.1,或者可能不想费力地将他们的 DataGrid 移植到 DataGridView

本文介绍如何使用 DataGrid 单元格中的 ToolTip 来解决上述问题。

注意:在下面的示例中,为了清晰起见,已删除或合并了一些详细信息。请参阅源 Zip 文件以获取更完整的示例。

显示正确行的 ToolTip

DataGrid 事件处理程序中,使用 DataGrid.HitTestInfo 获取 DataGrid 中的目标行和列,并使用 CurrencyManager 获取数据源中相应的行和列。如果 DataGrid 中允许排序,DataGrid.HitTestInfo 返回的行号可能指向数据源中的不同行。例如,DataGrid 中显示的第三行可能是数据源中的第一行。HitTestInfo 返回该行在 DataGrid 中显示的索引;CurrencyManager 将该索引映射到数据源的行。一旦找到正确的源单元格,就可以轻松地将 ToolTip 设置为该单元格的内容。

private void GridTest_MouseMove(object sender, MouseEventArgs e)
{
    // Determine which cell to inspect.

    DataGrid.HitTestInfo hitInfo = gridTest.HitTest(new Point(e.X, e.Y));
    CurrencyManager hitManager = (CurrencyManager)
      this.BindingContext[gridTest.DataSource, gridTest.DataMember];
        
    // If current target is valid cell, display contents in ToolTip.
    
    if (hitInfo.Row < hitManager.List.Count &&
        hitInfo.Type == DataGrid.HitTestType.Cell &&
        hitManager.List is DataView)
    {
        // Use CurrencyManager to get DataRowView corresponding to data 
        // source's row.
        
        DataRowView view = ((DataView)hitManager.List)[hitInfo.Row];
        
        // Now that row is available, set ToolTip to cell's contents or, 
        // if cell is empty, to null indicator.
        
        string tipText = 
            view.Row.IsNull(hitInfo.Column) ? "(null)" : 
                     view.Row[hitInfo.Column].ToString();
        toolTipTest.SetToolTip(gridTest, tipText);
    }
}

显示选定的 DataGridTextBoxColumn 单元格的 ToolTip

当选择一个 DataGridTextBoxColumn 单元格时,该单元格的 DataGridTextBox 会接管。它覆盖了单元格的很大一部分,以至于该单元格中的许多 DataGrid 事件都不太可能触发。要在此情况下显示 ToolTip,请为 DataGrid 中的每个 DataGridTextBox 分配一个事件处理程序,并使用该事件处理程序显示 ToolTip

private void InitializeControls()  
{
    // Assign MouseMove event handlers to all DataGridTextBoxes in DataGrid
    
    for (int tableIndex = 0; tableIndex < gridTest.TableStyles.Count; 
                             tableIndex++) 
    {  
        DataGridTableStyle tableStyle = gridTest.TableStyles[tableIndex]; 
    
        for (int columnIndex = 0; columnIndex < 
                 tableStyle.GridColumnStyles.Count; columnIndex++)
        {
            DataGridTextBoxColumn columnStyle = 
                tableStyle.GridColumnStyles[columnIndex] as DataGridTextBoxColumn; 
        
            // If the column is a DataGridTextBoxColumn (i.e. not null), 
            // assign MouseMove event handler to its TextBox.
        
            if (columnStyle != null) 
            { 
                columnStyle.TextBox.MouseMove += 
                    new MouseEventHandler(this.TextBox_MouseMove);
            }
        } 
    }
}

在事件处理程序中,使用 DataGridTextBox 的文本作为 ToolTip

private void TextBox_MouseMove(object sender, MouseEventArgs e)
{
    // Get textbox.
    
    DataGridTextBox hitBox = sender as DataGridTextBox;
    
    if (hitBox != null)
    {
        // Set ToolTip to textbox's text or, if textbox is empty, to 
        // null indicator.
    
        string tipText = hitBox.Text == null ? "(null)" : hitBox.Text;
        toolTipTest.SetToolTip(hitBox, tipText);
    }
}

显示正在编辑的 DataGridBoolColumn 单元格的 ToolTip

对于 DataGridBoolColumn 单元格,在单元格正在编辑时,让 ToolTip 与显示的复选框保持同步更加困难。尽管单元格中看起来有一个 CheckBox 控件,但这只是绘制的。一种解决方法是使用 DataGridMouseUp 事件切换到同一行中的另一个单元格。这将产生一个建议的 DataRowVersion,可用于获取要在 ToolTip 中显示的信息。请注意,此方法仅在 DataGrid 中有多列时才有效。如果您的 DataGrid 只有一列,则需要尝试 替代方法之一。

private void GridTest_MouseUp(object sender, MouseEventArgs e)
{
    // Get info to determine which cell to inspect.
    
    DataGrid.HitTestInfo hitInfo = gridTest.HitTest(new Point(e.X, e.Y));
    CurrencyManager hitManager = (CurrencyManager)
       this.BindingContext[gridTest.DataSource, gridTest.DataMember];
    
    // If current target is valid cell, display contents in ToolTip. 
    
    if (hitInfo.Row < hitManager.List.Count &&
        hitInfo.Type == DataGrid.HitTestType.Cell)
    {
        // Use CurrencyManager to get DataRowView corresponding to data 
        // source's row.
        
        DataRowView view = ((DataView)hitManager.List)[hitInfo.Row];
        
        if (view.Row.Table.Columns[hitInfo.Column].DataType == typeof(bool))
        {
            // Current target is in bool column. Keep track of current cell.

            DataGridCell cell = gridTest.CurrentCell;

            // Set direction of column change, depending on whether current 
            // column is last one in row.

            int columnChange = 
                view.Row.Table.Columns.Count == cell.ColumnNumber + 1 ? -1 : 1;

            // Move to another column to create proposed DataRowVersion, then back
            // again so that user is still in same cell.

            gridTest.CurrentCell = 
                new DataGridCell(cell.RowNumber, cell.ColumnNumber + columnChange);
            gridTest.CurrentCell = cell;

            // If move caused proposed version to be created, get cell information 
            // from proposed version.

            if (view.Row.HasVersion(DataRowVersion.Proposed)) 
            {
                // Set ToolTip to proposed version or, if proposed version is 
                // empty, to null indicator.
                
                string tipText = 
                    view.Row.IsNull(view.Row.Table.Columns[hitInfo.Column], 
                    DataRowVersion.Proposed) ? "(null)" : 
                    view.Row[hitInfo.Column, DataRowVersion.Proposed].ToString();
                toolTipTest.SetToolTip(gridTest, tipText);
            }
        }
    }
}

DataGridBoolColumn 问题的其他方法

另一种方法是为目标列派生一个新的 DataGridColumnStyle。有关此方法的示例,请参阅 MSDN。该示例的方法返回底层数据源的当前值,而不是与显示的复选框相对应的值,因此需要额外的努力才能使编辑后的值可用于 ToolTip

还有一种方法是为 bool 列指定 DataGridTextBoxColumn 样式,这将导致状态显示为文本而不是复选框。这种方法并不理想,因为用户必须输入“true”或“false”才能更改值,但它确实有不要求切换单元格即可捕获更改的优点。

尊重 DataGridColumnStyle 的 NullText

当明确定义了 DataGridColumnStyles 时,在显示单元格 ToolTips 时,尊重 NullText 设置是有意义的。一种方法是创建一个方法来获取显示文本,并使用它而不是显示硬编码的“ (null)”在上面的 GridTest_MouseMoveGridTest_MouseUp 示例中。

private string GetNullStyleInfo(int hitColumn)
{
    // Assume for the sake of this example that the DataGridTableStyle is
    // GridStyle. 
    
    if (GridStyle.GridColumnStyles.Count > hitColumn)
    {
        // The column has a DataGridColumnStyle. Return the NullText string.
        
        DataGridColumnStyle hitStyle = GridStyle.GridColumnStyles[hitColumn];
        return hitStyle.NullText;
    }
    
    // No DataGridColumnStyle was found. Return a default null text string.
    
    return "(null)";
}

在上面的 GridTest_MouseMoveGridTest_MouseUp 方法中,用对 GetNullStyleInfo(hitColumn) 的调用替换硬编码的“ (null)”。无需修改 TextBox_MouseMove 方法,因为它已经显示了正确的文本(即,DataGridTextBoxText 已经具有正确的文本,并且硬编码的“ (null)”可能永远不会被使用)。

历史

  • 12-17-2006:
    • 更新了文章,讨论了列样式的 null 文本。
  • 11-25-2006:
    • 改进了代码并更新了文章。
  • 10-21-2005:
    • 增加了对各种数据类型和列样式的支持。
  • 09-29-2005:
    • 初始版本。
© . All rights reserved.