[ Next ] [ Previous ] | Chapter 21 | [ Contents ] [ Chapter 20: Drag and Drop ] [ Chapter 22: Notebook ]

### Value Set

A value set is a control that provides a way for a user to select from several graphically illustrated choices. Only one choice can be selected at a time. A value set can use icons, bitmaps, colors, text, or numbers. However, it is optimal to use only graphical images and/or short text; other controls should be used if a choice of only text or numbers is offered. The value set is designed to show setting choices, not action choices; if an action choice needs to be designated, a push button or menu should be used. A value set must contain at least two items. A value set choice that is unavailable should be disabled; if a value set has text choices, a letter for each choice should be designated as a mnemonic. A value set can be used as a tool palette also; however, the pointer should be changed to represent the current "tool" selected. For instance, if a "paint" tools is selected, the cursor could be changed to represent a paintbrush. |
Figure 21.1 Example of the value set control.

#### Value Set Styles

Table 21.1 lists the available value set styles

Table 21.1 Value Set Styles
Style | Description
VS_BITMAP | Default all value set items to bitmaps.
VS_ICON | Default all value set items to icons.
VS_TEXT | Default all value set items to text strings.
VS_RGB | Default all value set items to color info in RGB values
VS_COLORINDEX | Default all value set items to color info in color indices
VS_BORDER | Add a border around the value set control.
VS_ITEMBORDER | Add a border around each value set item
VS_SCALEBITMAPS | Scale bitmaps to fit in cell size.
VS_RIGHTTOLEFT | Support right to left ordering.
VS_OWNERDRAW | Owner draws value set control.

The following example program shows the creation of a value set control with the style VS_COLORINDEX.

> VALUE.C
> VALUE.RC
> VALUE.H
> VALUE.MAK
> VALUE.DEF

####

#### The VALUE.RC Resource File

The VALUE.RC file contains two items: a menu and a dialog with the value set control. The dialog is created with the following code.

    DLGTEMPLATE IDD_VALUE LOADONCALL MOVEABLE DISCARDABLE
    {
        DIALOG  "Color Set", IDD_VALUE, 12, 12, 155, 105, WS_VISIBLE,
                FCF_SYSMENU | FCF_TITLEBAR
        {
            LTEXT           "Select color: ", -1, 11, 25, 102, 8
            VALUESET        IDV_VALUE, 13, 38, 91, 61, VS_COLORINDEX | VS_BORDER
                            CTLDATA 8, 0, 3, 4
            PUSHBUTTON      "Cancel", DID_CANCEL, 6, 2, 40, 14
        }
    }

The sixth parameter in the VALUESET statement is the combination of window and control styles. In this case, we specify VS_COLORINDEX, indicating that the choices of the value set are the indices into the color index table. We also use VS_BORDER, which draws a border around the value set. The last parameter is the CTLDATA statement. In this case, this represents the VSCDATA structure. The VSCDATA structure is defined as:

       typedef struct _VSCDATA     /* vscd */
       {
          ULONG   cbSize;                /* Size of control block             */
          USHORT  usRowCount;            /* Number of rows in value set       */
          USHORT  usColumnCount;         /* Number of columns in value set    */
       } VSCDATA;
       typedef VSCDATA *PVSCDATA;

The CTLDATA key word sees each parameter as a SHORT, so a LONG is represented as two parameters. The first two parameters correspond to the cbSize structure member. They are specified in low-byte, high-byte order. The third parameter represents usRowCount. Our value set will contain three rows. The fourth parameter represents usColumnCount. Our value set will contain four columns.
A structure defined at the top of the program is used for the window word. It is:

    	typedef          struct
    	{
    	   SHORT            sColor;
    	   HWND             hwndDlg;
    	} WNDDATA,*PWNDDATA;

In the structure, the first element SHORT sColor represents the currently selected color in the value set. The hwndDlg is the window handle for the dialog box.
Also, the array alColor is declared. This is the array of color index values that are used in the value set.``

#### Initializing the Value Set

          case  WM_INITDLG :
             {  SHORT            sColor;
                USHORT           usX;
                USHORT           usY;
                MRESULT          mrReply;

                sColor = 0;

                /***************************************************/
                /* loop through the rows and columns to initialize */
                /* items                                           */
                /***************************************************/
                for (usX = 1; usX <= 3; usX++)
                {
                   for (usY = 1; usY <= 4; usY++)
                   {
                      mrReply = WinSendDlgItemMsg(hwndDlg,
                                                  IDV_VALUE,
                                                  VM_SETITEM,
                                                  MPFROM2SHORT(usX,
                                                               usY),
                                                  MPFROMLONG(alColors
                                                     [sColor++]));
                      if (!LONGFROMMR(mrReply))
                         DisplayError("WinSendDlgItemMsg failed");

                   }                       /* endfor               */
                }                          /* endfor               */
             }                             /* end WM_INITDLG       */
             break;

