AN EASY CALENDAR CONTROL (intermediate level)

© 2002 by Alyce Watson

Home

Use of Color in Graphics

Bitmap Graphics Tutorial

Bitmap Color Formats

Bmp Dimensions

SCAN vs WAIT

Text Input Boxes

An Easy Calendar Control

Shareware Marketing

Date/Time Picker

Text Line-Wrap

Combining Commands

Newsletter help

Index


Many thanks to Brent Thorn for the demo of the Windows DateTimePicker control, which is included in the DEMOS section of this newsletter. The following is an in-depth look at both the calendar control, and the time-picker implementation of the control.

We can create controls for windows by using API calls, and they often work quite well. These custom controls expand the usage of Liberty BASIC and can add a very professional look to our applications. For more information on creating custom controls, try these downloadable electronic books:

Using Liberty BASIC 2

http://iquizme.0catch.com/lb/using/index.html

Mastering Liberty BASIC 3 (an update of the previous ebook)

http://iquizme.0catch.com/lb/lbw3/master.html

The first step in creating any control with API calls is to obtain the program window's handle, and use it to get the instance handle of the window. This is done with the function GetWindowLongA, using the window's handle as an argument and the flag _GWL_HINSTANCE to specify which long window value we want.

  CallDLL #user32, "GetWindowLongA", _
  hWindow As long,_         'window handle
  _GWL_HINSTANCE As long,_  'flag to get instance handle
  InstanceHandle As long    'returns instance handle

The next step is to initialize the Windows DLL that provides the common controls, of which the DateTimePicker is a part. For some common controls, it is possible to use the simpler version of initialization, InitCommonControls, but that won't work for this control, which requires the InitCommonControlsEx function. The function requires a struct that contains the initialization data:

    struct icex, _
    dwSize As ulong, _
    dwICC As ulong

To fill the struct, specify the size of the struct:

icex.dwSize.struct = Len(icex.struct)

Next, tell the DLL which control you want to have available:

icex.dwICC.struct = Hexdec("100") 'ICC_DATE_CLASSES

Once the struct is properly filled, call the function:

    CallDLL #comctl32, "InitCommonControlsEx", _
    icex As struct, r As long

Once the initialization function is called, the specified control is registered and available for your application's use.

THE CALENDAR (DATE PICKER) CONTROL

The calendar control allows a program's user to select a date. This may not sound too exciting, but the control is quite neat. It consists of a textbox with an arrow button at the end, which looks quite a lot like a combobox. The textbox portion contains the currently selected date. When the arrow button is clicked, a full month calendar drops into view! It appears below and aligns itself with the left side of the textbox. This calendar notes the "today" date and has arrow buttons that allow the user to scroll forward and backward through the months of the year -- a couple of hundred years in each direction. Once the user clicks on a date, the calendar disappears from view, and the selected date appears in the textbox portion of the control. The user can also click on any portion of the date in the textbox and type over it to change the date. For instance, the user can type over the month, making it a 12, and when the calendar drops down, it will display the month of December. So, the user may choose a date either by typing it into the textbox portion of the control, or by scrolling through the months in the dropdown calendar.

The calendar can be customized by the programmer to show the desired date format and display colors.

As with many of the controls we create with API functions, this one is created with CreateWindowExA. An extended style isn't needed for this window, so the first argument may be 0. The class argument is "SysDateTimePick32". You may give the window a name, but that is optional. You must also specify the x, y placement on the window, the width and height, the handle of the parent window, and the instance handle of the parent window. The parameter for the menu handle is set to 0. The function returns a handle to the newly created control:

    style = _WS_VISIBLE Or _WS_CHILD 
    CallDLL #user32, "CreateWindowExA", _
    0 As long,_                     'extended style
    "SysDateTimePick32" As ptr, _   'class
    "DateTime" As ptr, _            'name
    style As long,_                 'window style flags
    x As long, y As long,_          'upper left x,y
    w As long, h As long,_          'width and height
    hW As long,_                    'parent window handle
    0 As long,_                     'menu handle
    hInstance As long,_             'instance handle of parent
    0 As long, _                    'not used here - extra data
    ControlHandle As long           'handle of created control
    End Function

