Click here to Skip to main content
15,888,610 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
It seems like an insurmountable secret, but there is little to nothing about how to get the left margin of a richtextbox, below what I tried:

What I have tried:

Private Function GetSelectionMarginWidth() As Integer
      Const EM_GETRECT As Integer = &HB2
      Const EM_GETMARGINS As Integer = &HD4
      Dim margins As New RECT()
      Dim lParam As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(margins))
      Marshal.StructureToPtr(margins, lParam, False)
      SendMessage(Me.Handle, EM_GETRECT, New IntPtr(EM_GETMARGINS), lParam)
      margins = Marshal.PtrToStructure(lParam, GetType(RECT))
      Marshal.FreeHGlobal(lParam)
      Return margins.Left
  End Function

  <DllImport("user32.dll", CharSet:=CharSet.Auto)>
  Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
  End Function

  Public Structure RECT
      Public Left As Integer
      Public Top As Integer
      Public Right As Integer
      Public Bottom As Integer
  End Structure


Everything seems perfect but the left margin always gives me 0, while the other values are correct, where am I wrong? Thanks and sorry for the bad English...
Posted
Updated 15-Apr-24 5:52am
v2

You are using the EM_GETRECT message with the EM_GETMARGINS flag, which retrieves the formatting rectangle of the text in the rich text box, not the margins themselves - MS Learn | EM_GETMARGINS[^]

To get the left margin of your richtextbox, you should use the 'EM_GETMARGINS' message directly, and retrieve the values from your 'RECT' structure returned by the message -
VB
Private Function GetSelectionMarginWidth() As Integer
    Const EM_GETMARGINS As Integer = &HD4
    Dim margins As New RECT()
    Dim lParam As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(margins))
    SendMessage(Me.Handle, EM_GETMARGINS, IntPtr.Zero, lParam)
    margins = Marshal.PtrToStructure(lParam, GetType(RECT))
    Marshal.FreeHGlobal(lParam)
    Return margins.Left
End Function

<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function

Public Structure RECT
    Public Left As Integer
    Public Top As Integer
    Public Right As Integer
    Public Bottom As Integer
End Structure
 
Share this answer
 
Comments
10071962 16-Apr-24 0:17am    
Thank you for your help, but it doesn't work, debugging gives me meaningless values:

909586995
892612915
875574835
3211315
892940345
3145785
892679992
3604536
3407923
892940345
909586995
909586995
925905971
3145785
3538995
842609976
3276857
3735608
942880051
3276851
Andre Oosthuizen 17-Apr-24 11:42am    
These values are actually not meaningless, they are memory addresses or pointers returned that are being used internally by the SendMessage API call. "Doesn't work" is actually meaningless :) as you need to be specific on what does not work.
10071962 17-Apr-24 11:59am    
It doesn't work, it was referring to the GetSelectionMarginWidth function, from which I expected an integer value as the width of the selection border... Now if I may, is there a way to extract this blessed width of the selection border from these memory addresses or pointers? The method I posted below doesn't seem very professional to me. Sorry again for the Google English...
Andre Oosthuizen 17-Apr-24 12:59pm    
Using the values from the pointers is not entirely possible and WAY over even my head. Without testing, you can try the following but I personally think that is just paving the way for even more questions and less answers -
To calculate the width of the selection border indirectly you can use the information given by 'EM_GETMARGINS' and some additional Windows messages and properties -
Left and right margins using 'EM_GETMARGINS' -
Dim margins As New RECT()
Dim lParam As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(margins))
SendMessage(Me.Handle, EM_GETMARGINS, IntPtr.Zero, lParam)
margins = Marshal.PtrToStructure(lParam, GetType(RECT))
Marshal.FreeHGlobal(lParam)
Dim leftMargin As Integer = margins.Left
Dim rightMargin As Integer = margins.Right


Width of the rich text box control's client area by subtracting the left and right margins from the control's width -
Dim clientAreaWidth As Integer = Me.RichTextBox1.Width - leftMargin - rightMargin


Width of the rich text box control itself (including borders) by sending the 'EM_GETRECT' message -
Dim controlRect As New RECT()
Dim rectParam As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(controlRect))
SendMessage(Me.Handle, EM_GETRECT, 0, rectParam)
controlRect = Marshal.PtrToStructure(rectParam, GetType(RECT))
Marshal.FreeHGlobal(rectParam)
Dim controlWidth As Integer = controlRect.Right - controlRect.Left


Lastly, calculate the width of the selection border by subtracting the client area width from the control's width -
Dim selectionBorderWidth As Integer = controlWidth - clientAreaWidth
10071962 18-Apr-24 0:35am    
Thanks for your time, I did some testing and from what I saw the only correct value when it doesn't throw an exception is clientAreaWidth. For the rest it gives me the usual memory pointer values (I learned). This is what I did, it's highlighted where it sometimes throws an exception. I use the function directly in the derived class and I don't want this to be the problem, but I tried in the form and the result changes little, i.e. the values seem lower. I didn't think it was so complicated to have a value that should be accessible by default.

Private Function GetSelectionMarginWidth() As Integer

Try

Dim margins As New RECT()
Dim lParam As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(margins))
SendMessage(Me.Handle, EM_GETMARGINS, IntPtr.Zero, lParam)
margins = Marshal.PtrToStructure(lParam, GetType(RECT))
Marshal.FreeHGlobal(lParam)
Dim leftMargin As Integer = margins.Left
Dim rightMargin As Integer = margins.Right

'*** exception ***
Dim clientAreaWidth As Integer = Me.Width - leftMargin - rightMargin
'*****************

Dim controlRect As New RECT()
Dim rectParam As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(controlRect))
SendMessage(Me.Handle, EM_GETRECT, 0, rectParam)
controlRect = Marshal.PtrToStructure(rectParam, GetType(RECT))
Marshal.FreeHGlobal(rectParam)
Dim controlWidth As Integer = controlRect.Right - controlRect.Left

Dim selectionBorderWidth As Integer = controlWidth - clientAreaWidth

Return selectionBorderWidth 'controlWidth - clientAreaWidth

Catch ex As Exception
Return 0
End Try

Return 0

End Function
Since this topic is taboo, I post a possible solution, it works quite well, if ShowSelectionMargin = false returns 1, otherwise the value of the left selection margin which increases if we add SelectionIndent. I hope it helps anyone in my situation:

Private Function GetSelectionMarginWidth() As Integer
       Dim CharIndex As Integer = GetCharIndexFromPosition(New Point(0, 0))
       Dim CharPos As Point = GetPositionFromCharIndex(CharIndex)
       Return CharPos.X
   End Function
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900