Introduction
I´ve made this application because there are many people on the internet who try to make a "custom border". I solved this problem: semi transparent border, laggy resizer, not exactly the right colors, buttons with the right size and more. The bad things are that areosnap don´t work and it overlay parts of the taskbar.
Using the Code
One problem was that if you drag the border while it's maximized, it's restored to normal. But the titlebar doesn´t resize. Because it only detects if you press the MaximizeRestoreButton
. So I must detect when the Windowstate
changes, but there wasn´t any eventhandler
for this. I solved it with this code.
Here, we make a variable for the last WindowState
:
private FormWindowStatem LastState;
Now, we override the OnClientSizeChanged
, and detect if the windowstate
has changed.
protected override void OnClientSizeChanged(EventArgs e)
{
if (this.WindowState != mLastState)
{
mLastState = this.WindowState;
OnWindowStateChanged(e);
}
base.OnClientSizeChanged(e);
}
If the windowstate
has changed, we come to this void
:
protected void OnWindowStateChanged(EventArgs e)
{
if (WindowState == FormWindowState.Maximized)
{
TitleBarButtonMaximizeRestore.Image = Properties.Resources.ButtonFontRestore;
TitleBarContainer.Size = new Size(TitleBarContainer.Size.Width +6, 25);
TitleBarContainer.Location = new Point
(TitleBarContainer.Location.X -3, TitleBarContainer.Location.Y -3);
MainContainer.Size = new Size
(MainContainer.Size.Width + 6, MainContainer.Size.Height + 5 + 6);
MainContainer.Location = new Point
(MainContainer.Location.X - 3, MainContainer.Location.Y - 5 -3);
}
else if (WindowState == FormWindowState.Normal)
{
TitleBarButtonMaximizeRestore.Image = Properties.Resources.ButtonFontMaximize;
TitleBarContainer.Size = new Size(TitleBarContainer.Size.Width -6, 30);
TitleBarContainer.Location = new Point
(TitleBarContainer.Location.X +3, TitleBarContainer.Location.Y +3);
MainContainer.Size = new Size
(MainContainer.Size.Width - 6, MainContainer.Size.Height - 5 - 6);
MainContainer.Location = new Point
(MainContainer.Location.X +3, MainContainer.Location.Y + 5 +3);
}
}
Here, I check the new windowstate
and if the form is maximized, we remove the border, and change the image of the TitleBarButtonMaximizeRestore
to restore. If the new windowstate
is normal, we bring back the border and set the image of TitleBarButtonMaximizeRestore
to the maximize image.
So, here is the hardest part: make the borderless form resizable: We overwrite the "WndProc
".
protected override void WndProc(ref Message m)
{
const uint WM_NCHITTEST = 0x0084;
const uint WM_MOUSEMOVE = 0x0200;
const uint HTLEFT = 10;
const uint HTRIGHT = 11;
const uint HTBOTTOMRIGHT = 17;
const uint HTBOTTOM = 15;
const uint HTBOTTOMLEFT = 16;
const uint HTTOP = 12;
const uint HTTOPLEFT = 13;
const uint HTTOPRIGHT = 14;
const int RESIZE_HANDLE_SIZE = 10;
bool handled = false;
if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
{
Size formSize = this.Size;
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
Dictionary<UInt32, Rectangle> boxes = new Dictionary<UInt32, Rectangle>() {
{HTBOTTOMLEFT, new Rectangle(0, formSize.Height -
RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTBOTTOM, new Rectangle(RESIZE_HANDLE_SIZE, formSize.Height -
RESIZE_HANDLE_SIZE, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTBOTTOMRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE,
formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE,
RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE)},
{HTTOPRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE,
0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTTOP, new Rectangle(RESIZE_HANDLE_SIZE, 0,
formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTTOPLEFT, new Rectangle(0, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTLEFT, new Rectangle(0, RESIZE_HANDLE_SIZE,
RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE) }
};
foreach (KeyValuePair<UInt32, Rectangle> hitBox in boxes)
{
if (hitBox.Value.Contains(clientPoint))
{
m.Result = (IntPtr)hitBox.Key;
handled = true;
break;
}
}
}
if (!handled)
base.WndProc(ref m);
}
Now the simple stuff: make the Form
moveable:
private void moveForm_MouseDown(object sender, MouseEventArgs e)
{
(sender as Control).Capture = false;
Message msg = Message.Create
(Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
WndProc(ref msg);
}
And make the background semi transparent:
You must set the "TransparencyKey
" to any color and set the form backcolor
to the same color.
Then, you add an onpaint
event to make the full transparent background semitransparent
. Just like this:
private void SemiTransperent_Paint(object sender, PaintEventArgs e)
{
var hb = new HatchBrush(HatchStyle.Percent50, this.TransparencyKey);
e.Graphics.FillRectangle(hb, this.DisplayRectangle);
}