Contents
There are a few problems in displaying ToolTip
s for the contents of DataGrid
cells:
- There is no obvious way to show
ToolTip
s for the contents of the DataGrid
cells.
- When a
DataGrid
is sorted, the ToolTip
s 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 DataGrid
s in both .NET 1.1 and 2.0. The 2.0 DataGridView
makes it much easier to display ToolTip
s, but some developers have to continue using 1.1 for legacy reasons, or may not want to go through the trouble of porting their DataGrid
s to DataGridView
s.
This article explains how to solve the above problems with ToolTip
s 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: