Custom form border

G

Guest

I want to create a custom form border. By thought was to simply override the
DisplayRectangle property in order to control the border width and then draw
the border during the OnPaint event. The problem with this technique is that
controls that are outside of the ClientRectangle of the form will overlap the
form's border. You can see this in the following example:

Public Class Form1
Inherits Windows.Forms.Form

Private Const BorderWidth As Integer = 16 'This is just for demo purposes.

Public Sub New()
MyBase.New()
End Sub

Public Overrides ReadOnly Property DisplayRectangle() As
System.Drawing.Rectangle
Get
Return New Drawing.Rectangle(BorderWidth, BorderWidth, (Me.Width - (2
* BorderWidth)), (Me.Height - (2 * BorderWidth))) 'This is just for demo
purposes.
End Get
End Property

Protected Overrides Sub OnPaint(ByVal e As
System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)
e.Graphics.FillRectangle(Brushes.Blue, Me.ClientRectangle)
End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
Me.Text = ""
Me.ControlBox = False
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None

Dim p1 As New Windows.Forms.Panel 'This panel works as desired.
p1.BackColor = Color.Red
p1.Dock = DockStyle.Fill
Me.Controls.Add(p1)

Dim p2 As New Windows.Forms.Panel 'This panel should be covered by the
border.
p2.BackColor = Color.Yellow
p2.Bounds = New Drawing.Rectangle(0, 0, (Me.ClientSize.Width \ 2),
(Me.ClientSize.Height \ 2))
Me.Controls.Add(p2)
End Sub

End Class

Is there any way to cause the border to be on top of controls that are
within the border region. My guess is that I need to specify the border
region as being a nonclient area, but I'm not sure how this is done.

Thanks for any help!
Lance
 
R

rowe_newsgroups

I want to create a custom form border. By thought was to simply override the
DisplayRectangle property in order to control the border width and then draw
the border during the OnPaint event. The problem with this technique is that
controls that are outside of the ClientRectangle of the form will overlap the
form's border. You can see this in the following example:

Public Class Form1
Inherits Windows.Forms.Form

Private Const BorderWidth As Integer = 16 'This is just for demo purposes.

Public Sub New()
MyBase.New()
End Sub

Public Overrides ReadOnly Property DisplayRectangle() As
System.Drawing.Rectangle
Get
Return New Drawing.Rectangle(BorderWidth, BorderWidth, (Me.Width - (2
* BorderWidth)), (Me.Height - (2 * BorderWidth))) 'This is just for demo
purposes.
End Get
End Property

Protected Overrides Sub OnPaint(ByVal e As
System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)
e.Graphics.FillRectangle(Brushes.Blue, Me.ClientRectangle)
End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
Me.Text = ""
Me.ControlBox = False
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None

Dim p1 As New Windows.Forms.Panel 'This panel works as desired.
p1.BackColor = Color.Red
p1.Dock = DockStyle.Fill
Me.Controls.Add(p1)

Dim p2 As New Windows.Forms.Panel 'This panel should be covered by the
border.
p2.BackColor = Color.Yellow
p2.Bounds = New Drawing.Rectangle(0, 0, (Me.ClientSize.Width \ 2),
(Me.ClientSize.Height \ 2))
Me.Controls.Add(p2)
End Sub

End Class

Is there any way to cause the border to be on top of controls that are
within the border region. My guess is that I need to specify the border
region as being a nonclient area, but I'm not sure how this is done.

Thanks for any help!
Lance

Why not set the FormBorderStyle to none and then draw the border on
the form?

Thanks,

Seth Rowe
 
L

Linda Liu [MSFT]

Hi Lance,

I have performed a test based on your sample code and did see the problem.
I have researched this issue for some time, but unfortunately I haven't
find a good solution yet.

I think the reason why the 'yellow' panel covers the 'blue border' is that
the 'blue border' is not a real form border. The 'blue border' actually
resides in the client rectangle of the form.

I also performed a test on Panel. I Set the Panel's BorderStyle property to
None, and handle its Paint event to draw a border on it. Then I add a
TextBox, whose location is set to {0,0}, into the Panel. The result is that
the TextBox covers the custom border I draw around the Panel.

I think a simple workaround of your problem is to place controls in a
specific area. In your scenario, you may place all controls that are on the
form inside the DisplayRectangle.

Hope this helps.
If you have any question, please feel free to let me know.


Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Hi Linda,

