
Contents
There are a few problems in displaying ToolTips for the contents of DataGrid cells:
- There is no obvious way to show
ToolTips for the contents of the DataGrid cells.
- When a
DataGrid is sorted, the ToolTips need to be associated with the correct cells after the sort.
- When a
DataGridTextBoxColumn cell is selected, the cell's events might not fire because the cell is covered by a DataGridTextBox.
- When a cell in a
DataGridBoolColumn is being modified, the modified value is difficult to obtain.
These problems affect DataGrids in both .NET 1.1 and 2.0. The 2.0 DataGridView makes it much easier to display ToolTips, but some developers have to continue using 1.1 for legacy reasons, or may not want to go through the trouble of porting their DataGrids to DataGridViews.
This article explains how to solve the above problems with ToolTips in DataGrid cells.
Note: In the examples below, some details have been removed or combined for clarity. See the source Zip for a more complete example.
In a DataGrid event handler, use DataGrid.HitTestInfo to get the target row and column in the DataGrid, and use a CurrencyManager to get the corresponding row and column in the data source. If sorting is allowed in a DataGrid, the row number returned by DataGrid.HitTestInfo may refer to a different row in the data source. For example, the third row displayed in the DataGrid may be the first row in the data source. HitTestInfo returns the index for the row as it appears in the DataGrid; the CurrencyManager maps that index to the data source's row. Once the correct source cell has been found, it's easy to set the ToolTip to the contents of that cell.
private void GridTest_MouseMove(object sender, MouseEventArgs e)
{
DataGrid.HitTestInfo hitInfo = gridTest.HitTest(new Point(e.X, e.Y));
CurrencyManager hitManager = (CurrencyManager)
this.BindingContext[gridTest.DataSource, gridTest.DataMember];
if (hitInfo.Row < hitManager.List.Count &&
hitInfo.Type == DataGrid.HitTestType.Cell &&
hitManager.List is DataView)
{
DataRowView view = ((DataView)hitManager.List)[hitInfo.Row];
string tipText =
view.Row.IsNull(hitInfo.Column) ? "(null)" :
view.Row[hitInfo.Column].ToString();
toolTipTest.SetToolTip(gridTest, tipText);
}
}
When a DataGridTextBoxColumn cell is selected, the cell's DataGridTextBox takes over. It covers so much of the cell that many of the DataGrid's events are unlikely to fire in that cell. To display a ToolTip in this situation, assign an event handler to each DataGridTextBox in the DataGrid and use that event handler to display the ToolTip.
private void InitializeControls()
{
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 (columnStyle != null)
{
columnStyle.TextBox.MouseMove +=
new MouseEventHandler(this.TextBox_MouseMove);
}
}
}
}
In the event handler, use the DataGridTextBox's text for the ToolTip:
private void TextBox_MouseMove(object sender, MouseEventArgs e)
{
DataGridTextBox hitBox = sender as DataGridTextBox;
if (hitBox != null)
{
string tipText = hitBox.Text == null ? "(null)" : hitBox.Text;
toolTipTest.SetToolTip(hitBox, tipText);
}
}
For DataGridBoolColumn cells, it is more difficult to keep the ToolTip in sync with the displayed check box while the cell is being edited. Although it looks like there is a CheckBox control in the cell, it's just paint. One workaround is to use the DataGrid's MouseUp event to switch to another cell in the same row. This results in a proposed DataRowVersion that can be used to get the information to be displayed in the ToolTip. Note that this approach works only when there is more than one column in the DataGrid. If your DataGrid has only one column, you need to try one of the alternative approaches.
private void GridTest_MouseUp(object sender, MouseEventArgs e)
{
DataGrid.HitTestInfo hitInfo = gridTest.HitTest(new Point(e.X, e.Y));
CurrencyManager hitManager = (CurrencyManager)
this.BindingContext[gridTest.DataSource, gridTest.DataMember];
if (hitInfo.Row < hitManager.List.Count &&
hitInfo.Type == DataGrid.HitTestType.Cell)
{
DataRowView view = ((DataView)hitManager.List)[hitInfo.Row];
if (view.Row.Table.Columns[hitInfo.Column].DataType == typeof(bool))
{
DataGridCell cell = gridTest.CurrentCell;
int columnChange =
view.Row.Table.Columns.Count == cell.ColumnNumber + 1 ? -1 : 1;
gridTest.CurrentCell =
new DataGridCell(cell.RowNumber, cell.ColumnNumber + columnChange);
gridTest.CurrentCell = cell;
if (view.Row.HasVersion(DataRowVersion.Proposed))
{
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);
}
}
}
}
Another approach is to derive a new DataGridColumnStyle for the target column(s). See MSDN for an example of this approach. The example's approach returns the current value of the underlying data source rather than the value corresponding to the displayed check box, so it takes some additional effort to make the edited value available for the ToolTip.
Yet another approach is to specify a DataGridTextBoxColumn style for bool columns, which results in the state being displayed as text rather than as a check box. This approach is not ideal because the user has to type "true" or "false" to change the value, but it does have the virtue of not requiring cell-switching to capture the change.
When DataGridColumnStyles have been explicitly defined, it makes sense to respect the NullText setting when displaying cell ToolTips. One way to do this is to create a method to get the display text and use it instead of displaying the hard-coded "(null)" in the GridTest_MouseMove and GridTest_MouseUp examples above.
private string GetNullStyleInfo(int hitColumn)
{
if (GridStyle.GridColumnStyles.Count > hitColumn)
{
DataGridColumnStyle hitStyle = GridStyle.GridColumnStyles[hitColumn];
return hitStyle.NullText;
}
return "(null)";
}
In the GridTest_MouseMove and GridTest_MouseUp methods above, replace the hard-coded "(null)" with a call to GetNullStyleInfo(hitColumn). There's no need to modify the TextBox_MouseMove method because it already displays the correct text (i.e., the DataGridTextBox Text already has the correct text, and the hard-coded "(null)" will likely never be used).
- 12-17-2006:
- Updated article to discuss column style null text.
- 11-25-2006:
- Improved code and updated article.
- 10-21-2005:
- Added support for various data types and column styles.
- 09-29-2005: