HotProp Control

Environment: VS6/.NET, Windows 98/Me/2000 and XP

New features

Version 1.8
– Minor bugs fixed

Version 1.7
– Showing and hiding properties
– Bar style properties

Version 1.6
– Support for custom drawn properties
– New property types: button and enum

Version 1.5
– Improved appearance, including rolling menus, flat style, gradient fill and XP-like item highlighting
– New splitter window included

Overview

The HotProp Control was designed for applications that need a flexible, modeless property window. There are many traditional solutions, similar to the property list in Visual Basic. HotProp has a much more modern look, and it has also an improved functionality. You don’t have to select an item and click once again to open the combo or edit the property. HotProp doesn’t have any focus and item selection; instead, it uses the hot tracking technique. A border is drawn when you place the mouse above an item and you can open the popup list, toggle the item, or start in-place editing with just a single mouse click. The control also supports keyboard shortcuts for each property.

There are six types of properties available:

  • toggle—the user can switch between two values, eg. No and Yes
  • list—there is a number of options, which can be selected using a popup list
  • enum—similar to list, but there is no stringarray object (useful for owner drawn properties)
  • button—simple button, which can be pressed
  • string—any string can be entered (with a specified length limit)
  • integer—a number in a specified range can be entered

Each property has an identifier. It can be any number, provided that it’s unique for each property diplayed at the same time. The identifier is passed as the first argument to most of the functions. The name of the property is what is displayed on its bar, it can have an underscored mnemonic marked with the ‘&’ sign. The control has an image list; images can be displayed to the left of each property’s name.

Properties have two values, a number and a string, connected to each other. The string is always what is displayed in the control. The numeric value is 0 or 1 for a toggle, index of the selected option for a list and the entered value for an integer propetry. When the numeric value is set, the string is updated automatically (either to display proper option for a toggle/list, or the formatted number). A negative value can be given, causing the string to be cleared (it’s meant to be an undefined state of a property).

Properties can be added at the bottom or inserted after a specified one. You can remove individual properties or all at once. Remember that these operations don’t update the control to avoid flickering when you remove and add many properties. You have to call Update() after you add or remove properties to let the control update its size and redraw itself. You don’t have to call it when you change properties (name, value, state etc.). The name and image index of each property can be changed.

Each type of property has specific information that you can change by using the following functions:

  • toggle—the negative and positive strings can be changed by using SetPropToggle()
  • list—the options are stored in a CStringArray object that can be retrieved using GetPropStringArray() and modified whenever you need
  • enum—the number of entries in the list can be changed with SetPropLimit()
  • string—the maximum length of text can be specified with SetPropLimit() (default is 1000)
  • integer—the min and max value can be changed with SetPropLimits() (default is 0 to 1000)

Note that the value of the property is not changed automatically when you call these functions.

Another useful feature of HotProp is the ability to lock or disable individual properties. A locked property is read-only and a small lockpad appears to the right of its bar. When a property is disabled, its name and icon is grayed and no value is displayed.

In version 1.6, some basic grouping abilities have been added. You can hide and show individual properties and whole ranges of properties instead of creating and removing them every time. Hidden properties still remember their values and can be modified. Some properties may have a special “bar” style, useful for separating groups of items.

Project & Settings

Add the following files to your project:

  • HotPropCtrl.cpp/.h
  • HPDynEdit.cpp/.h
  • HPPopList.cpp/.h (these are needed by the control)
  • HCSplitWnd.cpp/.h (if you use CHPSplitWnd)

Link your application with msimg32.lib. The control uses the GradientFill function from msimg32.dll.

Some visual settings of the control can be changed by commenting out the macros in HotPropCtrl.h. The OfficeXP-like style can be used either when Windows XP is detected (default style otherwise) or on all systems (or never).

Using in a Splitter Window

The simplest way of creating the HotProp control is to create a static splitter window with the application’s view in one pane and the control in the other pane. It doesn’t hide the view and it’s much simplier than using control bars, so I used this method in the demo program. I wrote a class extending the standard splitter window—CHPSplitWnd. It can be used in the static mode only. It can display the contents of views when dragging instead of a black stripe (depending on system settings), and uses standard system cursors. If the splitter contains two views (vertical or horizontal), both views are resized proportionally when the window size is changed.

  1. Add the splitter window to CMainFrame. You can use the original CSplitterWnd instead of my class.
  2.    CHPSplitWnd m_wndSplitter;

  3. Create the OnCreateClient() virtual function to create static splitter, like this:

  4. BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT lpcs,
    CCreateContext* pContext )
    {
    if (!m_wndSplitter.CreateStatic(this, 1, 2))
    return FALSE;

    m_wndSplitter.SetInitialRatio(0.75); // left pane is 75%
    // wide

    if (!m_wndSplitter.CreateView( 0, 0,
    RUNTIME_CLASS(CHotPropView),
    CSize(100, 100),
    pContext)
    || !m_wndSplitter.CreateView( 0, 1,
    RUNTIME_CLASS(CHotPropCtrl),
    CSize(100, 100),
    pContext))
    {
    m_wndSplitter.DestroyWindow();
    return FALSE;
    }

    return TRUE;
    }

  5. You can add a public method to CMainFrame for easy access to the HotProp control from your view:

  6. CHotPropCtrl& CMainFrame::GetHotProp()
    {
    return *((CHotPropCtrl*)m_wndSplitter.GetPane(0, 1));
    }

  7. Since the control is not a view, the framework doesn’t destroy it automatically. You have to do it manually; for example, in OnDestroy():

  8. void CMainFrame::OnDestroy()
    {
    GetHotProp().DestroyWindow();

    CFrameWnd::OnDestroy();
    }

  9. Once the control is created, you need to init the image list. It’s best to do it in main frame’s OnCreate. The images should be 16 x 16 pixels. You can call GetImageList() and init the list manually or use the following function to load the bitmap:
  10.    GetHotProp().LoadImages(IDB_IMAGES, RGB(255,0,255));

