Skip to content

Commit

Permalink
Add support for different FormBorderStyles
Browse files Browse the repository at this point in the history
  • Loading branch information
wolframhaussig committed Aug 2, 2023
1 parent f4341c5 commit 718270f
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public FrmDefault()

private void stylableButton1_Click(object sender, EventArgs e)
{
new FrmMdi(true).Show();
new FrmMdi().Show();
}
}
}
46 changes: 16 additions & 30 deletions StylableWinFormsControls/StylableWinFormsControls.Example/FrmMdi.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
using StylableWinFormsControls.Controls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace StylableWinFormsControls.Example
{
Expand All @@ -23,6 +14,21 @@ public partial class FrmMdi : Form
/// </summary>
internal const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;

public FrmMdi()
{
InitializeComponent();
//set the dark mode to better show the styling support of the MDI child form
SetWindowTheme(this.Handle, "DarkMode_Explorer", null);
OpenThemeData(IntPtr.Zero, "Explorer::ScrollBar");
int useImmersiveDarkMode = 1;
DwmSetWindowAttribute(this.Handle, DWMWA_USE_IMMERSIVE_DARK_MODE, ref useImmersiveDarkMode, sizeof(int));

this.IsMdiContainer = true;
var child = new StylableMdiChildForm();
child.MdiParent = this;
child.Show();
}

/// <summary>
/// native method to set the title bar style
/// </summary>
Expand All @@ -39,25 +45,5 @@ public partial class FrmMdi : Form

[DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
internal static extern int SetWindowTheme(IntPtr hWnd, string pszSubAppName, string pszSubIdList);

public FrmMdi()
{
InitializeComponent();
SetWindowTheme(this.Handle, "DarkMode_Explorer", null);
OpenThemeData(IntPtr.Zero, "Explorer::ScrollBar");
int useImmersiveDarkMode = 1;
DwmSetWindowAttribute(this.Handle, DWMWA_USE_IMMERSIVE_DARK_MODE, ref useImmersiveDarkMode, sizeof(int));
}
public FrmMdi(bool parent = true): this()
{
if (parent)
{
this.IsMdiContainer = true;
var child = new StylableMdiChildForm();
child.TitleHeight = 40;
child.MdiParent = this;
child.Show();
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Runtime.InteropServices;
using System.Windows.Forms.VisualStyles;
using StylableWinFormsControls.Utilities;
using System.Runtime.InteropServices;

namespace StylableWinFormsControls.Controls
{
Expand All @@ -19,41 +19,17 @@ public partial class StylableMdiChildForm : Form
/// </summary>
private const int WM_NCPAINT = 0x0085;

private Brush _titleBrush = new SolidBrush(Color.FromArgb(32, 32, 32));

private Color _titleColor = Color.FromArgb(32, 32, 32);

private int _titleHeight = 0;

/// <summary>
/// defines the place where the icon is drawn
/// </summary>
private Rectangle _titleIconCanvas = new Rectangle(6, 6, 20, 20);

private Color _borderColor = Color.FromArgb(32,32,32);
private Brush _borderBrush = new SolidBrush(Color.FromArgb(32, 32, 32));
private Brush _titleBrush = new SolidBrush(Color.FromArgb(32, 32, 32));
private Color _titleColor = Color.FromArgb(32, 32, 32);
private int _titleHeight = 0;
public int TitleHeight {
get
{
return _titleHeight;
}
set
{
_titleHeight = value;
_titleIconCanvas = new Rectangle(6, 6, _titleHeight - 12, _titleHeight - 12);
RecalcDimensions();
}
}
private int _borderWidth = 0;
public int BorderWidth {
get
{
return _borderWidth;
}
set
{
_borderWidth = value;
RecalcDimensions();
}
}

/// <summary>
/// constructor
/// </summary>
Expand All @@ -63,33 +39,35 @@ public StylableMdiChildForm()
this.DoubleBuffered = true;
}

public Color BorderColor
public Color BorderColor { get; set; } = Color.FromArgb(32, 32, 32);

public Color TitleColor
{
get
{
return _borderColor;
return _titleColor;
}
set
{
_borderColor = value;
_borderBrush = new SolidBrush(_borderColor);
_titleColor = value;
_titleBrush = new SolidBrush(_titleColor);
}
}

public Color TitleColor
public int TitleHeight
{
get
{
return _titleColor;
return _titleHeight;
}
set
{
_titleColor = value;
_titleBrush = new SolidBrush(_titleColor);
_titleHeight = value;
_titleIconCanvas = new Rectangle(6, 6, _titleHeight - 12, _titleHeight - 12);
RecalcDimensions();
}
}


protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
Expand All @@ -99,7 +77,7 @@ protected override void WndProc(ref Message m)
//no need to draw a titlebar or a border so we can rely on the normal Drawing
return;
}
if(!IsMdiChild)
if (!IsMdiChild)
{
//when the form is not an MDI child, the form will be drawn by the Desktop Window Manager
//so we don't need to draw it ourselves as the DWM correctly styles the form
Expand All @@ -122,34 +100,33 @@ private void DrawFrame()
IntPtr hdc = GetWindowDC(this.Handle);
using (Graphics g = Graphics.FromHdc(hdc))
{
//draw title bar
g.FillRectangle(_titleBrush, 0, 0, this.Width, TitleHeight);
g.DrawString(this.Text, this.Font, Brushes.Coral, 35, 8);
if (this.Icon != null)
g.DrawIcon(this.Icon, _titleIconCanvas);
//draw left border
g.FillRectangle(_borderBrush, 0, TitleHeight, BorderWidth, this.Height - TitleHeight);
//draw bottom border
g.FillRectangle(_borderBrush, BorderWidth, this.Height - BorderWidth, this.ClientSize.Width, BorderWidth);
//draw right border
g.FillRectangle(_borderBrush, this.Width - BorderWidth, TitleHeight, BorderWidth, this.Height - TitleHeight);
DrawTitleBar(g);

g.DrawBorder(this);
}
ReleaseDC(this.Handle, hdc);
}

private void RecalcDimensions()
private void DrawTitleBar(Graphics g)
{
if (TitleHeight == 0)
TitleHeight = this.Height - this.ClientSize.Height - BorderWidth - 8;
if (BorderWidth == 0)
BorderWidth = (this.Width - this.ClientSize.Width) / 2;
this.Height = _titleHeight + this.ClientSize.Height + BorderWidth + 8;
this.Width = 2 * BorderWidth + this.ClientSize.Width;
//draw title bar
g.FillRectangle(_titleBrush, 0, 0, this.Width, TitleHeight);
g.DrawString(this.Text, this.Font, Brushes.Coral, 35, 8);
if (this.Icon != null)
g.DrawIcon(this.Icon, _titleIconCanvas);
}

private void Form_Load(object sender, EventArgs e)
{
RecalcDimensions();
}

private void RecalcDimensions()
{
int borderWidth = (this.Width - this.ClientSize.Width) / 2;
if (TitleHeight == 0)
TitleHeight = this.Height - this.ClientSize.Height - borderWidth - 8;
this.Height = _titleHeight + this.ClientSize.Height + borderWidth + 8;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using StylableWinFormsControls.Controls;

namespace StylableWinFormsControls.Utilities
{
/// <summary>
/// provides drawing logic for borders on a form
/// </summary>
internal static class CustomFormBorderStyleHandler
{
/// <summary>
/// As the drawing is called often, we do not want to create the Brush every time so we cache them
/// </summary>
private static readonly Dictionary<Color, Brush> BRUSH_CACHE = new Dictionary<Color, Brush>();

/// <summary>
/// draw a border
/// </summary>
/// <param name="g">the graphics object to draw on</param>
/// <param name="borderColor">the default color for the border. may be changed slightly (e.g. for 3D effects)</param>
/// <param name="f">the form to get the settings from</param>
public static void DrawBorder(this Graphics g, Color borderColor, Form f)
{
g.DrawBorder(borderColor, f.FormBorderStyle, topOffset: f.GetTitleBarHeight());
}

/// <summary>
/// draw a border
/// </summary>
/// <param name="g">the graphics object to draw on</param>
/// <param name="f">the form to get the settings from</param>
public static void DrawBorder(this Graphics g, StylableMdiChildForm f)
{
g.DrawBorder(f.BorderColor, f);
}

/// <summary>
/// draw a border
/// </summary>
/// <param name="g">the graphics object to draw on</param>
/// <param name="borderColor">the default color for the border. may be changed slightly (e.g. for 3D effects)</param>
/// <param name="style">the Border Style</param>
/// <param name="topOffset">offset at the top. Can be used to allow space for a title bar</param>
/// <param name="bottomOffset">offset at the bottom</param>
/// <param name="leftOffset">offset at the left</param>
/// <param name="rightOffset">offset at the right</param>
public static void DrawBorder(this Graphics g, Color borderColor, FormBorderStyle style, int topOffset = 0, int bottomOffset = 0, int leftOffset = 0, int rightOffset = 0)
{
switch (style)
{
case FormBorderStyle.Fixed3D:
//TODO: Implement
case FormBorderStyle.Sizable:
case FormBorderStyle.SizableToolWindow:
case FormBorderStyle.FixedDialog:
case FormBorderStyle.FixedToolWindow:
case FormBorderStyle.FixedSingle:
DrawSimpleBorder(g, borderColor, topOffset, bottomOffset, leftOffset, rightOffset);
break;

case FormBorderStyle.None:
//no border
break;
}
}

/// <summary>
/// returns a SolidBrush for the given Color
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public static Brush GetBrush(this Color c)
{
if (BRUSH_CACHE.ContainsKey(c))
{
return BRUSH_CACHE[c];
}
var brush = new SolidBrush(c);
BRUSH_CACHE.Add(c, brush);
return brush;
}

private static void DrawSimpleBorder(Graphics g, Color borderColor, int topOffset, int bottomOffset, int leftOffset, int rightOffset)
{
int totalHeight = (int)g.VisibleClipBounds.Height;
int totalWidth = (int)g.VisibleClipBounds.Width;
int borderWidth = 8;
var borderBrush = borderColor.GetBrush();
//draw left border
g.FillRectangle(borderBrush, leftOffset, topOffset, borderWidth, totalHeight - topOffset - bottomOffset);
//draw bottom border
g.FillRectangle(borderBrush, leftOffset, totalHeight - bottomOffset - borderWidth, totalWidth - leftOffset - rightOffset, borderWidth);
//draw right border
g.FillRectangle(borderBrush, totalWidth - rightOffset - borderWidth, topOffset, borderWidth, totalHeight - topOffset - bottomOffset);
}

/// <summary>
/// calculates the current border width on a form
/// </summary>
/// <param name="f">the form</param>
/// <returns></returns>
public static int GetBorderWidth(this Form f)
{
//setting the border style will update width and client size so we can calculate the width easily
return (f.Width - f.ClientSize.Width) / 2;
}

/// <summary>
/// calculates the height of the titlebar
/// </summary>
/// <param name="f">the form</param>
/// <returns></returns>
public static int GetTitleBarHeight(this Form f)
{
return f.Height - f.ClientSize.Height - f.GetBorderWidth();
}
}
}

0 comments on commit 718270f

Please sign in to comment.