ToolTipsFactory

Task-based Tutorial - How to use ToolTips with grid-controls

This article discusses how the ToolTipsFactory tooltips can be used with generic grid-controls. Of course, the subject of this article is not about, how static tooltip-content can be assigned to a grid-control itself. This is possible for all controls that derive from the System.Windows.Forms.Control-class and has been extensively described in other sections of the ToolTipsFactory documentation. The main goal of this article is to describe, how the ToolTipsFactory tooltips can be used with grid-controls, in order to dynamically display additional information (text, images etc.) for individual elements of the grid-control (rows, columns, cells). 

A generic grid-control is a Windows Forms control that divides its client area into individually addressable rows, columns and cells, which can be filled with data. In this article we are going to discuss a generic approach on how to integrate ToolTipsFactory tooltips with grid-controls, because there are many different commercial and free grid-components available, where each one provides different means and granularity to address the various elements of the grid. Also the number and type of events fired by the different grid-controls varies significantly.

Some grid-controls make the integration of ToolTipsFactory tooltips a breeze, while others may require some more code, depending on the functions they provide. But in general, the generic approach described here, to integrate ToolTipsFactory tooltips with grid-controls, can be applied to almost all available grid-controls. Only some steps, which are usually very grid-specific, need to be adapted accordingly.

 

Prerequisites 

A grid-control must meet two requirements in order to allow deep integration with the ToolTipsFactory tooltips. (Deep integration means the ability to dynamically change the tooltip-content for each UI-element of the grid-control, such as rows, columns and cells.)

      1. The grid-control must at least fire MouseEnter-, MouseLeave- and MouseMove-events (or something functionally equivalent).
      2. The grid-control must provide some means to translate the mouse-position into addressable UI-elements of the grid (row, column, cell).

Point 1 is probably self-explaining and usually not an issue because most controls provide this events anyway. The second point maybe needs some further explanation.

Grid-controls are usually able to hold huge amounts of data, which can be scrolled, because the size of the grid only allows to present a limited number of rows and/or columns. Therefore, the client-area of the grid-control is more a kind of a viewport or window to look at the data behind. The mouse-pointer, which is the main instrument to select or point to the information in the grid, is only able to navigate on the surface of this viewport. If the application that hosts the grid-control wants to know over which cell (or row, or column etc.) the mouse-pointer is hovering, it has no means to determine this based only on the mouse-position. Only the grid-control, which manages the layout and scrolling of the data, can provide this information.

Most grid-controls (that deserve this name) - but not all - provide some means to get this kind of information. Only the knowledge of the current mouse-position in terms of UI-elements of the grid-control, opens the possibility to display different tooltip-content for each grid-element. 

 

Integration 

In the following paragraphs we are going to analyze the integration of a ToolTipsFactory tooltip with a generic grid-control. We will do this with the help of descriptive pseudo-code. Working examples with real code are provided in two additional sections, where ToolTipsFactory tooltips are integrated with Microsoft's DataGrid and Infragistics' UltraWinGrid

For this discussion, we assume that a grid-control (Grid) and one of the ToolTipsFactory tooltip components (ToolTip) have been placed or added to a form (Form1). The sole purpose of this (pseudo-) implementation is to display the content of the cell, over which the mouse-pointer is hovering, in the tooltip. (This can be useful, if - for instance - the complete content of a cell, which maybe gets truncated because of a small column-width, should be displayed by the tooltip.)

To achieve this, we have to constantly monitor the movement of the mouse inside of the grid-control, because this is the only way to dynamically change the tooltip content as soon as the mouse reaches another grid-cell. Of course, to be able to react to any change of the mouse-position, we are going to rely on the MouseMove-event fired whenever the mouse is moved on the grid-control client-area

However, It would not be a good idea, to evaluate the cell, over which the mouse is hovering, with every MouseMove-event and then assign the content of this grid-cell to the tooltip. This approach could work, but it would be really bad in terms of performance. Actually, the content of the cell has to be assigned to the tooltip only once, as soon as the mouse enters the cell. Because this is not directly possible (there is nothing like a CellEnter-event available) we have to code the means to make sure that the MouseMove-event only initializes the tooltip once, when the cell is entered. 

