Control mitteilen, dass es ClientSize neu berechnen soll

Shakie

Erfahrenes Mitglied
Ich habe ein Control, dessen Rand ich selber zeichne. Dafür ändere ich den ClientBereich des Controls, wie hier beschrieben. Funktioniert auch soweit.
Nun möchte ich aber die Dicke des Randes zur Laufzeit ändern, d.h. einmal soll ein dicker Rand gezeichnet werden, später vielleicht ein dünner Rand. Das funktioniert nur zur Hälfte: der Rand wird zwar gezeichnet, wenn ich seine Dicke ändere (im Code die Variable "Randbreite"), aber die Child-Controls von meinem Control werden nicht neu angeordnet. Sie sitzen daraufhin im Rand!
Code zum Ändern der ClientSize sieht ungefähr so aus:
Code:
Private Sub AdjustClientRect(ByRef rect As RECT)
    rect.Top += Randbreite
    rect.Left += Randbreite
    rect.Right -= Randbreite
    rect.Bottom -= Randbreite
End Sub

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
     If m.Msg = WM_Messages.WM_NCCALCSIZE Then
         ' Größe des Client-Bereichs berechnen und nach m.LParam kopieren
         If m.WParam <> IntPtr.Zero Then
             Dim rcsize As NCCALCSIZE_PARAMS = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(NCCALCSIZE_PARAMS)), NCCALCSIZE_PARAMS)
             AdjustClientRect(rcsize.rcNewWindow)
             Marshal.StructureToPtr(rcsize, m.LParam, False)
        Else
             Dim rcsize As RECT = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(RECT)), RECT)
             AdjustClientRect(rcsize)
             Marshal.StructureToPtr(rcsize, m.LParam, False)
        End If
    m.Result = New IntPtr(1)
    ElseIf m.Msg = WM_Messages.WM_NCPAINT Then
    ' code zum Zeichnen...
 End If
Also ich ändere jetzt zur Laufzeit den Wert von "Randbreite" von beispielsweise 5 auf 10 Pixel. Damit der Rand neu gezeichnet wird rufe ich die API RedrawWindow auf. Funktioniert auch soweit.
Aber:
Sagen wir ich habe ein Child-Control mit Location (0,0) auf meinem Control, also eines das oben links in der Ecke hocken soll. Nachdem sich die Dicke des Randes ändert, Befindet sich die Ecke mit den Client-Coordinaten (0,0) nicht mehr dort, wo sie vorher war, sondern 5 Pixel weiter rechts und weiter unten. Das Child-Control bekommt davon aber nichts mit und hockt jetzt halb im NonClient-Bereich (und überdeckt meinen selbst gezeichneten Rand).
Ich habe versucht PerformLayout aufzurufen, damit das Child-Control neu angeordnet wird. Aber der Aufruf hilft nichts!
Frage: Wie kann ich dem Control mitteilen, dass es seinen ClientBereich neu berechnen soll?
 
Zuletzt bearbeitet:
Lösung gefunden

Ich habe eine Lösung gefunden. Sie ist seltsam, aber sie funktioniert:
Code:
' Randbreite sei beispielsweise zuvor 5 und wird jetzt auf 10 Pixel geändert
Randbreite = 10
' Jetzt muss das Control sein ClientRectangle neu berechnen.
' Deswegen setze ich irgendeine Eigenschaft, die das Layout beeinflusst, auf einen neuen (beliebigen!) Wert.
' Als beliebigen Wert wähle ich hier "Size.Empty"
Dim temp As Size = Me.ClientSize
Me.ClientSize = Size.Empty
Me.ClientSize = temp
Im Prinzip muss man nur irgend eine Eigenschaft verändern, die das Layout des Controls beeinflusst. Also man könnte auch Width, Height, etc. verändern.

Ich finde diese Lösung aber nicht hübsch.
 
Zuletzt bearbeitet:
Hi Vereth! WM_SIZE hat zwar leider nicht funktioniert, aber es hat mich auf die richtige Idee gebracht! Vielen Dank! Ich muss natürlich WM_NCCALCSIZE an mein Control senden. Das kann ich mit SetWindowPos machen. Hier der funktionierende Code:
Code:
<Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInt32) As IntPtr
End Function

Private Const SWP_NOSIZE As UInteger = &H1 ' Retains the current size (ignores the cx and cy parameters).
Private Const SWP_NOMOVE As UInteger = &H2 ' Retains the current position (ignores X and Y parameters).
Private Const SWP_NOZORDER As UInteger = &H4 ' Retains the current Z order (ignores the hWndInsertAfter parameter).
Private Const SWP_FRAMECHANGED As UInteger = &H20  ' Applies new frame styles set using the SetWindowLong  function. Sends a WM_NCCALCSIZE  message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.

Private Sub DickerRand
    ' Randbreite auf 10 Pixel setzen
    Randbreite = 10
    ' Control mitteilen, dass sich der Rand verändert hat
    SetWindowPos(Me.Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER Or SWP_FRAMECHANGED)
End Sub
 
Zuletzt bearbeitet:
Zurück