The value set initialization is a very simple process of sending a VM_SETITEM for each item in the value set. Because this value set is of style VS_COLORINDEX, mpParm2 will contain a color index constant. We will use the CLR_* values in the alColor array. mpParm1 is a collection of two SHORTS that make up the row and column of the item. Notice that there is no row or column 0; these values start at 1. All value set messages pertaining to a specific value set item are done by using the row and column of the item of interest.
By default, the first item in the value set is selected.

#### Value Set Select Notification

       /* get row and column of selected item                      */
       usRow = SHORT1FROMMP(mpParm2);
       usCol = SHORT2FROMMP(mpParm2);

       /* calculate index into color array                         */
       sColorIndex = ((usRow-1)*4)+(usCol-1);

       /* get the client window handle to post message             */
       hwndFrame = WinWindowFromID(HWND_DESKTOP,
                                   ID_FRAME);
       hwndClient = WinWindowFromID(hwndFrame,
                                    FID_CLIENT);
       bSuccess = WinPostMsg(hwndClient,
                             USRM_UPDATE,
                             MPFROMSHORT(sColorIndex),
                             MPVOID);

The WM_CONTROL message is where the value set will indicate when a new color has been selected. We check for the notification code VN_SELECT from WM_CONTROL message. The row number (starting with 1) is sent as the low order byte of mpParam2. The column number is sent as a high order byte of mpParm2. By doing some quick math, the index into the alColor array is determined. The next task is to notify the client window that a new selection has been made. This is done by posting the user-defined message, UM_UPDATE, to the client, with the color index sent in mpParm1.

#### VALUE Paint Processing

          case  WM_PAINT :
             {  HPS              hpsPaint;
                RECTL            rclPaint;
                SHORT            sColor;
                BOOL             bPaint = FALSE; /* variable to indicate whether to paint or not    */

                pwdData = WinQueryWindowPtr(hwndClient,
                                            QWL_USER);

                /* paint the entire client with the dropped color  */
                hpsPaint = WinBeginPaint(hwndClient,
                                         NULLHANDLE,
                                         &rclPaint);
                GpiErase(hpsPaint);

                /* do some error checking                          */
                if (pwdData)
                {  if (pwdData->sColor >= 0)
                   {
                      bPaint = TRUE;
                      sColor = pwdData->sColor;
                   }
                }

                if (bPaint)
                   WinFillRect(hpsPaint,
                               &rclPaint,
                               alColors[sColor]);
                WinEndPaint(hpsPaint);
             }
             break;

The WM_PAINT message starts with WinQueryWindowPtr to retrieve the window word of the client window. Next the usual WinBeginPaint is called. GpiErase is used to erase the entire invalidated region. If the sColor variable in the pwdData structure is greater than 0, a color has been selected by the user. Remember, the variable was initially set to -1. A Boolean variable bPaint is used to indicate all is okay, so go ahead and paint. WinFillRect fills the invalidated region with the specified color, and WinEndPaint is called to release the presentation space.

#### The User-defined Message UM_UPDATE

          case  USRM_UPDATE :
             /******************************************************/
             /* user message indicates end-user selected new color */
             /* in value set, window needs to repaint itself with  */
             /* new color                                          */
             /******************************************************/

             pwdData = WinQueryWindowPtr(hwndClient,
                                         QWL_USER);
             if (!pwdData)
             {
                DisplayError("WinQueryWindowPtr failed");
                break;
             }
             pwdData->sColor = SHORT1FROMMP(mpParm1);
             WinInvalidateRect(hwndClient,
                               NULL,
                               FALSE);
             WinUpdateWindow(hwndClient);
             break;

The message UM_UPDATE is a user-defined message that is sent from the value set when a new value set item has been selected. This is the signal to the client to repaint itself. The index of the selected item is sent in mpParam1. This value is retrieved and stored in the pwdData structure so it is visible to the WM_PAINT processing. WinInvalidateRect is used to invalidate the entire client window, and WinUpdateWindow message is used to force the update of the client window - in other words, generate a WM_PAINT message and process it, now!

    BOOL  APIENTRY WinUpdateWindow(HWND hwnd);

WinUpdateWindow has only one parameter - hwnd, which is the window handle of the window to update.

This potent approach is not always necessary, but the example program depends on a quick user notification of the new value set selection.

[ Next ] [ Previous ] | Chapter 21 | [ Contents ] [ Chapter 20: Drag and Drop ] [ Chapter 22: Notebook ]