Using Win32 functions in Visual FoxPro Image Gallery
Code examples:
GDI+: custom Clock Control
Monitoring changes in a directory
Disk in drive A:
Displaying hypertext links with the SysLink control (VFP9, Comctl32.dll)
How to delete file on FTP server
Reading parameters of streams in AVI file
Starting an external application in VFP using WinExec
How to release and renew a lease on an IP address previously obtained through Dynamic Host Configuration Protocol (DHCP)
DiskFreeSpace class
Displaying the main Dial-Up Networking dialog box
GDI+: cropping images
Accessing examples contained in this reference from a VFP application
How to display the Print property sheet
Retrieving the System Time adjustment
Using Shell for performing operations on files
Winsock: retrieving information from a host database for a given host name
Displaying Windows shell folders in TreeView control with Visual FoxPro FLL
Drawing a rectangle using Windows regular edges and borders
How to initiate System shutdown
Pocket PC: base class
GDI+: reading and writing metadata in JPEG and TIFF files
HOWTO: Use the Win32 API to Access File Dates and Times
Reading and setting explicit Application User Model ID for the current process (Win7)
Retrieving window and menu help context identifiers
GDI+: custom Clock Control

User rating: 10/10 (1 votes)
Rate this code sample:
  • ~
More code examples    Listed functions    Add comment     W32 Constants      Translate this page Printer friendly version of this code sample
Before you begin:


On each timer tick, an instance of control creates a temporary bitmap and draws on it the clock face, then calculates hand angles and draws the hands. Once all drawing is complete the temporary bitmap (the back buffer) is copied to the control rectangle (the front buffer).

Such double buffering allows drawing graphics with less flicker and tearing.

