Click here to Skip to main content
15,902,276 members
Articles / Programming Languages / C#
Tip/Trick

Custom Border (Windows 10 Style)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
28 Feb 2016CPOL1 min read 15.8K   591   2   1
I made this "borderless" application (WinForms), and added an semitransperent border (might laggy). The design is exactly the Windows 10 colors and button sizes.

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.

Image 1

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:

C#
private FormWindowStatem LastState;

Now, we override the OnClientSizeChanged, and detect if the windowstate has changed.

C#
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:

ASP.NET
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".

C#
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:

C#
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:

C#
private void SemiTransperent_Paint(object sender, PaintEventArgs e)
        {
            var hb = new HatchBrush(HatchStyle.Percent50, this.TransparencyKey);
            e.Graphics.FillRectangle(hb, this.DisplayRectangle);
        }

License

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


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIncomplete feature Pin
Gergilcan28-Feb-16 21:03
professionalGergilcan28-Feb-16 21:03 
There are a lot of samples and versions of Custom Window borderless that works perfectly fine and you post something that does not work as intended. This is just gonna mess the users mood.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.