Trackers: Implementing Trackers in Your OLE Application

OverviewHow Do ISampleTutorial

Trackers provide a graphical interface to enable users to interact with OLE client items. By using different tracker styles, OLE client items can be displayed with hatched borders, resize handles, or a variety of other visual effects. This article describes:

  • Tracking and how to implement it in your code.

  • The rubber-band effect and trackers.

The article also covers the use of styles with trackers.

In addition, it makes several references to the MFC OLE sample .

How to Implement Tracking in Your Code

OverviewHow Do ISampleTutorial

To track an OLE item, you must handle certain events related to the item, such as clicking the item or updating the view of the document. In all cases, it is sufficient to declare a temporary object and manipulate the item by means of this object.

When a user selects an item or inserts an object with a menu command, you must initialize the tracker with the proper styles to represent the state of the OLE item. The following table outlines the conventions used by the OCLIENT sample. For more information on these styles, see CRectTracker.

Container Styles and States of the OLE Item

Style displayed State of OLE item
Dotted border Item is linked
Solid border Item is embedded in your document
Resize handles Item is currently selected
Hatched border Item is currently in-place active
Hatching pattern overlays item Item’s server is open

You can handle this initialization easily using a procedure that checks the state of the OLE item and sets the appropriate styles. The SetupTracker function found in the OCLIENT sample demonstrates tracker initialization. The parameters for this function are the address of the tracker, pTracker; a pointer to the client item that is related to the tracker, pItem; and a pointer to a rectangle, pTrueRect. For a more complete example of this function, see the MFC OLE sample .

The SetupTracker code example below presents a single function; lines of the function are interspersed with discussion of the function’s features:

void CMainView::SetupTracker( CRectTracker* pTracker,
  CRectItem* pItem, CRect* pTrueRect )
{

The tracker is initialized by setting the minimum size and clearing the style of the tracker.

  pTracker->m_sizeMin.cx = 8;
  pTracker->m_sizeMin.cy = 8;

  pTracker->m_nStyle = 0;

The following lines check to see whether the item is currently selected and whether the item is linked to the document or embedded in it. Resize handles located on the inside of the border are added to the style, indicating that the item is currently selected. If the item is linked to your document, the dotted border style is used. A solid border is used if the item is embedded.

  if ( pItem == m_pSelection )
    pTracker->m_nStyle |= CRectTracker::resizeInside;

  if ( pItem->GetType( ) == OT_LINK )
    pTracker->m_nStyle |= CRectTracker::dottedLine;
  else
    pTracker->m_nStyle |= CRectTracker::solidLine;

The following code overlays the item with a hatched pattern if the item is currently open.

  if ( pItem->GetItemState( ) ==
           COleClientItem::openState ||
       pItem->GetItemState( ) ==
           COleClientItem::activeUIState )
    pTracker->m_nStyle |= CRectTracker::hatchInside;
}

You can then call this function whenever the tracker has to be displayed. For example, call this function from the OnDraw function of your view class. This updates the tracker’s appearance whenever the view is repainted. For a complete example, see the CMainView::OnDraw function of the MFC OLE sample .

Events will occur in your application that require tracker code, such as resizing, moving, or hit detecting. These actions usually indicate that an attempt is being made to grab or move the item. In these cases, you will need to decide what was grabbed: a resize handle or a portion of the border between resize handles. The OnLButtonDown message handler is a good place to test the position of the mouse in relation to the item. Make a call to CRectTracker::HitTest. If the test returns something besides CRectTracker::hitOutside, the item is being resized or moved. Therefore, you should make a call to the Track member function. See the CMainView::OnLButtonDown function located in the MFC OLE sample for a complete example.

The CRectTracker class provides several different cursor shapes used to indicate whether a move, resize, or drag operation is taking place. To handle this event, check to see whether the item currently under the mouse is selected. If it is, make a call to CRectTracker::SetCursor, or call the default handler. The following example is from the MFC OLE sample :

BOOL CMainView::OnSetCursor( CWnd* pWnd, UINT nHitTest,
  UINT message )
{
  if ( pWnd == this && m_pSelection != NULL )
  {
    // give the tracker for the selection a chance
    CRectTracker tracker;
    SetupTracker( &tracker, m_pSelection );
    if ( tracker.SetCursor( this, nHitTest ))
      return TRUE;
  }
  return CScrollView::OnSetCursor( pWnd,
    nHitTest, message );
}

Rubber-Banding and Trackers

OverviewHow Do ISampleTutorial

Another feature supplied with trackers is the “rubber-band” selection, which allows a user to select multiple OLE items by dragging a sizing rectangle around the items to be selected. When the user releases the left mouse button, items within the region selected by the user are selected and can be manipulated by the user. For instance, the user might drag the selection into another container application.

Implementing this feature requires some additional code in your application’s WM_LBUTTONDOWN handler function.

The following code sample implements rubber-band selection and additional features and is taken from the WM_LBUTTONDOWN handler function of MFC General sample .

if (pDoc->m_tracker.HitTest(point) < 0)
{
  // just to demonstrate CRectTracker::TrackRubberBand
  CRectTracker tracker;
  if (tracker.TrackRubberBand(this, point,
    pDoc->m_bAllowInvert))
  {
    MessageBeep(0); // beep indicates TRUE

    // see if rubber band intersects
    // with the doc's tracker
    CRect rectT;
    // so intersect rect works
    tracker.m_rect.NormalizeRect();
    if (rectT.IntersectRect(tracker.m_rect,
      pDoc->m_tracker.m_rect))
    {
    // if so, put resize handles on it
    // (i.e. select it)
      if (pDoc->m_tracker.m_nStyle &
        CRectTracker::resizeInside)
      {
      // swap from resize inside to
      // resize outside for effect
        pDoc->m_tracker.m_nStyle &=
          ~CRectTracker::resizeInside;
        pDoc->m_tracker.m_nStyle |=
          CRectTracker::resizeOutside;
      }
      else
      {
      // just use inside resize handles on first time
        pDoc->m_tracker.m_nStyle &=
          ~CRectTracker::resizeOutside;
        pDoc->m_tracker.m_nStyle |=
          CRectTracker::resizeInside;
      }
      pDoc->SetModifiedFlag();
      pDoc->UpdateAllViews(NULL,
        (LPARAM)(LPCRECT)rectSave);
      pDoc->UpdateAllViews(NULL);
    }
  }
}

If you want to allow reversible orientation of the tracker during rubber-banding, you should call with the third parameter set to TRUE. Remember that allowing reversible orientation will sometimes cause to become inverted. This can be corrected by a call to .

What do you want to know more about?

See Also