This can only be accomplished,  if we keep track of the grid-coordinates (row/column) during the MouseMove-event and only do something, if the current cell changes. 

To keep track of the current grid-cell (not the selected one, but the one over which the mouse is hovering!) we need a variable at the Form-level. This variable is best declared as a Point-structure, because most grid-controls address rows and columns (and therefore also cells) through indices, which can conveniently be stored in a Point-structure:

Public Class Form1

    Inherits System.Windows.Forms.Form

    

    'Holds the row/column-coordinates of the current cell...

    Private mCurrentCell As Point = New Point(-1,-1)

    ...

Because we always need to know, over which cell the mouse is located, we first have to take care of the first contact of the mouse-pointer with the grid-control. In other words, we have to handle the MouseEnter-event of the grid-control and instantly record the position. And, if the mouse has entered the grid and ended on a grid-cell, we also need to pass along the content of the current cell to the tooltip for the first time.

Private Sub Grid_MouseEnter(sender As Object, e As EventArgs) _
        Handles
Grid.MouseEnter

    'Translate the current MousePosition into grid-coordinates (row/column)...

     mCurrentCell = GetCellCoordinates(Grid.MousePosition)

    'If the mouse hits a grid-cell while entering the grid...

    If mCurrentCell.X > -1 And mCurrentCell.Y > -1 Then

        '...we can initiate the display of the cell-content in the tooltip.

       Me.ToolTip.Start(Grid, Grid.Cell(mCurrentCell).Content)

    End If

End Sub

 

To always keep things in a defined state, we should also handle the MouseLeave-event and reset the mCurrentCell-position to its default-position, when the mouse is not pointing at an actual grid-cell:

Private Sub Grid_MouseLeave(sender As Object, e As System.EventArgs) _

         Handles Grid.MouseLeave

    'As soon as the mouse leaves the grid, it can't be 

    'pointing to a grid-cell.

    'Therefore we switch it "off"

     mCurrentCell.X = -1

     mCurrentCell.Y = -1

End Sub

Now, all that's missing is the code to ensure that - whenever the mouse reaches another cell - the content of the new cell is assigned to the tooltip. All this happens in the MouseMove-event:

Private Sub Grid_MouseMove(sender As Object,e As MouseEventArgs) _

        Handles Grid.MouseMove

    

    'Translate the current MousePosition into grid-coordinates (row/column)...

    Dim newCellPos As Point

    newCellPos = GetCellCoordinates(e.X, e.Y)

 

    'If the mouse did not move on to another cell, the content to

    'be displayed in the tooltip is still the same and no further action

    'is required.

    If newCellPos.X = mCurrentCell.X _

        AND newCellPos.Y = mCurrentCell.Y Then Exit Sub

 

    'The cursor crossed the border to another cell since the

    'last MouseMove-event. Therefore the new cell becomes the

    'current cell.

    mCurrentCel = newCellPos

    

    'If the cursor points to no cell in the grid (free space)...

    If mCurrentCell.X = -1 And mCurrentCell.Y = -1 Then

        '..any previously displayed tooltip has to be switched off.

        Me.ToolTip.Reset(Grid, True)

    Else

        'The cursor points to a normal grid-cell. Therefore

        'we can initiate the display of the cell-content in the tooltip.

        Me.ToolTip.Start(Grid, Grid.Cells(mCurrentCell).Content)

    End If

End Sub

 

By adapting the pseudo-code from above, to integrate the ToolTipsFactory tooltip components with a specific grid-component, you are quickly up to speed, in order to create applications with nice features as shown in the movie-clip below (although the quality of the clip is poor compared to the real tooltips): 

 

We chosed this rather abstract way to discuss the integration of ToolTipsFactory tooltips with grid-controls, in order to provide you with the background necessary to successfully integrate the tooltips with any kind of grid-control. The points discussed in this section maybe also turn out to be helpful, if you try to use the tooltips with other complex controls. 

However, grid-controls are widely used in all kinds of applications and therefore well documented, working examples are quite important. Because of this, we provide two more articles (with real code!), where the integration of the ToolTipsFactory components with two popular grid-controls is discussed. The integration with the Microsoft DataGrid-control is described in "Microsoft's DataGrid", while the description related to the Infragistics UltraWinGrid can be found in "Infragistics' UltraWinGrid".