cgraphctx dont work with ddt anymore

Started by docroger, September 04, 2025, 06:12:41 AM

Previous topic - Next topic

docroger

Hello José,

With the last version of afxnova, my application dont work !
The graphs are not draw.

You gave me an example and it dont work to :

Here :
' ########################################################################################
' Microsoft Windows
' File: DDT_AdjustableArrowCapGetHeight.bas
' Contents: GDI+ - AdjustableArrowCapGetHeight example
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#define UNICODE
#INCLUDE ONCE "AfxNova/CGdiPlus.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
#INCLUDE ONCE "AfxNova/DDT.inc"
#INCLUDE ONCE "AfxNova/AfxExt.bi"   ' // optional: For dark mode
USING AfxNova

CONST IDC_GRCTX = 1001

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG

   END wWinMain(GetModuleHandleW(NULL), NULL, wCOMMAND(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

' ========================================================================================
' The following example creates an AdjustableArrowCap, myArrow, and sets the height of the
' cap. The code then creates a Pen, assigns myArrow as the ending line cap for the Pen,
' and draws a capped line. Next, the code gets the height of the arrow cap, creates a new
' arrow cap with height equal to the height of myArrow, assigns the new arrow cap as the
' ending line cap for the Pen, and draws another capped line.
' ========================================================================================
SUB Example_GetHeight (BYVAL hdc AS HDC)

   ' // Create a graphics object from the window device context
   DIM graphics AS CGpGraphics = hdc
   ' // Get the DPI scaling ratios
   DIM rxRatio AS SINGLE = graphics.GetDpiX / 96
   DIM ryRatio AS SINGLE = graphics.GetDpiY / 96
   ' // Set the scale transform
   graphics.ScaleTransform(rxRatio, ryRatio)

   ' // Create an AdjustableArrowCap with a height of 10 pixels
   DIM myArrow AS CGpAdjustableArrowCap = CGpAdjustableArrowCap(10, 10, TRUE)
   ' // Adjust to DPI by setting the scale width
   myArrow.SetWidthScale(rxRatio)

   ' // Create a Pen, and assign myArrow as the end cap
   DIM arrowPen AS CGpPen = ARGB_Violet
   arrowPen.SetCustomEndCap(@myArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 20, 100, 20)

   ' // Create a second arrow cap using the height of the first one
   DIM AS CGpAdjustableArrowCap otherArrow = CGpAdjustableArrowCap(myArrow.GetHeight, 20, TRUE)
   otherArrow.SetWidthScale(rxRatio)

   ' // Assign the new arrow cap as the end cap for arrowPen
   arrowPen.SetCustomEndCap(@otherArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 55, 100, 55)

END SUB
' ========================================================================================

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // The recommended way is to use a manifest.
   ' // Optional: Set process DPI aware.
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Optional: Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Create a new custom dialog using pixels
'   DIM hDlg AS HWND = DialogNewPixels("MyClassName", 0, "DDT - GraphCtx Test", 0, 0, 400, 250, WS_OVERLAPPEDWINDOW OR DS_CENTER)
   ' // Alternate way: Provides an exact control of the client size
   DIM hDlg AS HWND = DialogNewPixels("MyClassName", 0, "DDT - GraphCtx Test", 0, 0, 0, 0, WS_OVERLAPPEDWINDOW)
   DialogSetClient(hDlg, 400, 250)
   DialogCenter(hDlg)
   ' // Optional: Caption dark mode
   AfxEnableDarkModeForWindow(hDlg)

   ' // Add a graphic control
   DIM nWidth AS LONG = DialogGetClientWidth(hDlg)
   DIM nHeight AS LONG = DialogGetClientHeight(hDlg)
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
   ' // Make the control resizable
   pGraphCtx.Resizable = TRUE
   ' // Optional: Clear the graphic control background
   pGraphCtx.Clear RGB_FLORALWHITE
   ' // Get the memory device context of the graphic control
   DIM hdc AS HDC = pGraphCtx.GetMemDc

   ' // Pointer version:
'   DIM pGraphCtx AS CGraphCtx = NEW CGraphCtx(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
'   --or--
'   DIM pGraphCtx AS CGraphCtx PTR = ControlAddGraphic(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
'   pGraphCtx->Resizable = TRUE
'   pGraphCtx->Clear RGB_FLORALWHITE
'   DIM hdc AS HDC = pGraphCtx->GetMemDc

   ' // Draw the graphics
   Example_GetHeight(hdc)

   ' // Display and activate the dialog as modal
   DialogShowModal(hDlg, @DlgProc)

   ' // Pointer version: Delete the graphic control
 '  IF pGraphCtx THEN Delete pGraphCtx

   ' // Return the result code set with DialogEnd
   RETURN DialogEndResult(hDlg)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Dialog callback procedure
' ========================================================================================
FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

   SELECT CASE uMsg

      CASE WM_INITDIALOG
         RETURN TRUE

      ' // Optional: Theme has changed
'      CASE WM_THEMECHANGED
'         AfxEnableDarkModeForWindow(hDlg)

      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hDlg, WM_CLOSE, 0, 0
               END IF
         END SELECT

      CASE WM_CLOSE
         ' // End the application
         DialogEnd(hDlg)

   END SELECT

END FUNCTION
' ========================================================================================



No error message, just graph are not visible...

Any help ?

José Roca

Sorry, but I have been unable to get the graphic control to work properly with dialogs, so it is not longer supported. Resizing and stretching don't work properly, and the dialog refuses to display scroll bars. I have tried many things, even asking AI, but nothing has worked. Dialogs have many quirks with custom controls and, being a black box, it is not possible to know what they are doing internally. With CWindow everything works fine.

Frank Bruebach

#2
This graphx example IS working for me.. modified a little thats all..resizing works but Stretching I have No Idea.. I am using in General only SDK Modus winapi.

' ########################################################################################
' Microsoft Windows
' File: DDT_AdjustableArrowCapGetHeight.bas
' Contents: GDI+ - AdjustableArrowCapGetHeight example
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

'' 05-09-2025, frank bruebach, tiko, graphs are working here, scrollbar too

#define UNICODE
#define _WIN32_WINNT &h0602
#INCLUDE ONCE "AfxNova/CGdiPlus.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
#INCLUDE ONCE "AfxNova/DDT.inc"
#INCLUDE ONCE "AfxNova/AfxExt.bi"  ' // optional: For dark mode

USING AfxNova

CONST IDC_GRCTX = 1001


DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                          BYVAL hPrevInstance AS HINSTANCE, _
                          BYVAL pwszCmdLine AS WSTRING PTR, _
                          BYVAL nCmdShow AS LONG) AS LONG

  END wWinMain(GetModuleHandleW(NULL), NULL, wCOMMAND(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

' ========================================================================================
' The following example creates an AdjustableArrowCap, myArrow, and sets the height of the
' cap. The code then creates a Pen, assigns myArrow as the ending line cap for the Pen,
' and draws a capped line. Next, the code gets the height of the arrow cap, creates a new
' arrow cap with height equal to the height of myArrow, assigns the new arrow cap as the
' ending line cap for the Pen, and draws another capped line.
' ========================================================================================
SUB Example_GetHeight (BYVAL hdc AS HDC)

  ' // Create a graphics object from the window device context
  DIM graphics AS CGpGraphics = hdc
  ' // Get the DPI scaling ratios
  DIM rxRatio AS SINGLE = graphics.GetDpiX / 96
  DIM ryRatio AS SINGLE = graphics.GetDpiY / 96
  ' // Set the scale transform
  graphics.ScaleTransform(rxRatio, ryRatio)

  ' // Create an AdjustableArrowCap with a height of 10 pixels
  DIM myArrow AS CGpAdjustableArrowCap = CGpAdjustableArrowCap(10, 10, TRUE)
  ' // Adjust to DPI by setting the scale width
  myArrow.SetWidthScale(rxRatio)

  ' // Create a Pen, and assign myArrow as the end cap
  DIM arrowPen AS CGpPen = ARGB_BLUE 'ARGB_Violet
  DIM arrowPen2 AS CGpPen = ARGB_GREEN 'ARGB_Violet
 
  arrowPen.SetCustomEndCap(@myArrow)
  arrowPen2.SetCustomEndCap(@myArrow)
 

  ' // Draw a line using arrowPen
  'graphics.DrawLine(@arrowPen, 0, 20, 100, 20)
  graphics.DrawLine(@arrowPen, 10, 40, 120, 40)
  graphics.DrawLine(@arrowPen2, 40, 40, 120, 40)
 
  ' // Create a second arrow cap using the height of the first one
  DIM AS CGpAdjustableArrowCap otherArrow = CGpAdjustableArrowCap(myArrow.GetHeight, 20, TRUE)
  otherArrow.SetWidthScale(rxRatio)

  ' // Assign the new arrow cap as the end cap for arrowPen
  arrowPen.SetCustomEndCap(@otherArrow)
  arrowPen2.SetCustomEndCap(@otherArrow)
 
  ' // Draw a line using arrowPen
  graphics.DrawLine(@arrowPen, 0, 55, 100, 55)
  graphics.DrawLine(@arrowPen2, 0, 85, 120, 85)

END SUB
' ========================================================================================

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                  BYVAL hPrevInstance AS HINSTANCE, _
                  BYVAL pwszCmdLine AS WSTRING PTR, _
                  BYVAL nCmdShow AS LONG) AS LONG

  ' // The recommended way is to use a manifest.
  ' // Optional: Set process DPI aware.
  SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
  ' // Optional: Enable visual styles without including a manifest file
  AfxEnableVisualStyles

  ' // Create a new custom dialog using pixels
'  DIM hDlg AS HWND = DialogNewPixels("MyClassName", 0, "DDT - GraphCtx Test", 0, 0, 400, 250, WS_OVERLAPPEDWINDOW OR DS_CENTER)
  ' // Alternate way: Provides an exact control of the client size
  DIM hDlg AS HWND = DialogNewPixels("MyClassName", 0, "DDT - GraphCtx Test", 0, 0, 0, 0, WS_OVERLAPPEDWINDOW)
  DialogSetClient(hDlg, 450, 280)
  DialogCenter(hDlg)
  ' // Optional: Caption dark mode
  AfxEnableDarkModeForWindow(hDlg)

' // Make the dialog scrollable
  DialogSetViewPort(hDlg, 450, 260) '250, 260)
' // Center the window (must be done after shrinking the client size)
  'DialogCenter(hDlg)

  ' // Add a graphic control
  DIM nWidth AS LONG = DialogGetClientWidth(hDlg)
  DIM nHeight AS LONG = DialogGetClientHeight(hDlg)
  DIM pGraphCtx AS CGraphCtx = CGraphCtx(hDlg, IDC_GRCTX, "", 50, 50, nWidth, nHeight) '0, 0, nWidth, nHeight)
  ' // Make the control resizable
  pGraphCtx.Resizable = TRUE
  ' // Optional: Clear the graphic control background
  pGraphCtx.Clear RGB_FLORALWHITE
  ' // Get the memory device context of the graphic control
  DIM hdc AS HDC = pGraphCtx.GetMemDc

  ' // Pointer version:
'  DIM pGraphCtx AS CGraphCtx = NEW CGraphCtx(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
'  --or--
'  DIM pGraphCtx AS CGraphCtx PTR = ControlAddGraphic(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
'  pGraphCtx->Resizable = TRUE
'  pGraphCtx->Clear RGB_FLORALWHITE
'  DIM hdc AS HDC = pGraphCtx->GetMemDc

  ' // Draw the graphics
  Example_GetHeight(hdc)

  ' // Display and activate the dialog as modal
  DialogShowModal(hDlg, @DlgProc)

  ' // Pointer version: Delete the graphic control
 '  IF pGraphCtx THEN Delete pGraphCtx

  ' // Return the result code set with DialogEnd
  RETURN DialogEndResult(hDlg)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Dialog callback procedure
' ========================================================================================
FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

  SELECT CASE uMsg

      CASE WM_INITDIALOG
        RETURN TRUE

      ' // Optional: Theme has changed
'      CASE WM_THEMECHANGED
'        AfxEnableDarkModeForWindow(hDlg)

      CASE WM_COMMAND
        SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
              ' // If ESC key pressed, close the application by sending an WM_CLOSE message
              IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hDlg, WM_CLOSE, 0, 0
              END IF
        END SELECT

      CASE WM_CLOSE
        ' // End the application
        DialogEnd(hDlg)

  END SELECT

END FUNCTION
' ========================================================================================

' 05-09-2025, frank bruebach, tiko

And I add a scroll bar for x and y direction

Regards , Frank

docroger

Hello Franck,

Your code dont work for me with latest version AFXNova : i have an empty window.

Ill have to rework my apps : switch to window and abandon DDT mode.

Many work and the window syntax is less intuitive.

But José try and do his best.

AFXNova is a good lib.

José Roca

Frank is making the dialog scrollable, not the graphic control.

The best thing about SDK windows is that Windows does what you tell it to do, but dialogs interfere. They weren't designed to work as main windows.


The latest change that i have made to the graphic control is to get the scroll bars thumb propertly sized relative to the size of the virtual buffer.


José Roca

It's what you get used to. The syntax is the least of it. You get used to it quickly. What does change is that messages are processed a little differently. However, I have added and continue to add features that make the job easier. For example, with the control anchoring system, you don't have to worry about processing WM_SIZE. It's very easy to subclass using SetControlSubclass, and a couple of days ago I added SetControlCallback, which allows you to redirect WM_COMMAND and WM_NOTIFY messages to the designated callback procedure, as DDT allows with CALL CALLBACK.

Frank Bruebach

Hello Jose . How did you make the graphic Control scrollable?


José Roca

#7
Anchor the control: pWindow.AnchorControl(IDC_GRCTX, AFX_ANCHOR_HEIGHT_WIDTH)


' ########################################################################################
' Microsoft Windows
' File: AdjustableArrowCapGetHeight.bas
' Contents: GDI+ - AdjustableArrowCapGetHeight example
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#define UNICODE
#INCLUDE ONCE "AfxNova/CGdiPlus.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
USING AfxNova

CONST IDC_GRCTX = 1001

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG

   END wWinMain(GetModuleHandleW(NULL), NULL, wCOMMAND(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

' ========================================================================================
' The following example creates an AdjustableArrowCap, myArrow, and sets the height of the
' cap. The code then creates a Pen, assigns myArrow as the ending line cap for the Pen,
' and draws a capped line. Next, the code gets the height of the arrow cap, creates a new
' arrow cap with height equal to the height of myArrow, assigns the new arrow cap as the
' ending line cap for the Pen, and draws another capped line.
' ========================================================================================
SUB Example_GetHeight (BYVAL hdc AS HDC)

   ' // Create a graphics object from the window device context
   DIM graphics AS CGpGraphics = hdc
   ' // Get the DPI scaling ratios
   DIM rxRatio AS SINGLE = graphics.GetDpiX / 96
   DIM ryRatio AS SINGLE = graphics.GetDpiY / 96
   ' // Set the scale transform
   graphics.ScaleTransform(rxRatio, ryRatio)

   ' // Create an AdjustableArrowCap with a height of 10 pixels
   DIM myArrow AS CGpAdjustableArrowCap = CGpAdjustableArrowCap(10, 10, TRUE)
   ' // Adjust to DPI by setting the scale width
   myArrow.SetWidthScale(rxRatio)

   ' // Create a Pen, and assign myArrow as the end cap
   DIM arrowPen AS CGpPen = ARGB_Violet
   arrowPen.SetCustomEndCap(@myArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 20, 100, 20)

   ' // Create a second arrow cap using the height of the first one
   DIM AS CGpAdjustableArrowCap otherArrow = CGpAdjustableArrowCap(myArrow.GetHeight, 20, TRUE)
   otherArrow.SetWidthScale(rxRatio)

   ' // Assign the new arrow cap as the end cap for arrowPen
   arrowPen.SetCustomEndCap(@otherArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 55, 100, 55)

END SUB
' ========================================================================================

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Create the main window
   DIM pWindow AS CWindow = "MyClassName"
   pWindow.Create(NULL, "GDI+ AdjustableArrowCapGetHeight", @WndProc)
   ' // Size it by setting the wanted width and height of its client area
   pWindow.SetClientSize(400, 250)
   ' // Center the window
   pWindow.Center

   ' // Add a graphic control
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
   ' // Make it smaller that the client size to no confuse it with the main window
'   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "", 0, 0, pWindow.ClientWidth - 20, pWindow.ClientHeight- 20)
   pGraphCtx.Clear RGB_FLORALWHITE
   ' // Get the memory device context of the graphic control
   DIM hdc AS HDC = pGraphCtx.GetMemDc
   ' // Anchor the control
   pWindow.AnchorControl(IDC_GRCTX, AFX_ANCHOR_HEIGHT_WIDTH)

   ' // Draw the graphics
   Example_GetHeight(hdc)

   ' // Displays the window and dispatches the Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Main window procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

   SELECT CASE uMsg

      ' // If an application processes this message, it should return zero to continue
      ' // creation of the window. If the application returns –1, the window is destroyed
      ' // and the CreateWindowExW function returns a NULL handle.
      CASE WM_CREATE
         AfxEnableDarkModeForWindow(hwnd)
         RETURN 0

      ' // Theme has changed
      CASE WM_THEMECHANGED
         AfxEnableDarkModeForWindow(hwnd)
         RETURN 0

      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  RETURN 0
               END IF
         END SELECT

        CASE WM_DESTROY
         ' // Ends the application by sending a WM_QUIT message
         PostQuitMessage(0)
         RETURN 0

   END SELECT

   ' // Default processing of Windows messages
   FUNCTION = DefWindowProcW(hwnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================

Frank Bruebach


hello jose, thanks for your last example, but there's missing in winmain() part the scrollable graphctx control
this works however...

...
' // Create the main window
   DIM pWindow AS CWindow = "MyClassName"
   pWindow.Create(NULL, "GDI+ AdjustableArrowCapGetHeight", @WndProc)
   ' // Size it by setting the wanted width and height of its client area
   pWindow.SetClientSize(500, 350)
   ' // Make the window scrollable '----> you have forgotten to make the control scollable ;)
   pWindow.SetViewPort(420, 320)
   
   ' // Center the window
   pWindow.Center

second part..

it was just an experiment again with DDT..

looks at my part function DlgProc() below... CASE WM_INITDIALOG,  CASE WM_VSCROLL, WM_HSCROLL

perhaps you have an idea to manage and building the scrollable graphctx control...
if I am maximizing the dialog window there is a scrollbar in horizontal way for the graphctx control, not bad isn't it?
regards, frank



' ########################################################################################
' Microsoft Windows
' File: DDT_AdjustableArrowCapGetHeight.bas
' Contents: GDI+ - AdjustableArrowCapGetHeight example
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

'' 06-09-2025, frank bruebach, tiko, experiment ddt graphx control scrollbar

#define UNICODE
#define _WIN32_WINNT &h0602
#INCLUDE ONCE "AfxNova/CGdiPlus.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
#INCLUDE ONCE "AfxNova/DDT.inc"
#INCLUDE ONCE "AfxNova/AfxExt.bi"   ' // optional: For dark mode

USING AfxNova

CONST IDC_GRCTX = 1001

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG

   END wWinMain(GetModuleHandleW(NULL), NULL, wCOMMAND(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

' ========================================================================================
' The following example creates an AdjustableArrowCap, myArrow, and sets the height of the
' cap. The code then creates a Pen, assigns myArrow as the ending line cap for the Pen,
' and draws a capped line. Next, the code gets the height of the arrow cap, creates a new
' arrow cap with height equal to the height of myArrow, assigns the new arrow cap as the
' ending line cap for the Pen, and draws another capped line.
' ========================================================================================
SUB Example_GetHeight (BYVAL hdc AS HDC)

   ' // Create a graphics object from the window device context
   DIM graphics AS CGpGraphics = hdc
   ' // Get the DPI scaling ratios
   DIM rxRatio AS SINGLE = graphics.GetDpiX / 96
   DIM ryRatio AS SINGLE = graphics.GetDpiY / 96
   ' // Set the scale transform
   graphics.ScaleTransform(rxRatio, ryRatio)

   ' // Create an AdjustableArrowCap with a height of 10 pixels
   DIM myArrow AS CGpAdjustableArrowCap = CGpAdjustableArrowCap(10, 10, TRUE)
   ' // Adjust to DPI by setting the scale width
   myArrow.SetWidthScale(rxRatio)

   ' // Create a Pen, and assign myArrow as the end cap
   DIM arrowPen AS CGpPen = ARGB_BLUE 'ARGB_Violet
   DIM arrowPen2 AS CGpPen = ARGB_GREEN 'ARGB_Violet
   
   arrowPen.SetCustomEndCap(@myArrow)
   arrowPen2.SetCustomEndCap(@myArrow)
   
   ' // Draw a line using arrowPen
   'graphics.DrawLine(@arrowPen, 0, 20, 100, 20)
   graphics.DrawLine(@arrowPen, 10, 40, 120, 40)
   graphics.DrawLine(@arrowPen2, 40, 40, 120, 40)
   
   ' // Create a second arrow cap using the height of the first one
   DIM AS CGpAdjustableArrowCap otherArrow = CGpAdjustableArrowCap(myArrow.GetHeight, 20, TRUE)
   otherArrow.SetWidthScale(rxRatio)

   ' // Assign the new arrow cap as the end cap for arrowPen
   arrowPen.SetCustomEndCap(@otherArrow)
   arrowPen2.SetCustomEndCap(@otherArrow)
   
   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 55, 100, 55)
   graphics.DrawLine(@arrowPen2, 0, 85, 120, 85)

END SUB
' ========================================================================================

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // The recommended way is to use a manifest.
   ' // Optional: Set process DPI aware.
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Optional: Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Create a new custom dialog using pixels
'   DIM hDlg AS HWND = DialogNewPixels("MyClassName", 0, "DDT - GraphCtx Test", 0, 0, 400, 250, WS_OVERLAPPEDWINDOW OR DS_CENTER)
   ' // Alternate way: Provides an exact control of the client size
   DIM hDlg AS HWND = DialogNewPixels("MyClassName", 0, "DDT - GraphCtx Test", 0, 0, 0, 0, WS_OVERLAPPEDWINDOW)
   DialogSetClient(hDlg, 450, 280)
   DialogCenter(hDlg)
   ' // Optional: Caption dark mode
   AfxEnableDarkModeForWindow(hDlg)

' // Make the dialog scrollable
   DialogSetViewPort(hDlg, 450, 260) '250, 260)
' // Center the window (must be done after shrinking the client size)
   'DialogCenter(hDlg)

   ' // Add a graphic control
   DIM nWidth AS LONG = DialogGetClientWidth(hDlg)
   DIM nHeight AS LONG = DialogGetClientHeight(hDlg)
   'new
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(hDlg, IDC_GRCTX, "", 50, 50, nWidth, nHeight, WS_CHILD OR WS_VISIBLE OR WS_VSCROLL OR WS_HSCROLL)
   
   ' // Make the control resizable
   pGraphCtx.Resizable = TRUE
   ' // Optional: Clear the graphic control background
   pGraphCtx.Clear RGB_FLORALWHITE
   ' // Get the memory device context of the graphic control
   DIM hdc AS HDC = pGraphCtx.GetMemDc

   ' // Pointer version:
'   DIM pGraphCtx AS CGraphCtx = NEW CGraphCtx(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
'   --or--
'   DIM pGraphCtx AS CGraphCtx PTR = ControlAddGraphic(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
'   pGraphCtx->Resizable = TRUE
'   pGraphCtx->Clear RGB_FLORALWHITE
'   DIM hdc AS HDC = pGraphCtx->GetMemDc

   ' // Draw the graphics
   Example_GetHeight(hdc)

   ' // Display and activate the dialog as modal
   DialogShowModal(hDlg, @DlgProc)

   ' // Pointer version: Delete the graphic control
 '  IF pGraphCtx THEN Delete pGraphCtx

   ' // Return the result code set with DialogEnd
   RETURN DialogEndResult(hDlg)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Dialog callback procedure
' ========================================================================================
FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

   SELECT CASE uMsg

      CASE WM_INITDIALOG
      ' // Optional: Theme has changed
'      CASE WM_THEMECHANGED
'         AfxEnableDarkModeForWindow(hDlg)

'' new -------------------------------------------------- //
    DIM hGraphCtx AS HWND = GetDlgItem(hDlg, IDC_GRCTX)
    SetScrollRange(hGraphCtx, SB_VERT, 0, 100, TRUE) ' Vertical scrollbar range
    SetScrollRange(hGraphCtx, SB_HORZ, 0, 100, TRUE) ' Horizontal scrollbar range
    SetScrollPos(hGraphCtx, SB_VERT, 0, TRUE)
    SetScrollPos(hGraphCtx, SB_HORZ, 0, TRUE)
    RETURN TRUE
'' new -------------------------------------------------- //

      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hDlg, WM_CLOSE, 0, 0
               END IF
         END SELECT
         
'' new -------------------------------------------------- //
CASE WM_VSCROLL, WM_HSCROLL
    DIM hScroll AS HWND = GetDlgItem(hDlg, IDC_GRCTX)
    IF hScroll = CAST(HWND, lParam) THEN
        DIM nScrollCode AS INTEGER = LOWORD(wParam)
        DIM nPos AS INTEGER = HIWORD(wParam)

        DIM nBar AS INTEGER '= GET_WM_SCROLL_BAR(wParam) ' ???
        STATIC yPos AS INTEGER = 0
        STATIC xPos AS INTEGER = 0
        SELECT CASE nScrollCode
            CASE SB_LINEUP: yPos -= 5
            CASE SB_LINEDOWN: yPos += 5
            CASE SB_PAGEUP: yPos -= 20
            CASE SB_PAGEDOWN: yPos += 20
            CASE SB_THUMBTRACK: yPos = nPos
            CASE SB_LEFT: xPos -= 5
            CASE SB_RIGHT: xPos += 5
            CASE SB_PAGELEFT: xPos -= 20
            CASE SB_PAGERIGHT: xPos += 20
            CASE SB_THUMBPOSITION: xPos = nPos
        END SELECT
        yPos = MAX(0, MIN(yPos, 100))
        xPos = MAX(0, MIN(xPos, 100))
        SetScrollPos(hScroll, nBar, IIF(nBar = SB_VERT, yPos, xPos), TRUE)
        InvalidateRect(hScroll, NULL, TRUE)
        RETURN TRUE
    END IF
'' new -------------------------------------------------- //

'CASE WM_PAINT
'    DIM ps AS PAINTSTRUCT
'    DIM hGraphCtx AS HWND
'    DIM hdc AS HDC = BeginPaint(hGraphCtx, @ps)
'    ' // Get scroll positions
'    DIM yPos AS INTEGER = GetScrollPos(hGraphCtx, SB_VERT)
'    DIM xPos AS INTEGER = GetScrollPos(hGraphCtx, SB_HORZ)
'    ' // Create a GDI+ graphics object from the HDC
'    DIM graphics AS CGpGraphics = hdc
'    ' // Translate the graphics object by the scroll offset
'    graphics.TranslateTransform(-xPos, -yPos)
'    ' // Draw your graphics here
'    Example_GetHeight(hdc)
'    EndPaint(hGraphCtx, @ps)
'    RETURN TRUE

      CASE WM_CLOSE
         ' // End the application
         DialogEnd(hDlg)

   END SELECT

END FUNCTION
' ========================================================================================

' 06-09-2025, frank bruebach, tiko

José Roca

#9
> hello jose, thanks for your last example, but there's missing in winmain() part the scrollable graphctx control

I haven't forgotten anything. By default, the graphic control is auto-scrollable automatically if you resize it (unless you set to true the Resizable or Stretchable properties). You can either resize the graphic control processing WM_SIZE and using MoveWindow or SetWindowPos) or letting CWindow to do it automatically by anchoring the control with pWindow.AnchorControl(IDC_GRCTX, AFX_ANCHOR_HEIGHT_WIDTH). SetViewPort is to make the main window (not the graphic control) scrollable. They are different things. SetViewPort is to be used if the content does not fit in the available client area of the main window.

In SDK programming, main window or top window is what you will call dialog in DDT.

Also do not confuse Resizable with Scrollable. As stated inthe documentation, "If resizable, the virtual buffer is set to the size of the control. If the control is made smaller and then bigger, part of the contents are lost. Therefore, the caller must redraw it. Resizable and stretchable are mutually exclusive."

José Roca

Thanks for the scroll code, but the one used by the control is correct; otherwise, it won't work when used with CWindow. The problem is that dialogs refuse to show the scroll bars of the control. To begin with, they don't send the WM_SIZE message to the control, but even when processing WM_SIZE and forwarding the message to the control and even using ShowScrollbar, they aren't displayed. Dialogs work fine with standard Windows controls, but with custom controls there are always problems.