The code is based on:
  • custom GDI+ class
  • base GDI+ control

    Download both classes and save them as gdiplus.prg and BaseControl.prg respectively.

  • SET PROCEDURE TO gdiplus ADDITIVE 
    SET PROCEDURE TO basecontrol ADDITIVE 
    
    _screen.AddObject("CustomClock1", "CustomClock")
    
    WITH _screen.CustomClock1
        .BackStyle=0
        .Left=5
        .Top=5
        .ClockfaceCaption="London"
        .IsLocalTime=.F.
        .StandardBias=0
        .ClockfaceColor=RGB(255,255,255)
        .Visible = .T.
        .StartClock
    ENDWITH
    * end of main
    
    DEFINE CLASS CustomClock as GdiplusControl
    PROTECTED ClockfaceImage, hIcon,;
        ClockfaceIcon, BodyFont
    
        BorderWidth=0
        Width=160
        Height=160
    
        ClockfaceCaption=""
        ClockfaceFile=""
        ClockfaceIconFile=""
    
        CurrentTime=DTOT(DATE())
        IsLocalTime=.T.
        StandardBias=0
    
        hIcon=0
        ClockfaceIcon=NULL
        BodyFont=NULL
        ClockfaceImage=NULL
    
        ClockfaceColor=RGB(255,255,255)
        RimColor=RGB(32,32,32)
        HandColor=RGB(32,32,32)
        SecondHandColor=RGB(192,0,0)
        HandShadowColor=RGB(96,96,96)
        TimeTextColor=RGB(96,96,96)
    
        DIMENSION InfoStrings(3, 2)
    
        ADD OBJECT Timer1 as Timer WITH;
            Enabled=.F., Interval=200
    
    PROCEDURE Init
        GdiplusControl::Init
    
        WITH THIS
            .InitClockIcon
        ENDWITH
    
    PROCEDURE ReleaseGdiplusObjects
        WITH THIS
            .StopClock
            .ClockfaceImage=NULL
            .ClockfaceIcon=NULL
            .BodyFont=NULL
            .ReleaseClockIcon
        ENDWITH
    
    PROCEDURE ClockfaceFile_ASSIGN(cFilename)
        THIS.ClockfaceFile = FULLPATH(m.cFilename)
        THIS.ClockfaceImage = NULL
    
    PROCEDURE ClockfaceIconFile_ASSIGN(cFilename)
        WITH THIS
            .ClockfaceIconFile = cFilename
            .InitClockIcon
        ENDWITH
    
    PROCEDURE StartClock
        THIS.BindEvents
        THIS.Timer1.Enabled=.T.
    
    PROCEDURE StopClock
        THIS.Timer1.Enabled=.F.
    
    PROCEDURE Timer1.Timer
        THIS.Parent.DrawFrame
    
    PROCEDURE GetCurrentTime
    RETURN IIF(THIS.IsLocalTime, DATETIME(),;
        THIS.GetSysTime() + THIS.StandardBias * 3600)
    
    PROCEDURE GetClockIconHandle
    #DEFINE MAX_PATH 260
        LOCAL cBuffer, nBufsize, nIconIndex, hIcon
    
        IF FILE(THIS.ClockfaceIconFile)
            hIcon = ExtractAssociatedIcon(0, THIS.ClockfaceIconFile, 0)
            IF hIcon <> 0
                RETURN hIcon
            ENDIF
        ENDIF
    
        cBuffer = REPLICATE(CHR(0), MAX_PATH)
        nBufsize = GetModuleFileName(0, @cBuffer, MAX_PATH)
        cBuffer = LEFT(cBuffer, nBufsize)
        nIconIndex = 1  && try from 0 and up
    RETURN ExtractAssociatedIcon(0, cBuffer, @nIconIndex)
    
    PROCEDURE ReleaseClockIcon
        WITH THIS
            .ClockfaceIcon=NULL
            IF .hIcon <> 0
                DestroyIcon(.hIcon)
                .hIcon = 0
            ENDIF
        ENDWITH
    
    PROCEDURE InitClockIcon
    * icon to be displayed on clockface
        THIS.ReleaseClockIcon
    
        LOCAL hIcon
        hIcon = THIS.GetClockIconHandle()
    
        IF hIcon = 0
            RETURN
        ENDIF
    
        WITH THIS
            .hIcon = m.hIcon
            .ClockfaceIcon = CREATEOBJECT("gdibitmap")
            .ClockfaceIcon.CreateFromHICON(.hIcon)
            .ClockfaceIcon.graphics.SmoothingMode = 4
        ENDWITH
    
    FUNCTION GetSysTime()
        LOCAL cBuffer, nYear, nMonth, nDay,;
            nHour, nMinute, nSecond,;
            vStoredSet, cDate, cTime, tResult
    
        cBuffer = REPLICATE(CHR(0), 16)
        = GetSystemTime(@cBuffer)
    
        nYear = THIS.buf2word(SUBSTR(m.cBuffer,  1, 2))
        nMonth = THIS.buf2word(SUBSTR(m.cBuffer,  3, 2))  
        nDay = THIS.buf2word(SUBSTR(m.cBuffer,  7, 2))  
        nHour = THIS.buf2word(SUBSTR(m.cBuffer,  9, 2))  
        nMinute = THIS.buf2word(SUBSTR(m.cBuffer, 11, 2))  
        nSecond = THIS.buf2word(SUBSTR(m.cBuffer, 13, 2))
    
        vStoredSet = SET("DATE")
        SET DATE TO MDY
    
        cDate = STRTRAN(STR(nMonth,2) + "/" + STR(nDay,2) +; 
            "/" + STR(nYear,4), " ","0")
    
        cTime = STRTRAN(STR(nHour,2) + ":" + STR(nMinute,2) +; 
            ":" + STR(nSecond,2), " ","0")
    
        tResult = CTOT(m.cDate + " " + m.cTime)
        SET DATE TO (m.vStoredSet)
    RETURN m.tResult
    
    PROCEDURE declare
        GdiplusControl::declare
    
        DECLARE INTEGER ExtractAssociatedIcon IN shell32;
            INTEGER hInst, STRING lpIconPath, INTEGER @lpiIcon
    
        DECLARE INTEGER GetModuleFileName IN kernel32;
            INTEGER hModule, STRING @lpFilename, INTEGER nSize
    
        DECLARE INTEGER DestroyIcon IN user32 INTEGER hIcon
        DECLARE GetSystemTime IN kernel32 STRING @ lpSystemTime
    
    PROCEDURE InitBuffers
        IF VARTYPE(THIS.oBackBuffer) = "O"
            RETURN
        ENDIF
    
        THIS.InitClockface
        GdiplusControl::InitBuffers
    
    PROCEDURE InitClockface
        IF NOT EMPTY(THIS.ClockfaceFile) AND;
            FILE(THIS.ClockfaceFile)
            THIS.ClockfaceImage =;
                CREATEOBJECT("gdiimage", THIS.ClockfaceFile)
        ENDIF
    
        IF VARTYPE(THIS.ClockfaceImage) <> "O" OR;
            THIS.ClockfaceImage.imgwidth = 0
            THIS.CreateGenericClockface
        ENDIF
    
        IF VARTYPE(THIS.ClockfaceImage) = "O";
            AND THIS.ClockfaceImage.imgwidth > 0
            THIS.Width = THIS.ClockfaceImage.imgwidth
            THIS.Height = THIS.ClockfaceImage.imgheight
        ENDIF
    
    PROCEDURE CreateGenericClockface
        THIS.ClockfaceImage = CREATEOBJECT(;
            "gdibitmap", THIS.Width, THIS.Height)
    
        WITH THIS.ClockfaceImage
            .graphics.SmoothingMode = 2
    
            .graphics.FillEllipse(;
                ColorToARGB(ThisForm.BackColor, 255),;
                0, 0,;
                THIS.Width,;
                THIS.Height)
    
            .graphics.FillEllipse(;
                ColorToARGB(THIS.ClockfaceColor, 255),;
                1, 1,;
                THIS.Width - 2,;
                THIS.Height - 2)
    
            * the outer circle
            LOCAL objPen
    
            objPen = CREATEOBJECT(;
                "gdipen", ColorToARGB(THIS.RimColor, 255), 6)
    
            .graphics.DrawEllipse(;
                objPen,;
                4, 4,;
                THIS.Width - 8,;
                THIS.Height - 8)
    
            FOR nMinute=0 TO 59
                THIS.DrawMinuteDot(nMinute)
            ENDFOR
    
            .graphics.FillEllipse(;
                ColorToARGB(THIS.ClockfaceColor, 255),;
                16, 16,;
                THIS.Width - 32,;
                THIS.Height - 32)
    
        ENDWITH
    
    PROCEDURE DrawMinuteDot(nMinute)
        LOCAL nRad, nX, nY, nColor, nSize, objPen
    
        nRad = DTOR(nMinute * 6 + 90)
        nColor = ColorToARGB(THIS.RimColor, 255)
    
        IF MOD(nMinute, 5) = 0
        * larger dot
    
            nY = SIN(nRad) * (this.Height / 2) * 0.95
            nX = COS(nRad) * (this.Width / 2) * 0.95
    
            objPen = CREATEOBJECT(;
                "gdipen", nColor, 2)
    
            WITH THIS.ClockfaceImage
                .graphics.DrawLine(;
                    objPen,;
                    this.Width/2, this.Height/2,;
                    (this.Width/2) + nX,;
                    (this.Height/2) + nY)
            ENDWITH
    
        ELSE
        * regular dot
            nY = SIN(nRad) * (this.Height / 2) * 0.9
            nX = COS(nRad) * (this.Width / 2) * 0.9
            nSize = 2
    
            WITH THIS.ClockfaceImage
                .graphics.FillEllipse(;
                    nColor,;
                    (this.Width/2) + nX - (nSize/2),;
                    (this.Height/2) + nY - (nSize/2),;
                    nSize,;
                    nSize)
            ENDWITH
        ENDIF
    
    PROCEDURE DrawHourHand
        LOCAL nAngle
    
        nAngle = (60 * HOUR(THIS.CurrentTime) +;
            MINUTE(THIS.CurrentTime)) / 2
    
        THIS.DrawHand(;
                6,;
                ColorToARGB(THIS.HandColor, 255),;
                nAngle, 0.6)
    
    PROCEDURE DrawMinuteHand
        LOCAL nAngle
    
        nAngle= (MINUTE(THIS.CurrentTime) +;
            (SEC(THIS.CurrentTime)/60)) * 6
    
        THIS.DrawHand(;
                4,;
                ColorToARGB(THIS.HandColor, 255),;
                nAngle, 0.75)
    
    PROCEDURE DrawSecondHand
        LOCAL nColor
        nColor = ColorToARGB(THIS.SecondHandColor, 255)
    
        THIS.DrawHand(;
                2,;
                nColor,;
                SEC(THIS.CurrentTime) * 6, 0.90)
    
        WITH THIS.oBackBuffer
            .graphics.FillEllipse(;
                m.nColor,;
                this.Width/2-4,;
                this.Height/2-4,;
                8, 8)
    
            .graphics.FillEllipse(;
                ColorToARGB(THIS.RimColor, 255),;
                this.Width/2-1,;
                this.Height/2-1,;
                2, 2)
        ENDWITH
    
    PROCEDURE DrawHand(nWidth, nColor,;
                nAngle, nRadius)
    
        LOCAL nRad, nX, nY, objPen, objPen1
        nRad = DTOR(nAngle + 90)
    
        nY = SIN(nRad) * (this.Height / 2) * nRadius
        nX = COS(nRad) * (this.Width / 2) * nRadius
    
        objPen = CREATEOBJECT(;
            "gdipen", nColor, nWidth)
    
        objPen1 = CREATEOBJECT(;
            "gdipen", ColorToARGB(THIS.HandShadowColor, 96), nWidth)
    
        WITH THIS.oBackBuffer
            * casts a shadow
            .graphics.DrawLine(;
                objPen1,;
                this.Width/2, this.Height/2,;
                (this.Width/2) - nX * 0.975 + 2,;
                (this.Height/2) - nY * 0.975 + 2)
    
            .graphics.DrawLine(;
                objPen,;
                this.Width/2, this.Height/2,;
                (this.Width/2) - nX,;
                (this.Height/2) - nY)
        ENDWITH
    
    PROCEDURE DrawClockface
        IF VARTYPE(THIS.ClockfaceImage) = "O" AND;
            THIS.ClockfaceImage.imgwidth > 0
        * draws clock face image on the back buffer
            WITH THIS.oBackBuffer
                .graphics.DrawImage(;
                    THIS.ClockfaceImage,;
                    0, 0)
            ENDWITH
        ELSE
            WITH THIS.oBackBuffer
                .graphics.FillRectangle(;
                    ColorToARGB(THIS.ClockfaceColor, 255),;
                    0, 0, THIS.Width, THIS.Height)
            ENDWITH
        ENDIF
    
    PROCEDURE DrawClockfaceIcon
        IF VARTYPE(THIS.ClockfaceIcon) <> "O";
            OR THIS.ClockfaceIcon.imgwidth = 0
            RETURN
        ENDIF
    
        WITH THIS.oBackBuffer
            LOCAL nIconX, nIconY
            nIconX = THIS.Width/2 -;
                (THIS.ClockfaceIcon.imgwidth/2)
    
            nIconY = THIS.Height/2 -;
                THIS.ClockfaceIcon.imgheight - 10
    
            .graphics.DrawImage(;
                THIS.ClockfaceIcon,;
                nIconX,;
                nIconY)
        ENDWITH
    
    PROTECTED PROCEDURE FillInfoStrings()
        THIS.InfoStrings[1, 1] =;
            UPPER(PADR(CDOW(THIS.CurrentTime), 3)) + ", " +;
            PADR(CMONTH(THIS.CurrentTime), 3) + " " +;
            TRANSFORM(DAY(THIS.CurrentTime))
    
        cTime = TRANSFORM(THIS.CurrentTime)
    
        THIS.InfoStrings[2, 1] =;
            LOWER(SUBSTR(cTime, AT(" ", cTime) + 1))
    
        THIS.InfoStrings[3, 1] = THIS.ClockfaceCaption
    
    PROCEDURE DrawInfoStrings
        LOCAL oRect, nStartX, nStartY, cStr
    
        THIS.FillInfoStrings()
    
        IF VARTYPE(THIS.BodyFont) <> "O"
            THIS.BodyFont = CREATEOBJECT(;
                    "gdifont", "Arial", 8, 0,;
                    ColorToARGB(THIS.TimeTextColor, 255))
        ENDIF
    
        WITH THIS.oBackBuffer
            * measures the string
            * before centering it horizontally
            THIS.InfoStrings[1, 2] = .graphics.MeasureString(;
                THIS.InfoStrings[1, 1], THIS.BodyFont)
    
            THIS.InfoStrings[2, 2] = .graphics.MeasureString(;
                THIS.InfoStrings[2, 1], THIS.BodyFont)
    
            THIS.InfoStrings[3, 2] = .graphics.MeasureString(;
                THIS.InfoStrings[3, 1], THIS.BodyFont)
    
            nStartY = THIS.Height/2 +;
                (THIS.Height/2 -;
                 THIS.InfoStrings[1, 2].rheight * 2) / 2
    
            cStr = THIS.InfoStrings[1, 1]
            oRect = THIS.InfoStrings[1, 2]
            nStartX = THIS.Width/2 - oRect.rwidth/2
    
            .graphics.DrawText(;
                m.cStr,;
                THIS.BodyFont,;
                nStartX,;
                nStartY,;
                oRect.rwidth,;
                oRect.rheight)
    
            cStr = THIS.InfoStrings[2, 1]
            oRect = THIS.InfoStrings[2, 2]
            nStartX = THIS.Width/2 - oRect.rwidth/2
    
            .graphics.DrawText(;
                m.cStr,;
                THIS.BodyFont,;
                nStartX,;
                nStartY + oRect.rheight,;
                oRect.rwidth,;
                oRect.rheight)
    
            cStr = THIS.InfoStrings[3, 1]
            oRect = THIS.InfoStrings[3, 2]
            nStartX = THIS.Width/2 - oRect.rwidth/2
    
            .graphics.DrawText(;
                m.cStr,;
                THIS.BodyFont,;
                nStartX,;
                oRect.rheight * 2,;
                oRect.rwidth,;
                oRect.rheight)
        ENDWITH
    
    PROCEDURE DrawHands
        WITH THIS
            * center dot
            .oBackBuffer.graphics.FillEllipse(;
                ColorToARGB(THIS.HandColor, 255),;
                THIS.Width/2-8, THIS.Height/2-8,;
                16, 16)
    
            .DrawHourHand
            .DrawMinuteHand
            .DrawSecondHand
        ENDWITH
    
    PROCEDURE DrawOnBackBuffer(lEnabled as Boolean)
        WITH THIS
            .CurrentTime = .GetCurrentTime()
            .DrawClockface
            .DrawClockfaceIcon
            .DrawInfoStrings
            .DrawHands
        ENDWITH
    
    ENDDEFINE
    

    User rating: 10/10 (1 votes)
    Rate this code sample:
    • ~
    10874 bytes  
    Created: 2013-12-19 22:59:55  
    Modified: 2014-01-07 13:18:41  
    Visits in 7 days: 151  
    Listed functions:
    DestroyIcon
    ExtractAssociatedIcon
    GetModuleFileName
    GetSystemTime
    Printer friendly API declarations
    My comment:
    Wikipedia: Clock angle problem

    By default the drawing procedure produces a generic picture of a clock. Otherwise set the ClockfaceFile parameter to a valid image file of a clock face.

    Download one of these images and use it as a clock face (the ClockfaceFile parameter). Otherwise the instance will create a generic image of clock face.

    devianart.com   devianart.com

    Such background image should be well centered and with sufficiently good symmetry.

    * * *
    For a generic clock face, ideally all sizes and dimensions should derive from the radius. That would provide better scalability comparing to what this particular code sample can do having all sizes hard-coded.

    Ideally the clock image should be drawn by a dedicated thread, which requires FLL or ActiveX implementation.

    With VFP implementation, whenever the VFP process becomes busy with a task, this immediately affects the clock drawing procedure -- the movement of the seconds hand becomes less regular with some jumps and stops.

    Lower Timer Interval values, for example 16 milliseconds, may allow implementing completely smooth movement of the seconds hand. I.e. the hand does not jump from one second mark to the next one but rather rotates with a constant speed. But again this can only be done in ActiveX or FLL implementation, one that allows creating a dedicated drawing thread.
    Word Index links for this example:
    Translate this page:
      Spanish    Portuguese    German    French    Italian  
    FreeTranslation.com offers instant, free translations of text or web pages.
    User Contributed Notes:
    There are no notes on this subject.


    Copyright 2001-2018 News2News, Inc. Before reproducing or distributing any data from this site please ask for an approval from its owner. Unless otherwise specified, this page is for your personal and non-commercial use. The information on this page is presented AS IS, meaning that you may use it at your own risk. Microsoft Visual FoxPro and Windows are trade marks of Microsoft Corp. All other trademarks are the property of their respective owners. 

    Privacy policy
    Credits: PHP (4.4.9), an HTML-embedded scripting language, MySQL (5.6.38), the Open Source standard SQL database, AceHTML Freeware Version 4, freeware HTML Editor of choice.   Hosted by Korax Online Inc.
    Last Topics Visited (54.242.250.208)
    7 sec.Function: 'SQLAllocHandle'
    Function group: 'ODBC API'
    14 sec.Solutions
    20 sec.Function: 'SetTextColor'
    Function group: 'Font and Text'
    27 sec.Function: 'ntohs'
    Function group: 'Windows Sockets 2 (Winsock)'
    34 sec.Function: 'CeGetStoreInformation'
    Function group: 'Remote Application Programming (RAPI)'
    42 sec.Function: 'EnableWindow'
    Function group: 'Keyboard Input'
    48 sec.Example: 'Adding and deleting Scheduled Tasks using NetScheduleJob API functions'
    56 sec.Function: 'GdipCreateFromHDC2'
    Function group: 'GDI+ Graphics'
    1.03 min.Function: 'SHGetSpecialFolderLocation'
    Function group: 'Shell Functions'
    1.15 min.Function: 'InternetSetFilePointer'
    Function group: 'Internet Functions (WinInet)'
    Google
    Advertise here!