Using in a Modeless Window

You can create the HotProp control as a child of a modeless dialog, a property page, docking control bar, and so forth. All you have to remember is that the CHotPropCtrl object has to be allocated on the heap, since it deletes itself in PostNcDestroy().

  1. Add a pointer to the parent window’s class:
  2.    CHotPropCtrl* m_pHotProp;

  3. Add the following code to your window’s OnCreate() or OnInitDialog():

  4. m_pHotProp = new CHotPropCtrl;
    m_pHotProp->CreateEx( WS_EX_STATICEDGE,
    NULL,
    NULL,
    WS_VISIBLE|WS_CHILD,
    CRect(…position…),
    this,
    …control ID…);

    In this case, the control is destroyed automatically with the parent window. Don’t delete it manually.

Interaction with the View

HotProp control is designed for SDI and MDI applications. By default, the control sends the notification to the active view. You can change this by overriding the NotifyView() virtual function. The notification is sent whenever a property is changed by the user (or a button property is clicked).

  1. To handle notifications, add the following line to your view’s header file:
  2.    afx_msg void OnHotPropUpdate(int nID, int nValue);

  3. Add the following entry to the message map:
  4.    ON_MESSAGE(HPCM_UPDATE, OnHotPropUpdate)

  5. Implement OnHotPropUpdate(), which gets the identifier of the property that was changed and its new numeric value. You can safely modify any properties in the message handler. If you add or remove properties, don’t forget about calling Update().

  6. void CYourView::OnHotPropUpdate(int nID, int nValue)
    {
    CHotPropCtrl& prop =
    ((CMainFrame*)AfxGetMainWnd())->GetHotProp();

    // apply changes to the document
    }

  7. To handle keyboard mnemonics, you can create a WM_CHAR handler in your view’s class. Pass the character to the ProcessKey() function.

  8. void CYourView::OnChar( UINT nChar,
    UINT nRepCnt,
    UINT nFlags )
    {
    CHotPropCtrl& prop =
    ((CMainFrame*)AfxGetMainWnd())->GetHotProp();
    prop.ProcessKey(nChar);
    }

Custom Drawn Properties

Properties of all types can be custom drawn. It means that instead of drawing the text value, HotProp calls the DrawCustomItem() virtual function. You can override it in your class to implement drawing. By default, it sends a HPCM_DRAWCUSTOM message to the active view, so you don’t need to subclass anything.

Use SetCustomProp() to make a property custom drawn. You should give it a non-zero code that is passed to your drawing procedure. Giving it a code of 0 restores the standard way of drawing. If several properties are drawn the same way, you can assign them the same code. You should only draw the value itself; the control handles the background and frames for you. You can use GetImageList() if you want to draw images.

Custom drawn properties can be used with lists—the items are drawn using the same function. If you don’t need a string array associated with the property, you can use the enum type instead of a list. Just set the number of entries and draw whatever you need.

  1. Add the following line to the header file of your view:

  2. afx_msg void OnHotPropDrawCustom( int nCustom,
    CHotPropCtrl::CustomItem*
    pItem);

  3. Add the following entry to the message map:
  4.    ON_MESSAGE(HPCM_DRAWCUSTOM, OnHotPropDrawCustom)

  5. Implement OnHotPropUpdate(), which gets the custom code and a stucture containing the DC, item rectangle, identifier, and values to use. They are not always the current values of the property—the list uses custom drawing on all items.


    void CYourView::OnHotPropDrawCustom( int nCustom,
    CHotPropCtrl::CustomItem*
    pItem)
    {
    CHotPropCtrl& prop =
    ((CMainFrame*)AfxGetMainWnd())->GetHotProp();
    CDC& dc = *pItem->m_pDC;
    CRect rcItem = pItem->m_rcRect;

    switch (nCustom)
    {
    // draw item
    }
    }

Visit the author’s home page at http://www.mimec.w.pl.

Downloads


Download demo project – 57 Kb


Download source – 18 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read