No announcement yet.

Custom Control Not Painting

  • Filter
  • Time
  • Show
Clear All
new posts

  • Custom Control Not Painting

    Hello again, I'm back with more DDT-Custom Control questions. Actually, I have only one:
    Why doesn't it paint?! Here's the setup:

    The control won't paint anything except the background color (white). The control sends
    & receives messages, responds to events and executes functions but no painting! For example,
    if I right-click on the control area, it sends the SHOW_PROPERTIES_PAGE message, telling me
    that the user wants to see the Properties page. But when I call the "ShowPropertiesPage()"
    function the properties page doesn't show up!

    Spy++ shows that the control is created, has a hWnd, a Thread ID, a parent hWnd and as I
    mentioned processes events & messages OK. The control window has the WS_GROUP, WS_VISIBLE,
    WS_TABSTOP and %WS_CHILD attributes, nothing unnusual there.

    Here's the DDT control-creation line I'm using:
       Control Add "ClassName", hfrmMain, %ID_CUSTOM, "Title", 1,50,370,180, _
    The control itself handles all painting issues and has it's own window message procedure,
    so there's very little for the host app to do. So I'm puzzled (certainly not the first time )
    as to why there's no painting.

    Any ideas on what might be causing this, or how I could investigate further?


    Mark Newman
    Mark Newman

  • #2
    Think we need to see some code. If big, maybe just your WM_PAINT
    procedure will do. Do you use InvalidateRect and UpdateWindow to
    actually force it to repaint when needed, like after adding/changing
    stuff in the control? Have you checked your DC's in WM_PAINT, if that
    is where all painting takes place?



    • #3
      Okay, the code follows. But to answer your questions first: According to the demo program source
      and the documentation, there's no need to handle the WM_PAINT message in the main program message
      pump. Since all interaction with the control is handled via function calls to the control, it
      can handle all the painting tasks. I presume that the window procedure of the control is seeing
      the messages it needs to accomplish that task. The only window-related message handling that I've
      seen in the demo C source is WM_SIZE.

      Having said that, I'll try using the InvalidateRect and UpdateWindow functions to see if that makes
      any difference.

      Onto the source: This is a pared-down version of the actual code I'm running, though not by much.
      The control itself is a charting component, if that helps. The function names have been changed
      enough to protect the copyright holder . The core logic is lifted straight from a C demo program.
      #Compile Exe "demo.exe"
      #Dim All
      #Register None
      #Include  ""
      ' Charting component declares, UDTs, equates, etc.
      Declare Function ChartCreate Lib "CHART.DLL" Alias "ChartCreate" () As Dword 
      Declare Function ChartAttachWindow Lib "CHART.DLL" Alias "ChartAttachWindow" (hChart As Dword, hWnd As Long) As Long
      Declare Function ChartDataCreateFromFile Lib "CHART.DLL" Alias "ChartDataCreateFromFile" (sFile As Asciiz, sErrBuff As Asciiz) As Dword
      Declare Sub ChartSetValues Cdecl Lib "CHART.DLL" Alias "ChartSetValues" (hChart As Dword, _
      	lProp As Long, lValue As Dword, lNull As Long)
      Declare Sub ChartCallAction Lib "CHART.DLL" Alias "ChartCallAction" (hChart As Dword, Action As Long, X As Long, Y As Long)  
      %CHART_REPAINTED               = %WM_USER + 3001
      %CHART_PROPERTIES              = %WM_USER + 3007
      Type ChartCallbackStruct
         X                           As Long 					' --read only--
         Y                           As Long              ' --read only--
      End Type
      ' For comparison, here's the original C declares for the above:
      ' typedef struct HCHART__ { int unused; } FAR* HCHART;
      ' typedef struct DataHandle__ { int unused; } FAR* DataHandle;
      ' extern __declspec( dllimport ) HCHART ChartCreate(void);
      ' extern __declspec( dllimport ) BOOL   ChartAttachWindow(HCHART, HWND);
      ' extern __declspec( dllimport ) DataHandle       ChartDataCreateFromFile(char *, char *);
      ' extern __declspec( dllimport ) void   CDECL ChartSetValues(HCHART handle, ... );
      ' extern __declspec( dllimport ) void   ChartCallAction(HCHART handle, long action, long x, long y);
      ' Control DDT ID
      %ID_CHART                      = 125
      ' Program functions
      Declare Callback Function FormProcess
      Declare Sub ShowDialog(ByVal hParent As Long)
      Global hDlg                    As Long
      Global ghChart                 As Dword 
      Global my_data                 As Dword 
      ' *************************************************************
      Function PbMain
      ' Program start
         Local lCount As Long
         ShowDialog 0
            Dialog Doevents To lCount
         Loop Until lCount = 0
         ' Free allocated memory
         ChartDestroy ByVal ghChart
      End Function
      ' *************************************************************
      Sub ShowDialog(ByVal hParent As Long)
         Local lStyle As Long, lExStyle As Long
         lExStyle = 0
         Dialog New hParent, "Charting Demo Using PB", 0, 0, 379,  246, lStyle, lExStyle To hDlg
         ' Add the Charting control
         Control Add "ChartClassName", hDlg, %ID_CHART, "Chart", 1,50,370,180, _
         Dialog Show Modeless hDlg , Call frmMain_DLGPROC
      End Sub
      ' *************************************************************
      Callback Function FormProcess
         Local  lCallback    As ChartCallbackStruct Ptr
         Static hwndChart    As Long
         Select Case CBMSG
            Case %WM_INITDIALOG		
               Control Handle hDlg, %ID_CHART To hwndChart 
               ' Creates the chart structures, returns a handle to them
               ghChart = ChartCreate()
               ' Attaches the chart structures to the chart window
               lTmp = ChartAttachWindow(ByVal ghChart, ByVal hwndChart)
               ' Loads a demo chart X-Y data file, returns a handle to the data
               my_data = ChartDataCreateFromFile("mm63.dat", $NUL)
               ' Attaches the loaded data to the chart
               ChartSetValues ghChart, %CHART_DATA, ByVal my_data, %NULL
            Case %WM_SIZE
               Local lWidth As Long, lHeight As Long, lTmp As Long
               Local hRect As RECT
               lWidth = LoWrd(Cblparam)
               lHeight = HiWrd(Cblparam)
               lTmp = GetWindowRect(hwndChart, hRect)
               lTmp = SetWindowPos(hwndChart, %NULL, hRect.nleft, hRect.ntop, _
      	     lWidth - 3, lHeight - hRect.ntop + 190, %SWP_NOMOVE)
            Case %CHART_REPAINTED
               ' Message is sent AFTER the control has been repainted; according to the documentation
               ' no action is required here. 
            Case %CHART_PROPERTIES
               ' Message is sent when a right-mouse click occurs within the control's area;
               ' requests that the Properties Page be displayed. CBLPARAM holds a pointer to a 
               ' ChartCallbackStruct UDT (defined above)
               lCallback = CBLPARAM
               ' Display the Properties Page at the X-Y coordinates (from the callback)
               ChartCallAction ByVal ghChart, ByRef %CHART_ACTION_PROPERTIES, @lCallback.X, @lCallback.Y 
            Case %WM_CLOSE
               ' Close the chart data 
               ChartDataDestroy my_data
            Case %WM_DESTROY
               PostQuitMessage 0
            Case %WM_PAINT
            Case %WM_COMMAND
            Case Else
         End Select
         ' Allow the default processor to run, even though it slows things down a bit
         FUNCTION = %FALSE
      End Function
      Mark Newman

      [This message has been edited by Mark Newman (edited March 29, 2001).]
      Mark Newman


      • #4
        Okay, now I remember. It was an external DLL. Well, I see one thing:
        Type ChartCallbackStruct
          hdc           As Long                                  ' --read only--
          rectDamaged   As RECT              ' --read only--
        End Type
        Local  lCallback As ChartCallbackStruct Ptr
          lCallback = CBLPARAM
          ChartCallAction ByVal ghChart, ByRef %CHART_ACTION_PROPERTIES, @lCallback.X, @lCallback.Y
        Now - where is X and Y in ChartCallbackStruct?
        Try @lCallback.rectDamaged.nRight, @lCallback.rectDamaged.nBottom
        or, maybe @lCallback.rectDamaged.nLeft, @lCallback.rectDamaged.nTop

        Also, providing full path and filename to a file is usually safer.



        • #5
          Oops! In my "sanitizing" of the code I copied the wrong structure. The correct one
          does have just an X and Y value (code sample has been edited). The actual X-Y values
          are reasonable, following the mouse position perfectly.

          Also, the data file is being loaded okay; the function returns a NULL on any errors
          and the return value is always non-NULL. I checked it with an invalid filename and
          it did return NULL.

          Thanks for looking at the code!

          Mark Newman
          Mark Newman


          • #6
            Just a thought - did you translate the headers yourself? Should not
            some members there be declared as "BYVAL handle AS LONG", etc? Also,
            that final FUNCTION = %FALSE for entire callback looks strange.
            Maybe only needed in the actual %CHART_ events?



            • #7
              Yes, indeed I did translate the headers. Are they correct? Who knows?!

              I thought that using DWORDs in place of LONGs for things like window handles
              was "technically preferable" though in reality it shouldn't matter in most cases.

              I've been experimenting with BYVAL and BYREF - kinda odd, in some functions the
              control wants the handle passed BYVAL, in others BYREF. Hmmm.

              As to the window proc returning %FALSE, I thought that allowing the default
              Windows message proc to run regardless of actions taken in the callback procedure
              wasn't harmful, just consumed extra time. I'll try enabling the default processor
              only for unhandled messages.


              Mark Newman
              Mark Newman