I've seen that some apps (maybe not .NET apps) that have an extra button on the left from the minimize button on the form's title bar? How can I achieve this in C#?
-
2Note that you shouldn't do this: http://msdn.microsoft.com/en-us/library/aa974173(v=MSDN.10).aspx – »Don't add controls to a window frame. Put the controls within the window instead.« – Joey May 15 '10 at 18:13
-
3@Johannes, I admit sometimes apps that do this can be annoying but some apps do it very well. Like Ultramon which puts a button to swap monitors on the frame that looks very native in Windows 7. So there are applications that do it tastefully. – Josh May 15 '10 at 18:19
-
3@Josh: Office 2007 also did it (but that's not the first occurrence of the Office team ignoring any Windows UX Guidelines). Still, that document is written by UX professionals and mere developers (or code monkeys) are working hard to ignore them. And for the sake of the users I think they should just follow along, unless they can bring people with experience and knowledge and – most importantly – usability tests, to prove otherwise. – Joey May 15 '10 at 21:38
-
3Well that's why they're guidelines and not laws. And if you read the entire page, you'll see many instances of cases where Microsoft's own design guidelines which were once considered the way to go, are now considered wrong. – Josh May 16 '10 at 13:37
-
6I love articles when MS ignore their own guidelines https://s3.amazonaws.com/github-images/blog/2012/gh4w/makingof/Windows8-vs.png – Dave Sep 04 '13 at 10:41
3 Answers
UPDATE: Added a solution that will work with Aero enabled for Windows Vista and Windows 7
***Non-Aero Solution***
The non-client area of a window interaction is managed by a series of non-client specfic messages. For example WM_NCPAINT message is sent to the window procedure to paint the non-client area.
I have never done this from .NET, but I suspect you can overide the WndProc and handle the WM_NC* messages to achieve what you want.
Update: Since I never tried this from .NET I got a few minutes and thought I would give it a quick try.
Trying this on Windows 7, I found that I needed to disable the Themes for the Window if I wanted to OS to do the base rendering of the non-client area. So here is a short test. I used GetWindowDC to get the DC of the entire window rather than GetDCEx, that was just because I could interop that from memory and did not have lookup all the flag constants for GetDcEx. And of course the code could do with more error checking.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class CustomBorderForm : Form
{
const int WM_NCPAINT = 0x85;
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("user32.dll", SetLastError = true)]
public static extern void DisableProcessWindowsGhosting();
[DllImport("UxTheme.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList);
public CustomBorderForm()
{
// This could be called from main.
DisableProcessWindowsGhosting();
InitializeComponent();
}
protected override void OnHandleCreated(EventArgs e)
{
SetWindowTheme(this.Handle, "", "");
base.OnHandleCreated(e);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCPAINT:
{
IntPtr hdc = GetWindowDC(m.HWnd);
using (Graphics g = Graphics.FromHdc(hdc))
{
g.FillEllipse(Brushes.Red, new Rectangle((Width-20)/2, 8, 20, 20));
}
ReleaseDC(m.HWnd, hdc);
}
break;
}
}
}
}
Btw. I called DisableProcessWindowsGhosting
, this will stop the OS from drawing the non-client area if the application takes too long to respond to windows messages. If you do not do this, then in some situations the border will be renderd but your adornments will not be shown. So that depends on your requirements it that is right for you or not.
***Aero supported solution***
Prompted by the comment from @TheCodeKing, I thought I would take another look at this. It turns out this can be done in a fully documented way while supporting Aero. But it is not for the faint of heart. I will not provide a complete solution here, there are still some kinks to workout, but it does the basics.
This code/solution is based off the Win32 example which can be found at the following location http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx
In principal what you need to do is the following.
- Extend the client area of the window to cover the Frame. This is done by handling the WM_NCCALCSIZE message and returning 0. This gives the Non-Client area a size of 0 and therefore the client area now covers the entire window.
- Extend the Frame into the client area using DwmExtendFrameIntoClientArea. This gets the OS to render the Frame over the client area.
The above steps will give you a windows with the standard glass frame excluding the system menu (Window Icon) and the title. The minimize, maximize and close buttons will still be drawn and will work. What you will not be able to do is drag or resize the window, this is because the frame is not really there, remember the client area covers the whole window, we have just asked the OS to draw the frame onto the client area.
Now you can draw on the window as normal, even on top of the frame. You can even put controls in the caption area.
Finally, allow the DWM to handle hit-testing for you, by calling DwmDefWindowProc from your WndProc
(before you've processed it). It returns a boolean indicating whether the DWM handled the message for you.

- 52,623
- 10
- 78
- 89
-
This technique doesn't work with aero glass unfortunately. I'd be interested if anyone knows the voodoo required to get in working on glass. So far I haven't found any working examples. – TheCodeKing Aug 24 '11 at 21:07
-
1@TheCodeKing, I have updated the answer to cover your question. I have also prototyped this in .NET just to confirm that it can be made to work. Here is the MSDN article that I referenced http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx – Chris Taylor Aug 31 '11 at 19:37
-
Actually that reminds me I do know how to do this, I have some code somewhere, it just didn't render in design mode how it looked at runtime. Do you have a C# example of the technique you have described, I've tried most and they don't work? – TheCodeKing Aug 31 '11 at 19:54
-
2I have a very basic (limited) sample that I prototyped to test the concept. You can get the code from my skydrive. This is by no means complete, it is just a proof of concept! https://skydrive.live.com/?cid=5bb13275dc6b8248&sc=documents&uc=1&id=5BB13275DC6B8248%21162# – Chris Taylor Sep 01 '11 at 08:05
-
@ChrisTaylor: I found the control buttons do not work in your *limited* sample and I've managed to fix it. Kindly update the code for `WM_NCHITTEST` to this: `if (callDWP) { callDWP = (ht == Win32Constants.HTNOWHERE); result = new IntPtr(ht); }` to get the desired effect. – Alex Essilfie Apr 16 '12 at 20:35
-
@ChrisTaylor: I try to use these code to change the title's font by using `g.DrawString()`, but I got some problems: If I use `SetWindowTheme(this.Handle, "", "");`, the title will show exactly, but the border of form will be gray. If I use `SetWindowTheme(this.Handle, "explorer", null);`, the title won't show (behind title bar), but the border of form will be not change (defaul color). How could I do to show title with default color? Thanks! – GSP Mar 30 '15 at 01:51
-
In order to achieve hit-testing for the buttons, you need to allow the DWM to handle certain messages. In your WndProc, first call `DwmDefWindowProc` (it returns a bool to say whether the message was handled). @TheCodeKing – Mark Ingram Nov 30 '18 at 16:42
-
@Scratte - Thanks I did not notice the edit, I appreciate the heads-up. – Chris Taylor Aug 12 '21 at 15:41
-
Simple Solution:
Step 1: Create a Windows Form (this will be your custom title bar)
-Set Form Border Style to None
-Add whatever controls you would like to this
-I will name this custom form "TitleBarButtons"
Step 2. In the Form that you want to use this custom control in, add
titleBarBtn = new TitleBarButtons();
titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
titleBarBtn.Show();
titleBarBtn.Owner = this;
To your constructor... you can mess with the offsets this just fit in a nice position for my app
Step 3. Add the Move event to your main Form
private void Form14_Move(object sender, EventArgs e)
{
titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
}
Please let me know if you would like a better explanation of any of the above code.

- 153
- 1
- 9

- 658
- 1
- 14
- 24
-
What is a TitleBarButtons? Cant seem to find any references of this class. – Borik Dec 30 '13 at 06:03
-
I'm sorry I realized I wasn't clear I believe "TitleBarButtons" was the custom form made in step one. Ill revise my answer to make it more clear. – hrh Dec 30 '13 at 14:11
-
@hrh pretty old thread though is it possible in wpf too, adding form to wpf window – murmansk Aug 22 '17 at 11:02
I think a way to do this would be to handle WM_NCPAINT message (non-client paint) to draw the button, and to handle non-client mouse clicks to know someone clicked on the "button".

- 16,353
- 21
- 92
- 151