We haven't yet mentioned the style flags. The minimum for any control are "_WS_VISIBLE Or _WS_CHILD" which make the control visible and specify that it is a child window. Here are some additional style flags that are specific to the DateTime control:

DTS.UPDOWN = 1          'shows updown arrows, no calendar
DTS.RIGHTALIGN = 32     'aligns calendar with right side of control
DTS.SHORTDATEFORMAT = 0 'default short date format:  9/22/02
DTS.LONGDATEFORMAT = 4  'long date format: Sunday, September 22, 2002
DTS.SHORTDATECENTURYFORMAT = 12 '                  9/22/2002
DTS.APPCANPARSE = 16    'double-clicking highlights entire date
                        'default-click highlights only one part of date

The control is quite slick, but useless if we can't read the date chosen by the user! We can obtain the displayed date with a call to GetWindowTextA. We'll first want to find out the length of the text so that we can set up a properly sized buffer:

  CallDLL #user32, "GetWindowTextLengthA",_
    hW As long,_    'handle of date/time control
    tLength As long 'returns length of text

Now we can set up a string buffer and make the call to GetWindowTextA. After the call, the date information will be contained in Title$

    Title$=Space$(tLength)+Chr$(0)
    l=Len(Title$)
    CallDLL #user32, "GetWindowTextA",_
    hW As long,_    'handle of date/time control
    Title$ As ptr,_ 'buffer to hold text
    l As long,_     'length of buffer
    result As long

    notice trim$(Title$)

The method using GetWindowTextA works fine, but if we want to separate the date or time components, Windows has provided a message that allows us to get the date and time chosen by the user. We use SendMessageA and send the DTM_GETSYSTEMTIME message to the control. This message requires a struct to hold all of the components of the time and date: year, month, day of week, day, hour, minute, second and millisecond. No struct members need to be filled before calling the function.

    DTM.GETSYSTEMTIME = 4097
    Struct SYSTEMTIME, _
        wYear As word, _
        wMonth As word, _
        wDayOfWeek As word, _ '0 = Sunday, 1 = Monday, etc.
        wDay As word, _
        wHour As word, _
        wMinute As word, _
        wSecond As word, _
        wMilliseconds As word

Now that the struct is defined, call the function SendMessageA:

    CallDLL #user32, "SendMessageA", _
        hW As long, _               'handle of control
        DTM.GETSYSTEMTIME As long,_ 'flag to get chosen date/time
        0 As long, _                'wparam=0
        SYSTEMTIME As struct,_      'name of struct
        ret As long

To retrieve a single value, read the struct member desired. Here, we're checking for the month:

mon=SYSTEMTIME.wMonth.struct

If mon = 12, then the month is December. If you want to know the month name, simply set up a string like this:

m$="JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC"

You could use entire month names, like "January", of course!

To get the month name, knowing the month number, use the word$() function:

Month$=word$(m$,mon)

CUSTOMIZING THE CALENDAR

We can customize the calendar by setting style flags when we create the control. If we include the flag for DTS.LONGDATEFORMAT then the date will appear with the weekday spelled out, the month spelled out, followed by the day and year. The style flags would look like this:

style = _WS_VISIBLE Or _WS_CHILD or DTS.LONGDATEFORMAT