Thank you for taking time to look into my issue. I like your idea of
limiting controls to the DisaplyRectangle and that was what I tried
originally. But, if you recall my question about how to change the border of
the MdiClient the fix was to increase the size of the MdiClient by twice its
border width and then set the offset the bounds by the border width. At
first, this seemed like a perfect fix until I tried it in my real app at
which point I realized that the enlarged MdiClient covers my form border.
So, it became clear that I need a more sophisticated approach for specifying
the form's border region.

Do you have any other ideas? Is there a windows message that can be handled
by overriding the WndProc method (e.g., WM_NCCALCSIZE)?

Thanks again,
Lance
 
L

Linda Liu [MSFT]

Hi Lance,

Thank you for your prompt response. After reviewing your pervious post
about how to change the border of the MdiClient, I understand what the
problem you have.

I think there're two solutions for your problem. One solution is to find a
better way to remove the 3D border of the MdiClient actually. Thus, you
could just dock the MdiClient to the MdiParent and needn't reposition and
enlarge it any longer.

The other solution is to draw a real form border for the MdiParent. To do
this, we could handle WM_NCPAINT windows message in the override WndProc
method. A problem of this solution is that you may not able to change the
form border width.

As for the solution 1, I found an article saying that "The thick inset
border of the MdiClient window is drawn by Windows because the MDIClient
window has the WS_EX_CLIENTDGE extended window style set. This can be
removed using the GetWindowLong and SetWindowLong Win32 API calls". I have
performed a test on the SetWindowLong and GetWindowLong, but unfortunately,
I haven't worked it out yet.

I will go on researching for the and will get the result back to you ASAP.
I appeciate your patience.


Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

Sounds great Linda. I am so glad to have you working on this and I look
forward to see what you come up with.

Thanks,
Lance
 
L

Linda Liu [MSFT]

Hi Lance,

To really remove the thick inset border of the MdiClient, we could use the
Win32 API call 'SetWindowLong' to set the window border to another extended
style.

Note that certain window data is cached, so changes you make using
SetWindowLong will not take effect until you call the SetWindowPos
function. Specifically, if you change any of the frame styles, you must
call SetWindowPos with the SWP_FRAMECHANGED flag for the cache to be
updated properly. So we shoud call the Win32 API function 'SetWindowPos'
after we call the function 'SetWindowLong'.

The following is a sample to remove the thick inset border of the MdiClient.

Imports System.Runtime.InteropServices

Public Class Form1

Public Shared GWL_EXSTYLE As Integer = -20
Public Shared WS_EX_CLIENTEDGE As Integer = &H200

Public Shared SWP_NOMOVE As UInt32 = &H2
Public Shared SWP_NOSIZE As UInt32 = &H1
Public Shared SWP_NOZORDER As UInt32 = &H4
Public Shared SWP_FRAMECHANGED As UInt32 = &H20

Public Declare Auto Function GetWindowLong Lib "user32.dll" (ByVal hwnd
As IntPtr, ByVal index As Integer) As Integer
Public Declare Auto Function SetWindowLong Lib "user32.dll" (ByVal hwnd
As IntPtr, ByVal index As Integer, ByVal style As Integer) As Integer
Public Declare Auto Function SetWindowPos Lib "user32.dll" (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 Boolean


Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim MdiClient As MdiClient = Nothing
For Each ctrl As Control In Me.Controls
If (Equals(ctrl.GetType(), GetType(MdiClient))) Then
MdiClient = CType(ctrl, MdiClient)
Exit For
End If
Next

If Not (MdiClient Is Nothing) Then
' get the current extended window style of the MdiClient
Dim iStyle As Integer = GetWindowLong(MdiClient.Handle,
GWL_EXSTYLE)
' remove the WS_EX_CLIENTEDGE extended window style from the
current window style
Dim iNewStyle As Integer = iStyle And (Not WS_EX_CLIENTEDGE)
' set the new window style
iStyle = SetWindowLong(MdiClient.Handle, GWL_EXSTYLE, iNewStyle)
' call the function SetWindowPos to take the new sytle into
effect
SetWindowPos(MdiClient.Handle, IntPtr.Zero, 0, 0, 0, 0,
SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER Or SWP_FRAMECHANGED)
End If

End Sub
End Class

I have tested the above code and confirmed it works.

I think once you could really remove the thick inset border of the
MdiClient, you should be able to solve your problem.

Please try my sample code and let me know the result.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

Hi Linda,

That is a perfect fix. Thank you so much for your time and effort. You
have been a big help!

Lance
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top