Announcement

Collapse
No announcement yet.

date picker with %DTM_SETRANGE

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • date picker with %DTM_SETRANGE

    Disclaimer: I am dealing with dates in the American format of Month/Day/Year.

    Sending the message of %DTM_SETRANGE to a calendar controls allows us to set the range of a date picker control. The problem I am having is two-fold:
    1. The range will not wait to validate until it gets the entire date. For example if the calendar control has a date of 9/24/2008 and a range that goes from 9/24/2008 to 2/1/2009. When I type 2 for the month, it will not allow it. I assume this is because it is validating "2/24/2008" but that is not what I am trying to type. If I try to start with the date then I get other problems because it appears to validate "9/24/2009." But if I use my mouse and click on the pop-up part of the control then I can get to the correct date. This is really annoying. Has anybody encountered this before? If so, what are choices of work-arounds. I guess it is possible to create my own validation when the NM_KILLFOCUS notification is sent. However, I am looking for better ideas.

    2. This is the fun part. Here is an example:
    Code:
    #COMPILE EXE
    #DIM ALL
    
    #IF NOT %DEF(%WINAPI)
        #INCLUDE "WIN32API.INC"
    #ENDIF
    #IF NOT %DEF(%COMMCTRL_INC)
        #INCLUDE "COMMCTRL.INC"
    #ENDIF
    
    '------------------------------------------------------------------------------
    '   ** Constants **
    '------------------------------------------------------------------------------
    %IDD_DIALOG1             =  101
    %IDC_SYSDATETIMEPICK32_1 = 1002
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    '   ** Declarations **
    '------------------------------------------------------------------------------
    DECLARE CALLBACK FUNCTION ShowDIALOG1Proc()
    DECLARE FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    '   ** Main Application Entry Point **
    '------------------------------------------------------------------------------
    FUNCTION PBMAIN()
        ShowDIALOG1 %HWND_DESKTOP
    END FUNCTION
    '------------------------------------------------------------------------------
    '------------------------------------------------------------------------------
    '   ** CallBacks **
    '------------------------------------------------------------------------------
    CALLBACK FUNCTION ShowDIALOG1Proc()
        LOCAL systime() AS SYSTEMTIME
        LOCAL hctl, x   AS LONG
        DIM SYSTIME(1)
        
        SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                ' Initialization handler
                  systime(0).wYear  = 2008
                  systime(0).wMonth = 1
                  systime(0).wDay   = 1
                  systime(1).wYear  = 2009
                  systime(1).wMonth = 12
                  systime(1).wDay   = 31
                  CONTROL HANDLE CBHNDL, %IDC_SYSDATETIMEPICK32_1 TO hCtl
                  X=SendMessage(HCTL, %DTM_SETRANGE, %GDTR_MIN OR %GDTR_MAX, VARPTR(systime(0)))
                  
            CASE %WM_NCACTIVATE
                STATIC hWndSaveFocus AS DWORD
                IF ISFALSE CBWPARAM THEN
                    ' Save control focus
                    hWndSaveFocus = GetFocus()
                ELSEIF hWndSaveFocus THEN
                    ' Restore control focus
                    SetFocus(hWndSaveFocus)
                    hWndSaveFocus = 0
                END IF
    
            CASE %WM_COMMAND
                ' Process control notifications
                SELECT CASE AS LONG CBCTL
                    CASE %IDC_SYSDATETIMEPICK32_1
    
                END SELECT
        END SELECT
    END FUNCTION
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Dialogs **
    '------------------------------------------------------------------------------
    FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt AS LONG
        LOCAL hDlg  AS DWORD
    
        DIALOG NEW hParent, "Dialog1", 270, 249, 247, 114, TO hDlg
        CONTROL ADD "SysDateTimePick32", hDlg, %IDC_SYSDATETIMEPICK32_1, _
            "SysDateTimePick32_1", 55, 50, 80, 15, %WS_CHILD OR %WS_VISIBLE OR _
            %WS_TABSTOP OR %DTS_SHORTDATEFORMAT, %WS_EX_LEFT OR _
            %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR OR %WS_EX_CLIENTEDGE
    
        DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
    
        FUNCTION = lRslt
    END FUNCTION
    The above example will allow the user to type in "12/30/2009". If the user types in "12/31/2009" it will change it to "12/31/2008." I can however, use the pop-up part of the control and choose "12/31/2008." So the validation again seems odd. Again the only work-around I can think of is to do my own validation of the range using the NM_KILLFOCUS notification. Can somebody explain this control's behavior to me? Can anybody think of any other work-around?

    Thanks!
    Bill Scharf

  • #2
    You're not going to believe this, but I got it to take any date in range by adding parens around the wParam:
    Code:
    X=SendMessage(HCTL, %DTM_SETRANGE, (%GDTR_MIN OR %GDTR_MAX), VARPTR(systime(0)))
    I don't normally type with these controls, I use the up/down arrows to change dates, but both typing and arrows would accept any date in your range.

    MCM
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      This is a 'picky' control all right!
      Try this slight mod, it seemed to help on my machine..
      Code:
      ' 
                    systime(1).wDay   = 31
                    DIALOG POST CBHNDL, %WM_USER + 1000, 0, 0
       
               CASE %WM_USER + 1000
                    CONTROL HANDLE CBHNDL, %IDC_SYSDATETIMEPICK32_1 TO hCtl
      '
      Rgds, Dave

      Comment


      • #4
        still no luck

        Thank you both for your help. I am still not having much success. Here is what I have now:
        Code:
        #COMPILE EXE
        #DIM ALL
        
        #IF NOT %DEF(%WINAPI)
            #INCLUDE "WIN32API.INC"
        #ENDIF
        #IF NOT %DEF(%COMMCTRL_INC)
            #INCLUDE "COMMCTRL.INC"
        #ENDIF
        
        '------------------------------------------------------------------------------
        '   ** Constants **
        '------------------------------------------------------------------------------
        %IDD_DIALOG1             =  101
        %IDC_SYSDATETIMEPICK32_1 = 1002
        '------------------------------------------------------------------------------
        '------------------------------------------------------------------------------
        '   ** Declarations **
        '------------------------------------------------------------------------------
        DECLARE CALLBACK FUNCTION ShowDIALOG1Proc()
        DECLARE FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
        '------------------------------------------------------------------------------
        '------------------------------------------------------------------------------
        '   ** Main Application Entry Point **
        '------------------------------------------------------------------------------
        FUNCTION PBMAIN()
            ShowDIALOG1 %HWND_DESKTOP
        END FUNCTION
        '------------------------------------------------------------------------------
        '------------------------------------------------------------------------------
        '   ** CallBacks **
        '------------------------------------------------------------------------------
        CALLBACK FUNCTION ShowDIALOG1Proc()
            STATIC systime() AS SYSTEMTIME
            LOCAL hctl, x   AS LONG
            DIM SYSTIME(1)
        
            SELECT CASE AS LONG CBMSG
                CASE %WM_INITDIALOG
                    ' Initialization handler
                      systime(0).wYear  = 2008
                      systime(0).wMonth = 2
                      systime(0).wDay   = 1
                      systime(1).wYear  = 2009
                      systime(1).wMonth = 8
                      systime(1).wDay   = 31
                      DIALOG POST CBHNDL, %WM_USER + 1000, 0, 0
               
                 CASE %WM_USER + 1000
                      CONTROL HANDLE CBHNDL, %IDC_SYSDATETIMEPICK32_1 TO hCtl
                      X=SendMessage(HCTL, %DTM_SETRANGE, (%GDTR_MIN OR %GDTR_MAX), VARPTR(systime(0)))
        
                CASE %WM_NCACTIVATE
                    STATIC hWndSaveFocus AS DWORD
                    IF ISFALSE CBWPARAM THEN
                        ' Save control focus
                        hWndSaveFocus = GetFocus()
                    ELSEIF hWndSaveFocus THEN
                        ' Restore control focus
                        SetFocus(hWndSaveFocus)
                        hWndSaveFocus = 0
                    END IF
        
                CASE %WM_COMMAND
                    ' Process control notifications
                    SELECT CASE AS LONG CBCTL
                        CASE %IDC_SYSDATETIMEPICK32_1
        
                    END SELECT
            END SELECT
        END FUNCTION
        '------------------------------------------------------------------------------
        
        '------------------------------------------------------------------------------
        '   ** Dialogs **
        '------------------------------------------------------------------------------
        FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
            LOCAL lRslt AS LONG
            LOCAL hDlg  AS DWORD
        
            DIALOG NEW hParent, "Dialog1", 270, 249, 247, 114, TO hDlg
            CONTROL ADD "SysDateTimePick32", hDlg, %IDC_SYSDATETIMEPICK32_1, _
                "SysDateTimePick32_1", 55, 50, 80, 15, %WS_CHILD OR %WS_VISIBLE OR _
                %WS_TABSTOP OR %DTS_SHORTDATEFORMAT, %WS_EX_LEFT OR _
                %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR OR %WS_EX_CLIENTEDGE
        
            DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
        
            FUNCTION = lRslt
        END FUNCTION
        Note that the date range is Feb 1, 2008 to Aug 31, 2009.

        1. So I try to enter in Jan 1, 2008. I type "01" to just do the month and it will not take it. I don't even get as far as entering the day. I cannot change the year first and then go back to the day or month. I am forced to use the mouse I guess?!?

        2. The last day in the range is 8/31/2009. I can type 8/30/2009, but not 8/31/2009. I can get to 8/31/2009 using the mouse. Again, it seems I am forced to use the mouse.

        I do normally use the mouse, but we have hundreds of users that try to avoid using a mouse as much as possible. Does anybody else have a thought or idea?
        Bill Scharf

        Comment


        • #5
          I am using PB/WIN 8.03 on WIn/XP Pro SP3

          Only change from your code is the parens suggested above.

          When I type '01' it changes it to '1'

          I can type 8/31/09 just fine.

          However, I can also type in 9/30/2009 and it does not complain.

          The arrow keys work perfectly in any one the three fields (MM, DD, YYYY)

          Maybe you have an older version of COMMCTRL.DLL?

          You could try a different display format with DTP_SETDATEFORMAT message or with different styles.

          (I'm also curious how that works at all without calling InitCommonControls).

          MCM
          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            >(I'm also curious how that works at all without calling InitCommonControls).

            Just for the heck of it I generated a list of imports for your program (to see i maybe compiler was automatically calling InitCommonControls.)

            I'll bet you didn't think you were using this many functions...
            Code:
            Listing of imported functions for file D:\Software_Development\pbwin80\work\test_dtp.exe
            Run Date & Time 09-25-2008    09:03:25
            
            GDI32.DLL
                BitBlt
                CreateCompatibleBitmap
                CreateCompatibleDC
                CreateFontIndirectA
                CreateSolidBrush
                DeleteDC
                DeleteObject
                GetDeviceCaps
                GetStockObject
                GetTextMetricsA
                MoveToEx
                SelectObject
                SetBkColor
                SetBkMode
                SetTextAlign
                SetTextColor
            KERNEL32.DLL
                CloseHandle
                CreateFileA
                EnumResourceNamesA
                ExitProcess
                GetCommandLineA
                GetCurrentThreadId
                GetLastError
                GetModuleHandleA
                GetStartupInfoA
                GetVersionExA
                GlobalAlloc
                GlobalFree
                MultiByteToWideChar
                ReadFile
                SetErrorMode
                SetFilePointer
                SetLastError
                Sleep
                TlsAlloc
                TlsFree
                TlsGetValue
                TlsSetValue
                WideCharToMultiByte
                WriteFile
                RtlMoveMemory
            OLE32.DLL
                CLSIDFromProgID
                CoCreateInstance
                CoInitialize
                CoUninitialize
                ProgIDFromCLSID
            OLEAUT32.DLL
                GetActiveObject
                SafeArrayCreate
                SysAllocStringByteLen
                SysFreeString
                SysStringByteLen
                VariantClear
                VariantCopy
            USER32.DLL
                AttachThreadInput
                CheckRadioButton
                ClientToScreen
                CreateDialogIndirectParamA
                CreateDialogParamA
                CreateWindowExA
                DestroyIcon
                DestroyWindow
                DialogBoxIndirectParamA
                DispatchMessageA
                EnableWindow
                FillRect
                GetClientRect
                GetDC
                GetDlgItem
                GetForegroundWindow
                GetMenu
                GetMenuItemInfoA
                GetSysColor
                GetSysColorBrush
                GetWindowLongA
                GetWindowRect
                GetWindowTextA
                GetWindowTextLengthA
                GetWindowThreadProcessId
                IsDialogMessageA
                IsWindow
                LoadImageA
                MapDialogRect
                PeekMessageA
                PostMessageA
                RedrawWindow
                ReleaseDC
                ScreenToClient
                SendMessageA
                SetFocus
                SetForegroundWindow
                SetWindowLongA
                SetWindowPos
                SetWindowTextA
                ShowWindow
                SystemParametersInfoA
                TranslateMessage
                DialogBoxParamA
                GetFocus
                GetWindow
            COMCTL32.DLL
                ImageList_ReplaceIcon
                ImageList_Remove
                ImageList_GetIcon
                ImageList_LoadImageA
                    END OF REPORT    109  items printed
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              I am running Vista Business. Comctl32.dll version 5.82.60001.18000. Date on the file is 1/19/2008. I will see if I get different results in other OS's. It will let me use the arrow keys to get to 8/31/2009, but not let me get there by typing it. Once I use the arrow keys or mouse, then it will let me type it?!?! Very very strange indeed!
              Bill Scharf

              Comment


              • #8
                I just tried it on XP home SP 3 and got the same problem. I click on the field. I type "8" then right arrow, then "31", then right arrow, then "2009" and it will not accept it.

                At this point I am going to do my own validation, but it will be when the user leaves the field. The thing I liked about the setRange message was that the user is limited on the pop-up window--so they cannot click a bad date to begin with. Now the user will choose a bad date and not know about it until they leave the field. This is not ideal, but I think it will work ok.
                Bill Scharf

                Comment


                • #9
                  I just tried it on XP home SP 3 and got the same problem. I click on the field. I type "8" then right arrow, then "31", then right arrow, then "2009" and it will not accept it
                  Same sequence works OK here.

                  Commctl32.dll: Hmm, "System Information" shows two loaded modules named comctl32:
                  In windows/system32, version 5.82 (xpsp.80413-2105)
                  In windows/winsxs/x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.2600.5512_X-ww_35d4ce83 , version 6.0 (xpsp.80413-2105)

                  MCM
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    interesting. WHen I do dir comctl*.dll /s on my Vist business, it finds 7 instances of the file. The first one is the normal comctl32.dll and then latter 6 are in windows\winsxs\....blah blah. The latter 6 seem to all be different version numbers:
                    6.0.6001.18000
                    5.82.6000.16386
                    5.82.6001.18000
                    6.0.6000.16386
                    6.0.6001.18000
                    Since we must support everything from win 98SE to Vista then I can assume that some users will have problems regardless of the version of the dll. I can duplicate the problem on Vista and XP and that is probably a majority of our users. this is interesting though
                    Bill Scharf

                    Comment


                    • #11
                      What I'm seeing here is that the DTP control is stating off set to 9/25/2008 (curent date)
                      You can't start to change it by first setting the month to 1 as Jan 26 2008 is outside of the acceptable range.
                      (Jan won't be accepted as long as the year is 2008).
                      You can't change the year as long as the month is Sept (now) because Sept 2009 would be outside the range!!
                      Once you do have the year and the month the day won't accept 31 from the numeric keys!!

                      This control is a bugger to type directly into! However if you must use the keyboard the best results seem to come with using the arrow keys. Tab into the control then use left and right to move betweem fields and the up and down keys to change the values.
                      Rgds, Dave

                      Comment


                      • #12
                        Thank you all for your help. If nothing else, you confirmed that I am not totally crazy. Sending the setRange command is something I will never do again. I will have to rely on my own validation for DatePicker controls.

                        I hope in future years MS will make this control easier to use with the keyboard. Does anybody know of documentation about any common control changes for the upcoming Windows 7? Or does anybody know of another DatePicker control that will work with keyboard input and a date range?
                        Bill Scharf

                        Comment


                        • #13
                          Code not shown, but when you retrieve an 'out of range' date from the screen, does it come back valid?
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            Michael,

                            I don't follow 100% of what you said.

                            If you want me to post code, here it is...it is nothing noteworthy though.
                            This is the validation I use when the user tries to save the date. The user must enter in a beginning and end date. The end date must be within a year and a day.
                            Code:
                            'get the start datedate
                            CONTROL SEND hdlg, %IDC_DATE_PICKER, %DTM_GETSYSTEMTIME, 0, VARPTR(st1) TO x
                            tempdateStart = format$(st1.wyear) + format$(st1.wMonth, "00") + format$(st1.wDay, "00")
                            tempdateEnd   = format$(st1.wyear + 1) + format$(st1.wMonth, "00") + format$(st1.wDay, "00")
                            'get Enddate
                            CONTROL SEND hdlg, %IDC_DET_DATE_END, %DTM_GETSYSTEMTIME, 0, VARPTR(st) TO x
                            Tempdate = format$(st.wyear) + format$(st.wMonth, "00") + format$(st.wDay, "00")
                            'validate it
                            if TempdateStart > TempDate OR tempDateEnd <  TempDate then
                               msgbox "Invalid date. End date must be between " + format$(st1.wmonth) + _
                            "/" + format$(st1.wDay) + "/" + format$(st1.wYear) + " and " + _
                            format$(st1.wmonth) + "/" + format$(st1.wDay) + "/" + _
                            format$(st1.wYear+1),%MB_ICONERROR + %MB_TASKMODAL,"Invalid End Date"
                                CONTROL SET FOCUS hdlg, %IDC_DET_DATE_END
                                function = 0
                                EXIT FUNCTION
                            end if
                            The user can still pick an invalid date...but they cannot save it. It is not quite as good as field-level validation, but it will work.
                            Bill Scharf

                            Comment


                            • #15
                              I meant, does DTM_GETSYSTEMTIME return GDT_VALID or GDT_ERROR?

                              ("X" in your code).
                              Michael Mattias
                              Tal Systems (retired)
                              Port Washington WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment

                              Working...
                              X