(Note that Liberty BASIC doesn't recognize the date/time Windows constants, so we must supply these values in our code.)

The other styles are documented above and include DTS.UPDOWN DTS.RIGHTALIGN, DTS.SHORTDATEFORMAT, DTS.LONGDATEFORMAT, DTS.SHORTDATECENTURYFORMAT, and DTS.APPCANPARSE.

If the DTS.UPDOWN style is chosen, then no calendar will drop down. In that case, the user can type over the portion of the date he wishes to alter, or he can use the up-down arrows to scroll the currently highlighted portion of the date. For instance, if the month is highlighted, then pressing the up arrow will increase the month value.

COLORS

We can change the default display colors to the ones of our choice by sending the calendar control a message after it is created. We do this with SendMessageA, and the appropriate arguments. Here are the arguments from which we may choose:

DTM.SETMCCOLOR=4102     'message flag to set calendar background color

The above value is the message we send to change a color in the calendar. The values below are the parts of the calendar whose color can be changed:

MCSC.BACKGROUND = 0     'Set the background color displayed between months.
MCSC.TEXT = 1           'Set the color used to display Text within a month.
MCSC.TITLEBK = 2        'Set the background color displayed in the calendar's title.
MCSC.TITLETEXT = 3      'Set the color used to display Text within the calendar's title.
MCSC.MONTHBK=4          'Set the background color displayed within the month.
MCSC.TRAILINGTEXT = 5   'Set the color used to display header day and trailing day text.
                        'Header and trailing days are the days from the previous and
                        'following months that appear on the current month calendar.

Here is a small demo that changes the background color of the displayed calendar:

  DTM.SETMCCOLOR=4102 'message flag to set calendar color
  MCSC.MONTHBK=4      'color to change=month background
  col = 255 + (160*256) + (160*256*256)

  CallDLL #user32, "SendMessageA",_
  hW As long, _               'handle of control
  DTM.SETMCCOLOR As long,_    'set color message
  MCSC.MONTHBK As long,_      'part of control to set color on
  col As long,_               'color desired
  re As long

The formula to create a color is:

color = red + (green*256) + (blue*256*256)

See the color article above for more on RGB colors.

TIME PICKER

The time picker implementation isn't as fancy. It consists of a textbox containing the time,and spinner (up-down) arrows that allow the user to change the time The user can type over the time the textbox or use the arrows to scroll. The arrows will scroll the time segment that is highlighted -- either hours, or minutes, or seconds. The control must be created with the DTS.TIMEFORMAT style:

  DTS.TIMEFORMAT=hexdec("0x0009")
  style = _WS_VISIBLE Or _WS_CHILD Or DTS.TIMEFORMAT

SETTING INITIAL DATE/TIME

Setting the initial date or time that is displayed on a control is similar to retrieving the chosen date or time.

The message to send to the control to set the date or time is :

DTM.SETSYSTEMTIME = 4098

The message requires the SYSTEMTIME struct as before:

    Struct SYSTEMTIME, _
        wYear As word, _
        wMonth As word, _
        wDayOfWeek As word, _ '0 = Sunday, 1 = Monday, etc.
        wDay As word, _
        wHour As word, _
        wMinute As word, _
        wSecond As word, _
        wMilliseconds As word

Fill the struct members desired before sending the message. This demo sets the date to February 14, 2003:

        SYSTEMTIME.wMonth.struct=2
        SYSTEMTIME.wDay.struct=14
        SYSTEMTIME.wYear.struct=2003

    DTM.SETSYSTEMTIME = 4098'message flag to set date/time displayed on control

Now, make the API call to send the message to the control:

    CallDLL #user32, "SendMessageA", _
        hwndDTP As long, _          'handle of control
        DTM.SETSYSTEMTIME As long,_ 'flag to set chosen date/time
        0 As long, _                'wparam=0
        SYSTEMTIME As struct,_      'name of struct
        ret As long

DEMOS

Attached are two demos. Calendar_nl.bas creates a calendar control with the date format DTS.SHORTDATECENTURYFORMAT, which displays like this: 9/22/2002. It includes code to change the month background color to pink. It includes both methods of retrieving the date - the GetWindowTextA method, or the more involved message for DTM_GETSYSTEMTIME. It also includes a routine to retrieve the month name from the chosen date. It also includes a function to set the initial date displayed on the control.

Timepick_nl.bas demonstrates the time-picker implementation of the control.