Szymon Kobalczyk's Blog

A Developer's Notebook

  Home  |   Contact  |   Syndication    |   Login
  106 Posts | 6 Stories | 572 Comments | 365 Trackbacks

News

View Szymon Kobalczyk's profile on LinkedIn

Twitter












Tag Cloud


Article Categories

Archives

Post Categories

Blogs I Read

Tools I Use

Download source code from ProjectDistributor.net

 

Figure 1. Sample form with custom drawn border.

I have split the code into two primary classes. First one, FormWithNonClientArea, extends the standard Form class by adding support for non-client area messages (more on this shortly) and can be uses for various scenarios. Second one, CustomBorderForm, utilizes these messages and represents a base class for drawing graphical borders composed of several bitmap parts. It also draws a form header including icon, text and form buttons (more on this later). This way I can separate dirty plumbing required for enabling non-client drawing and actual drawing of the graphical elements. So lets see how it works.

Extending Form with Non-Client Area Painting

Each window we see on screen (be it a Form, UserControl or any other Control) is described by two rectangles: the bounds of the window and it's client area. Bounds specify the location and size of the window as a whole while the client area specifies the region inside the window that is available for client controls. By default Windows Forms allows us to access only the client part of the window. To gain access to the non-client part we need to intercept some additional windows messages. We can do this by overriding the WndProc message loop. For each message I defined dedicated method, so my WndProc method only redirects calls to this methods.

Positioning the Client Ractangle

If we are going to draw our custom borders good chances are that their size and proportions will differ from the standard ones. To correct this we need to specify a new client rectangle for the window. This is done in the WM_NCCALCSIZE message. This message can be raised in two ways. When WParam is equal to zero the LParam points to RECT structure with window bound that we should adjust to proposed client ractangle. Alternatively, when WParam value is one the LParam points to NCCALCSIZE_PARAMS strucure allowing to move the existing client area inside the window. For our purpose we will simply adjust the proposed rectangle to required coordinates.

private void WmNCCalcSize(ref Message m)
{
    if (m.WParam == NativeMethods.FALSE)
    {
        NativeMethods.RECT ncRect = (NativeMethods.RECT)m.GetLParam(typeof(NativeMethods.RECT));
        Rectangle proposed = ncRect.Rect;
        OnNonClientAreaCalcSize(ref proposed);
        ncRect = NativeMethods.RECT.FromRectangle(proposed);
        Marshal.StructureToPtr(ncRect, m.LParam, false);
        m.Result = IntPtr.Zero;
    }
    else if (m.WParam == NativeMethods.TRUE)
    {
        NativeMethods.NCCALCSIZE_PARAMS ncParams =
            (NativeMethods.NCCALCSIZE_PARAMS)m.GetLParam(typeof(NativeMethods.NCCALCSIZE_PARAMS));
        Rectangle proposed = ncParams.rectProposed.Rect;
        OnNonClientAreaCalcSize(ref proposed);
        ncParams.rectProposed = NativeMethods.RECT.FromRectangle(proposed);
        Marshal.StructureToPtr(ncParams, m.LParam, false);
    }

}

Note that this method calls a virtual OnNonClientAreaCalcSize method taking a Rectangle that you can overwrite in your code.

Painting the non-client area

The main message responsible for painting the non-client area is the WM_NCPAINT message. The WParam for this message contains the handle to a clip region or 1 if entire window should be repainted. So to paint anything we only need to create a Graphics object from the window handle and use it as we would in the typical OnPaint method.

private void WmNCPaint(ref Message msg)
{
    PaintNonClientArea(msg.HWnd, (IntPtr)msg.WParam);
    msg.Result = NativeMethods.TRUE;
}

Now is the tricky part; If you leave it that way you quickly notice that on some occasions you still get some parts of the standard border painted over your brand new framing. That indicates that there are some other messages that cause painting in the non-client area.

The first one is the WM_SETTEXT message that transports new title for the window (stored as Text property on the Form). Apparently it also repaints the border in order to update the title bar. Of course, we still want to send out the new title so we need to pass the message to the DefWndProc method. But we will handle painting on our own.

private void WmSetText(ref Message msg)
{
    DefWndProc(ref msg);
    PaintNonClientArea(msg.HWnd, (IntPtr)1);
}

The second culprit happens to be the WM_ACTIVATE message that is responsible for switching the window active state. Window is active when it is the top level window that you interact with and it has different border to show that. When you switch to another window the first one updates its border to indicate that it has lost the focus. The WParam of this messages holds the window active state and is 1 when border should be drawn as active and zero otherwise. We will handle the painting and skip to the DefWndProc only when window is minimized.

private void WmNCActivate(ref Message msg)
{
    bool active = (msg.WParam == NativeMethods.TRUE);
    if (this.WindowState == FormWindowState.Minimized)
        DefWndProc(ref msg);
    else
    {
        PaintNonClientArea(msg.HWnd, (IntPtr)1);
        msg.Result = NativeMethods.TRUE;
    }
}

I agree that this is big design inconsequence and all painting should be done in one place but it is around for a long time and we must live with it. Now that we cleared this out we can get down to actual painting.

The most important thing here is to get the correct hDC handle and we wil use native GetDCEx function for that. It takes three parameters: the window handle, the clip region and option. First two we got already from the messages. As for the options the MSDN states that only WINDOW and INTERSECTRGN are needed, but other sources confirm that CACHE is required on Win9x and you need CLIPSIBLINGS to prevent painting on overlapping windows.

If we get a valid hDC we can quickly create the Graphics object using Graphics.FromHdc() method, paint our stuff and dispose it. Here it is worth noting that when we dispose a Graphics instance it will also automatically free the hDC so there is no need for calling the ReleaseDC manually.

private void PaintNonClientArea(IntPtr hWnd, IntPtr hRgn)
{
    NativeMethods.RECT windowRect = new NativeMethods.RECT();
    if (NativeMethods.GetWindowRect(hWnd, ref windowRect) == 0)
        return;

    Rectangle bounds = new Rectangle(0, 0,
        windowRect.right - windowRect.left,
        windowRect.bottom - windowRect.top);

    if (bounds.Width == 0 || bounds.Height == 0)
        return;

    Region clipRegion = null;
    if (hRgn != (IntPtr)1)
        clipRegion = System.Drawing.Region.FromHrgn(hRgn);

    IntPtr hDC = NativeMethods.GetDCEx(hWnd, hRgn,
        (int)(NativeMethods.DCX.DCX_WINDOW | NativeMethods.DCX.DCX_INTERSECTRGN
            | NativeMethods.DCX.DCX_CACHE | NativeMethods.DCX.DCX_CLIPSIBLINGS));

    if (hDC == IntPtr.Zero)
        return;

    using (Graphics g = Graphics.FromHdc(hDC))
    {
        OnNonClientAreaPaint(new NonClientPaintEventArgs(g, bounds, clipRegion));
    }
}

At the begining ot this method I use native GetWindowRect function to get the correct coordinates of the window. At this point the Bounds property is not accurate and especially during resizing seems to always stay behind. Next I validate window size as obviously no painting is needed when it is empty. The actual painting should be done in the virtual OnNonClientAreaPaint method.

Removing flicker with double-buffering

Unfortunatelly painting this way is fine only as long as you don't try to resize the window. When you do you will see very unpleasant flickering. Totally not cool. We need to apply double-buffering in order to fix it and I just found a cool mechanism in .NET Framework that should help with that.

There is a class called a BufferedGraphics buried in the System.Drawing namespace. It's the same class that is used when you set DoubleBuffered flag on any control. (To be honest I haven't checked if this class existed prior to .NET 2.0). There is also a factory class called BufferedGraphicsManager that we use to create such object. The Allocate method takes either an existing Graphics object or the targetDC handle. Having an instance of BufferedGraphics we obtain a real Graphics object, do the painting as usual, and then call the Render method to draw the buffered image to the screen (presumably using some form of bit blitting).

using (BufferedGraphics bg = BufferedGraphicsManager.Current.Allocate(hDC, bounds))
{
    Graphics g = bg.Graphics;
    OnNonClientAreaPaint(new NonClientPaintEventArgs(g, bounds, clipRegion));
    bg.Render();
}

Whew, the above code looks to simple to possibly work. And indeed it doesn't. It all looks good when the window stays active, but when it gets covered by another window suddenly all of the client area gets painted in black. So there is something missing, like establishing a clip region to exclude this area from bliting. I hope that someone smarter then me could help and figure out a better way to fix this.

A not so scary ghost story

There are two more things that need to be done in order to get perfectly drawn custom border. First thing is to completely get rid of XP themes on our window. We have already taken over all painting but when themes are turned on they also might affect other aspects of window. For example thay would likely change the window shape to something non-rectangular (like adding round corners) and obviously we want to prevent this. We will use the SetWindowTheme native function with empty parameters to completely disable theming on the current window. Note however that this will only affect the window itself so you don't need to worry that you loose theming on the control placed in it's content area.

As for the second thing, I wonder how many of you heard about windows ghosting feature? I didn't know about it until recently. Quoting MSDN "Window ghosting is a Windows Manager feature that lets the user minimize, move, or close the main window of an application that is not responding." Basically when the process doesn't respond to window messages within designeted time (hangs) the Windows Manager will finally loose patience and draw the window frame by itself allowing the user to do something with the application. This can hapen when the process executes some long running task (like query or complex processing) in the same thread as the windows message loop.

In theory for a well written application that delegates all heavy processing to background workers this should never happen. But I haven't written such application yet. This feature can be disabled using DisableWindowGhosting native function but only for the entire application. Now it's your decision whether you want to present the users with consistent user experience even on these odd occasions or you can cope with some occasional quirks but let the user control the situation all the time.

protected override void OnHandleCreated(EventArgs e)
{
    NativeMethods.SetWindowTheme(this.Handle, "", "");
    NativeMethods.DisableProcessWindowsGhosting();
    base.OnHandleCreated(e);
}

Handling mouse actions

After we have positioned and painted the border it's time to make it behave like the normal one does. One such behavior is indicating whether the form is sizable when mouse moves on the border. Another is that when we double-click on the title bar the windows maximizes or restores respectively. And of course the window recognizes when mouse is over or user clicks on the form buttons (minimize, maximize, close, and help). To make this work properly we need to tell the border how all these elements are positioned on our form. This is done in the WM_NCHITTEST message. The LParam holds the screen coordinates of current mouse position. As a result we should return a hit-test code telling the system what part of window the mouse is over.

private void WmNCHitTest(ref Message m)
{
    Point clientPoint = this.PointToWindow(new Point(msg.LParam.ToInt32()));
    m.Result = new System.IntPtr(OnNonClientAreaHitTest(clientPoint));
}

When mouse is on the border edge and the window is resizable we should return values like HTLEFT, HTTOPRIGHT or HTBOTTOM . When mosue is over one of the window buttons we return code for that buton (HTMINBUTTON, HTMAXBUTTON, HTCLOSE). To indicate that mouse hovers over the title bar we can pass the HTCAPTION value. Finally when mouse is inside the client rectangle we should return the HTCLIENT value.

Capturing mouse move is quite simple. The WM_NCMOUSEMOVE message delivers new mouse position each time it is moved over the non client area. Here, I am using the standard MouseMoveEventArgs to pass this to the virtual method.

private void WmNCMouseMove(ref Message msg)
{
    Point clientPoint = this.PointToWindow(new Point(msg.LParam.ToInt32()));
    OnNonClientMouseMove(new MouseEventArgs(MouseButtons.None, 0, 
        clientPoint.X, clientPoint.Y, 0));
    msg.Result = IntPtr.Zero;
}

To capture mouse click we should intercept the WM_NCLBUTTONDOWN and WM_NCLBUTTONUP messages, for the left mouse button (and similar for the other two). For all these messages the WParam contains the hit-test value that we returned when processing the WM_NCHITTEST message, and the LParam contains the screen coordinates of the mouse cursor. I'm using extended NonClientMouseEventArgs to pass all this information to the virtual method. In return the method should set the Handled flag to indicate that our application processed this message.

private void WmNCLButtonDown(ref Message msg)
{
    Point pt = this.PointToWindow(new Point(msg.LParam.ToInt32()));
    NonClientMouseEventArgs args = new NonClientMouseEventArgs(
        MouseButtons.Left, 1, pt.X, pt.Y, 0, msg.WParam.ToInt32());
    OnNonClientMouseDown(args);
    if (!args.Handled)
    {
        DefWndProc(ref msg);
    }
    msg.Result = NativeMethods.TRUE;
}

Continued in part two, where you learn how to actually construct a border from bitmap parts and how to handle title bar buttons.

posted on Monday, July 4, 2005 9:00 PM

Feedback

# re: Drawing Custom Borders in Windows Forms 7/23/2005 2:29 PM Mick Doherty
Just had a look at this in .net 2.

I started something like this a while back and never finished it. Just thought I would point out one small problem. Set the forms Opacity to less than 100% and the left and bottom edges are not drawn.

This can be overcome by creating a compatible bitmap and using bitblt via interop to draw the window to the bitmap update and then bitblt back to the window, instead of using GDI+ on a graphics object created from the Window DC.

# re: Drawing Custom Borders in Windows Forms 7/23/2005 9:05 PM Andy J
This is awesome thanks for posting it... I've made some changes to put it in my .net 1.1 forms... Lovin it... I'm gonna try to figure out the problem with the double buffering to... Awesome work though... thanks again

# re: Drawing Custom Borders in Windows Forms 7/24/2005 2:00 PM Andy J
if you make the following adjustments the form will be double buffered...

inside the nonclientareaform draw do this

offScreenBmp = new Bitmap(bounds.Width, bounds.Height);
offScreenDC = Graphics.FromImage(offScreenBmp);

using (Graphics g = Graphics.FromHdc(hDC))
{
//cliping rect is not cliping rect but actual rectangle
OnNonClientAreaPaint(new NonClientPaintEventArgs(g, bounds, clipRegion), ref offScreenDC, ref offScreenBmp);
}

then make overrides that pass the offScreenDC and offScreenBmp to the drawing methods in CustomBorderForm.cs... mine look like this...

protected override void OnNonClientAreaPaint(NonClientPaintEventArgs e, ref Graphics offScreenDC, ref Bitmap offScreenBmp)
{
OnNonClientAreaPaintBackground(e, ref offScreenDC, ref offScreenBmp);
OnNonClientAreaPaintForeground(e, ref offScreenDC, ref offScreenBmp);

e.Graphics.DrawImage(offScreenBmp, 0, 0);
}

then basically the OnNonClientAreaPaintBackground, and foreground paint to offScreenDC instead of e.Graphics... when they finish you paint the offScreenBmp and you got yourself easy double buffering without having to use win32 dll's...

I'm pretty sure that going the BitBlt route would be faster, but I see no effect on my application and it works beautifully...


Also of note I noticed that the method WmNCCalcSize when the NativeMethods.FALSE, was better left commented out because otherwise my version in .net 1.1 seemed to draw strange... it kept changing offsets depending on whethere that was true or false, so I just commented it out since the only time i could see it was being called was on the initial load... maybe that's a bad idea I don't really know, but for my .net 1.1 version I had to do it....

Anyways thanks again for the great code here Szymon



# re: Drawing Custom Borders in Windows Forms 7/24/2005 3:11 PM Andy J
actually with that double buffering I did, now when I move the window around it has a delay before it refreshes the non window area... may need to go the bitblt route...

# re: Drawing Custom Borders in Windows Forms 7/25/2005 3:57 AM Szymon
Thank you all for your kind comments. Mick I think I've seen your examples and I'm aware of this problem. I just didn't needed semi-transparent window yet ;-)

As for double-bufferring I guess it has to be done with the Bitblt and thats why I used the BufferedGraphics class which should encapsulate this. However this works strange so I have do this manually. Can anyone help with that?

# re: Drawing Custom Borders in Windows Forms 7/25/2005 8:21 AM Chris H
mm, Any .net 1.1 adaptation available ?
i tried to port it, but got stuck on some
System.OutOfMemoryException

# re: Drawing Custom Borders in Windows Forms 7/25/2005 8:36 AM Szymon
As I see there is great need for working .NET 1.x version. I will try to provide this as soon as possible (hopefully this evening). Please stay tuned.


# re: Drawing Custom Borders in Windows Forms 7/25/2005 8:03 PM Szymon
I have just finished porting this to .NET 1.x. As expected there werent many problems. The most troubles I had with resource files (I love the new resource handling tools in VS2005). I have published updated release on projectdistributor. Please read notes at the end of second part of the article for some details.

# Double Buffering Posted Above 7/25/2005 9:19 PM Andy J
Well I figured out why it was acting funny using the non BitBlt double buffer... that code works you still need the InvalidateWindow() method call though... but it gets rid of the flicker of the non client area...

# re: Drawing Custom Borders in Windows Forms 7/25/2005 9:25 PM Andy J
Sorry for the double post but I just checked out the issue Mick talked about and with the non BitBlt double buffering I put in mine, it still draws all the borders just fine with lower than 100% opacity... the non buffered is another story...

# re: Drawing Custom Borders in Windows Forms 7/29/2005 12:12 AM Andy J
I cleaned up my double buffering so you don't have to do all that overriding BS... here's the snippet I got it from another object I'm using in my form... only problem now is that the non client area is double buffered... but for some reason when things are dragged over the client area it messes up horribly drawing what has been dragged over it...

using (Graphics screenGraphics = Graphics.FromHdc(hDC))
{
using(Image i = new Bitmap(bounds.Width, bounds.Height))
{
using(Graphics g = Graphics.FromImage(i))
{
OnNonClientAreaPaint(new NonClientPaintEventArgs(g, bounds, clipRegion));
}

// Now draw all the graphics at once.
screenGraphics.DrawImage(i, bounds);
}
}

# Big Problem 7/29/2005 4:31 PM Andy J
Just running some more tests on this, and double buffered or not, there is something missing in the code to redraw the client area when it needs to be invalidated while a window is dragged over it... the double buffer I just posted above does get rid of the flicker of the text, etc. in the titlebar and borders, but if anyone is going to use this as is, they better keep it with TopMost = true... I'd love to find someway to fix this I just don't even know what causes it...

# re: Drawing Custom Borders in Windows Forms 7/29/2005 5:27 PM Andy J
pretty sure I fixed that part aswell... you need to add this in the formwithnoborder.cs


case (int)NativeMethods.WindowMessages.WM_SYNCPAINT:
{
this.DefWndProc(ref m);
break;
}

then with the code I posted above it double buffers just fine, and the content repaints when it needs to instead of waiting for the on idle paint to refresh which is what I think it was doing...

# re: Drawing Custom Borders in Windows Forms 7/31/2005 3:41 AM Andy J
alright the code above doesn't work right either.... I changed it to this and it works... there is probably a way to determine the region of the form that needs to be invalidated but this is a crude way of making the controls repaint when something is dragged over the custom form, border or not...

case (int)NativeMethods.WindowMessages.WM_SYNCPAINT:
{
foreach(Control c in this.Controls)
{
c.Refresh();
}
this.DefWndProc(ref m);
break;
}

only way I know of fixing the window dragged over the top issue... and it still has a set back because if the form doesn't have any controls on it, the actual form itself still paints what was dragged over it... so hell I'm just gonna make sure I fill my forms with a panel ;)

I don't know if I've helped here or not but I hope I haven't been a pest

# re: Drawing Custom Borders in Windows Forms 8/12/2005 6:32 PM Enes M
Does anyone have vb.net version of this? I have successfully converted the project with the conversion tool and fixed the compile errors, but left some things unfunctional (borders not painting). Caption bar does paint. Also if you add a menu control to the form it does not show at runtime, and paints incorrectly in design time.

# re: Drawing Custom Borders in Windows Forms 8/13/2005 11:40 AM Enes M
I forgot to mention that i'm working on .net 1.1 version.

# re: Drawing Custom Borders in Windows Forms 8/21/2005 6:20 PM Szymon
Sorry Enes, to keep you waiting (I had vacations). I haven't tried this on VB and can't help you much as I don't know much of this language. The only thing I can suggest is that you compile the project in C# and then reference the assembly in your VB project. You could than just change inheritance parent of your forms.

# re: Drawing Custom Borders in Windows Forms 9/5/2005 4:33 AM Codeable
Most excellent source! You have saved me HOURS of work. I do, however, have something to point out: When you disable a caption button (say the maximize button) the event still fires when you click it. I simpy added the following code to the DepressButton function:

if(!currentButton.Enabled)
{
return false;
}

Also, has anyone figured out why transparent windows don't draw correctly? I've tried both methods by Andy J to no avail.. If anyone has, please e-mail me! will@codeable.net


# re: Drawing Custom Borders in Windows Forms 9/5/2005 5:10 AM Szymon
Hi Will,
I'm glad that my article could help you, and many others. I still intend to fix and update the sources (I need to find the cure for transparancy and double-buffering issues) and finish the article but right now my day-to-day schadule is packed to its limits. Still thanks for pointing the error. If you find anything else please send it here. I'm also curious what use have you found to this code and would be happy if you could send me some screenshots.

# re: Drawing Custom Borders in Windows Forms 9/5/2005 7:13 AM Codeable
After playing with it for a bit, (and failing) I've decided to simply go the BitBlt route. This fixes all previously mentioned clipping and transparency issues on v1.1 for me.(current application hasn't been ported to 2.0 yet.) The code update doesn't take much, just a few additions to the NativeMethods, and yet another version of the PaintNonClientArea event-raising block. Here is my quicky PaintNonClientArea code to get it started: [NativeMethod additions should be easy: copy n paste from msdn :)]

IntPtr CompatiblehDC = NativeMethods.CreateCompatibleDC(hDC);
IntPtr CompatibleBitmap = NativeMethods.CreateCompatibleBitmap(hDC,bounds.Width,bounds.Height);
NativeMethods.SelectObject(CompatiblehDC,CompatibleBitmap);
NativeMethods.BitBlt(CompatiblehDC,0,0,bounds.Width,bounds.Height,hDC,0,0,NativeMethods.TernaryRasterOperations.SRCCOPY);

using (Graphics g = Graphics.FromHdc(CompatiblehDC))
{
OnNonClientAreaPaint(new NonClientPaintEventArgs(g, bounds, clipRegion));
}

NativeMethods.BitBlt(hDC,0,0,bounds.Width,bounds.Height,CompatiblehDC,0,0,NativeMethods.TernaryRasterOperations.SRCCOPY);
NativeMethods.DeleteObject(CompatibleBitmap);
NativeMethods.DeleteDC(CompatiblehDC);

Not much to it eh? Just keep in mind to add error trapping for those interops. Probably gonna tcf that block.

I'm currently using your class to *skin* my secure chat app - and since it's in pieces at the moment I don't have any screen shots for ya. [Should be done in a week] Although, once I'm done - your class will get some solid alpha testing while I alpha the new version. ha. I'll post feedback as it develops.

# re: Drawing Custom Borders in Windows Forms 9/5/2005 7:22 AM Szymon
Thanks for the update. I should try this as the time allows. This code is already heavy tested for several months in the business project I work on. Till now it didn't cause any unexpected errors or memory leaks. You should be aware however that it doesn't work well on Windows 2003 Server (it doesn't crash but the border simply don't draw). I don't expect my customers to use it on this platform but you should at least provide some fallback solution (turn on the standard borders). Anyway, let me know if you have any other suggestions or requirements.

# re: Drawing Custom Borders in Windows Forms 9/5/2005 2:13 PM Andrew
Awsome stuff, I've been playing with it all night and see a lot of potential for it with my stuff. Hopefully we can get around that Windows 2003 Server issue though :)

Anyway, I have a problem I haven't been able to get around with the Maximize/Restore button. Try Maximize and Restore back-and-forth; the Restore button doesn't show like it should. this.WindowState keeps returning Normal...Or is it just me?

# re: Drawing Custom Borders in Windows Forms 9/5/2005 3:39 PM Andrew
OperatingSystem os = System.Environment.OSVersion;

if ((os.Version.Major == 5 && os.Version.Minor == 1) // WinXP
|| os.Version.Major >= 6) // Vista / XP Compatible
_TopForm = new XPCompatibleForm();
else
_TopForm = new TopForm(); // 95, 98, 2000, 2003, etc


# re: Drawing Custom Borders in Windows Forms 9/6/2005 6:34 PM Szymon
Actually, I think I haven't tested the code that changes appearance of maximize/restore button as I didn't have alternative graphics for it in my skin.

The problem was that I was checking the WindowsState during NC_CALCSIZE message and it was still in Normal state when Window was being maximized. To fix this I added yet another handling method for the WM_SYSCOMMAND message. Now I switch the button appearances on the SC_MAXIMIZE and SC_RESTORE commands. Strangely it occurs that when you double-click on the title bar the codes are slightly different.

Anyway, I just uploaded the updated code to ProjectDistributor. It also includes the solution to double-buffering issues provided by Codeable. Big thanks to all of you for helping me out with this project.

# re: Drawing Custom Borders in Windows Forms 9/6/2005 6:38 PM Szymon
Andrew,
Have you actually tested this on Windows Vista! ? I think I read somewhere that it is bassed on Windows 2003 kernel so there might be problems with that as well. For now it is safe to assume it only works on Windows XP. However, I think it could work on Windows 2000 as well.

# re: Drawing Custom Borders in Windows Forms 9/10/2005 12:07 AM Codeable
Hey - I was lucky enough to get the chance to look at this again. Have you implemented a custom MainMenu yet? Or are you working on one? I've managed to get the MainMenu bar working (draws, and supports skin switching on/off) with a bit of hacky gdi and extending the client area. It's only my development build though, and needs to be done properly (with bitmap support.. etc etc) -- I just don't want to write the additional code if it has already been done for this class. And if it hasn't - no prob. I'll get right on it. I'll be watchin' for a comment.

Again, thanks for this class. Can't count how many hours I've saved.

# re: Drawing Custom Borders in Windows Forms 9/10/2005 6:46 PM Enes M
I've been trying to port this to vb.net version, and now i'm just stuck with borders not always painting(mostly when resizing the form, borders just stay in the client area and don't resize with the form). Since i compared class by class, i couldn't find any difference, except that i don't know in C# what (IntPtr)1 is. Here's a sub as an example:

private void DrawButton(CaptionButton button)
{
IntPtr hDC = NativeMethods.GetDCEx(this.Handle, (IntPtr)1,
(int)(NativeMethods.DCX.DCX_WINDOW | NativeMethods.DCX.DCX_INTERSECTRGN
| NativeMethods.DCX.DCX_CACHE | NativeMethods.DCX.DCX_CLIPSIBLINGS));
if (hDC != IntPtr.Zero)
{
using (Graphics g = Graphics.FromHdc(hDC))
{
button.DrawButton(g);
}
}
}

I've changed (IntPtr)1 to New IntPtr(1) in vb.net, but i think that this i where the problem is (although not sure). Can someone explain what value (IntPtr)1 is holding in the sub above.

# re: Drawing Custom Borders in Windows Forms 9/10/2005 8:51 PM Enes M
A small fix: when you press the resize,close or minimize button and move the mouse while holding the button it causes the window to resize,close or minimize respectively. Also when you hold one of the buttons the cpu spikes to 100%, and we don't want this.
Here's the fix for both of these:
1. add done = false; to depressbutton function

case (int)NativeMethods.WindowMessages.WM_MOUSEMOVE:
{
Point clientPoint = PointToWindow(PointToScreen(new Point(m.LParam.ToInt32())));
if (currentButton.Bounds.Contains(clientPoint))
{
if (currentButton.State == CaptionButtonState.Normal)
{
currentButton.State = CaptionButtonState.Pressed;
DrawButton(currentButton);
}
}
else
{
if (currentButton.State == CaptionButtonState.Pressed)
{
currentButton.State = CaptionButtonState.Normal;
DrawButton(currentButton);
}
}
done = false; //added 9/6/05 - fixes the button behavior when pressed and cursor is moved - we still need to loop until the button is released
break;
}


2. Add Sleep(1) to the depressbutton function when the loop starts to control the cpu

while (!done)
{
System.Threading.Thread.Sleep(1); //added to control cpu

You can play with the buttons to see the effects i'm talking about. watch your cpu when holding the button for a few seconds.

# re: Drawing Custom Borders in Windows Forms 9/15/2005 3:48 AM Szymon
Codeable,
No, I haven't done any work with menus. In my current project we don't use traditional menus so there was no need to customize them. But you are right that that this would be huge benefit for anyone to make them consistent with borders and other screen elements. If you have started any work on this and would like to share it I would be happy to include it in this project and help you make them work. I think that we should include support for customized statusbar as well.

# re: Drawing Custom Borders in Windows Forms 9/15/2005 3:55 AM Szymon
Enes M.,
Thanks for your feedback and bug fixes. I will test this out and include with the next release. As for the IntPtr it is "A platform-specific type that is used to represent a pointer or a handle." In C# it can be cast to and from an integer. But it should be all the same if use a constructor like you did. Do you still have problems in VB? You can always try to compile this in C# and reference in your VB project as external assembly. Then you can override the CustomDrawnForm class as needed.

# re: Drawing Custom Borders in Windows Forms 9/15/2005 5:12 PM Codeable
Excellent, I shall finish the main menu functionality, and have some good stuff this weekend. (have to fix a few issues that arise when the form is smaller than the menu)

Enes.. instead of casting an integer, make a new one for that method call:

Dim hDC As IntPtr = NativeMethods.GetDCEx(Me.Handle, New IntPtr(1),....

That should work.

# re: Drawing Custom Borders in Windows Forms 9/17/2005 7:21 PM Andrew
Szymon, I just tested it on Vista Beta 1 and it's no good :(
The border comes up completely black. It sorta' renders if I maximize/restore once but it doesn't draw completely.

So unless something changes by final Vista release, remove from my previous post:
|| os.Version.Major >= 6) // Vista / XP Compatible

I guess this is XP only otherwise. That might change though considering that it "sorta' rendered", meaning that there is something there trying. Still pretty cool to have as an XP feature to my software either way!

# re: Drawing Custom Borders in Windows Forms 9/19/2005 6:08 PM Codeable
Hey Szymon - I've finished the main menu implementation. It's a healthy amount of code update so I don't want to post it here. E-mail me (william@codeable.net) or tell me how to get you the code.

# re: Drawing Custom Borders in Windows Forms 9/23/2005 3:50 PM Enes M
Codeable,
Can you send the source to enes@qwest.net?

# re: Drawing Custom Borders in Windows Forms 9/23/2005 10:12 PM Codeable
En route.

# re: Drawing Custom Borders in Windows Forms 9/24/2005 7:05 PM Louis
Wow this, like many others, has saved me a bunch of time. This really isnt my area of expertise so it has been a learning curve for me which is great. The main problem I'm having is probably really trivial as well. When I change the img's used for the borders they dont change the application. Sounds fairly silly, I know. Even If I remove them all together, its still the original appearence, not even a error. Any suggestions would be muchly appreciated.

cheers,
Louis

# re: Drawing Custom Borders in Windows Forms 9/24/2005 8:42 PM Louis
Alrighty :D, i fixed my simple little problem. It was my Resource1.resx file, I just recreated it using the link on the next page. Hopefully I can contribute to this little project as much as everyone else. I know i'll be ripping through the code here in the next little while.

# re: Drawing Custom Borders in Windows Forms 9/25/2005 3:45 PM Codeable
Enes, Szymon:

Boolean logic bug in that code I sent. "OR instead of AND"

((keyData & Keys.Menu) == Keys.Menu) should be: ((keyData | Keys.Menu) == Keys.Menu)


protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if(this.EnableNonClientAreaPaint && ((keyData | Keys.Menu) == Keys.Menu)) //No menu please. :)
{
//TODO: Add implementation of menu requests.
return true;
}
else
{
return base.ProcessCmdKey(ref msg, keyData);
}
}

# re: Drawing Custom Borders in Windows Forms 9/25/2005 4:02 PM Szymon
Louis,

I'm glad the code was useful for you. At first I though your problem was that you can't change the form's appearance at runtime. I never though this should be needed so the Appearance is read only once when form is constructed. But if you think such feature would be required we can try to add it. Now, I don't fully understand what your problem was and how you fixed. Could you explain?

# re: Drawing Custom Borders in Windows Forms 9/25/2005 4:05 PM Szymon
Codeable,

I'm really sorry but I still haven't got time to take a look at your code (I have hard time at work and was out of town for the weekend), but you are at top of my personal to-do list. I will try to include your code in next release.

# re: Drawing Custom Borders in Windows Forms 9/25/2005 4:44 PM Louis
Szymon,

I was trying to replace the images with my own. But the Resources1.resx was not updating. So It was still using the images you sent with your code. I fixed this by recreating the .resx file manualy using the tools you mentioned on the next page.

# re: Drawing Custom Borders in Windows Forms 9/26/2005 12:35 AM Codeable
As a side note: I've had no problems changing the appearance at runtime. (and when you run the code I sent - you'll see that in action!)

Don't have to put me at the top of the list. :) I already have the code.

# re: Drawing Custom Borders in Windows Forms 10/7/2005 12:11 AM Codeable
I think I will see if there is a way to tap into the Form's implementation of the main menu and shift it south a bit. It would be much nicer to simply override the onpaint methods of the menu's and items. Then you could include a CustomMenuItem object and have this thing be the "Class to rule them all" haha.

# re: Drawing Custom Borders in Windows Forms 10/21/2005 8:26 AM Robin
Great work! Is it also possible to change the transparency of the NC-Area only?
I'm looking for the new vista look.

# re: Drawing Custom Borders in Windows Forms 10/26/2005 6:40 AM Codeable
Success! I was able to move the windows generated MainMenu down to the "appropriate" area. I did it in a spike, so no code samples yet (but you won't believe how easy it was). I will write it up this weekend using performance friendly customdraw instead of the hacky virtualmenuitems. Who need sleep!?

# re: Drawing Custom Borders in Windows Forms 11/5/2005 3:07 PM some problems i have with this s
first of all , thank you for your good work.

here are the problems.
when i use it as a mdi child form and minimize it then it does not seem as it should.

when i use is as mdi child form and click the control buttons then they dont do what they are supposed to do.

in normal use when i change the title height , it seems again weird.

thankx

# re: Drawing Custom Borders in Windows Forms 11/5/2005 3:36 PM some problems i have with this s
hello again..

cant see the help button is a plus.

i am using net 2.0 beta 2 by the way.

# re: Drawing Custom Borders in Windows Forms 11/25/2005 12:25 AM Timothy Akehurst
Hi,
I am currently trying as a fun project more then anything else to code a 'areo glass' effect around the NC Area of a window, much like in the upcoming vista.
The goal is to use per pixel alpha blended bitmaps. First attemps show them as black, and not showing the background at all (go figure). Using UpdateLayeredWindow will allow us to update the regions we want to paint. The only problem with this apart from effeciency is the fact that you have to set the Extended style of the form to
cp.ExStyle |= 0x00080000; // WS_EX_LAYERED
This will prevent the rest of the form from painting.

This is where I am stuck right now, if anyone has any comments or wishes to see the source that I currently have, drop me a mail at tim.akehurst@noos.fr.

Doing this under XP/2000 is possible. . WindowsBlinds will support the glass look and feel for Windows XP in their next release.

Thanks for your time,

Timothy Akehurst.
Style Cafe SARL, Paris France.

# re: Drawing Custom Borders in Windows Forms 12/3/2005 11:45 AM Jelle van der Beek
Hi,

I've read your article and you mentioned a problem with the BufferedGraphics object:

"...It all looks good when the window stays active, but when it gets covered by another window suddenly all of the client area gets painted in black. So there is something missing, like establishing a clip region to exclude this area from bliting. I hope that someone smarter then me could help and figure out a better way to fix this."

I don't know whether you've resolved this issue yet. In any way, I think you are refering to an issue I've also stumbled upon. BufferedGraphics.Render uses BitBlt internally, which ignores GDI+ clipping regions. I've filed this issue on ProductSupport:

http://lab.msdn.microsoft.com/ProductFeedback/viewfeedback.aspx?feedbackid=9dd77247-66db-4fff-b30e-2a97ba72a89e

To perform clipping, you will have to use GDI regions. This piece of code will create a clipping rectangle which excludes the client area:

private void OnWMNCPaint( ref Message m )
{
IntPtr hDC = NativeMethods.GetWindowDC( m.HWnd );
if( hDC != IntPtr.Zero )
{
IntPtr hrgnWindow = IntPtr.Zero;
IntPtr hrgnClient = IntPtr.Zero;
try
{
// GetClientRectangle should return the clientrectangle's position relative to the control's upper-left corner
Rectangle rect = GetClientRectangle();
hrgnWindow = NativeMethods.CreateRectRgn( 0, 0, Width, Height );
hrgnClient = NativeMethods.CreateRectRgn( rect.Left, rect.Top, rect.Right, rect.Bottom );
NativeMethods.CombineRgn( hrgnWindow, hrgnWindow, hrgnClient, NativeMethods.RGN.RGN_DIFF );
NativeMethods.SelectClipRgn( hDC, hrgnWindow );

using( Graphics g = Graphics.FromHdc( hDC ) )
{
PaintNonClientArea( g );
}
m.Result = IntPtr.Zero;
}
finally
{
NativeMethods.ReleaseDC( m.HWnd, hDC );
if( hrgnWindow != IntPtr.Zero )
{
NativeMethods.DeleteObject( hrgnWindow );
}
if( hrgnClient != IntPtr.Zero )
{
NativeMethods.DeleteObject( hrgnClient );
}
}
}
}

Is that the solution to your clipping problems?

# re: Drawing Custom Borders in Windows Forms 1/12/2006 1:22 PM Martin
Hi, I have strange problem with your sample code... when I rebuild it while the Design of the DemoForm.cs is open, it changes its size! Something adds some width and height to the form. I am using VS 2005 with .net 2.0

# re: Drawing Custom Borders in Windows Forms 1/23/2006 7:06 PM Abe Heidebrecht
This is an excellent job, and has really helped me develop the custom look that my bosses require. Unfortunately, I have run into a strange problem that only occurs when setting the Theme in windows to be the Classic Windows theme. If this is set, and you disable custom borders, and then reenable them, the behavior shows up. What happens is that the default windows controls are drawn over the custom controls in the upper-right corner of the screen when you move the mouse over the resizable borders. Also, if you have a larger than normal title bar, a line is drawn where the bottom of the default titlebar would be. The controls are also drawn on minimize/maximize. I ran Spy++ on the form, and it seems that the windows controls are being drawn in WM_SETCURSOR when the mouse is moved over the border, but it must be drawn in WM_MAXIMIZE and WM_RESTORE for the other two messages. I am running VS 2005, btw.

# re: Drawing Custom Borders in Windows Forms 1/23/2006 7:51 PM Szymon
Abe,
Thanks for the excellent bug report. Following your instructions I was able to reproduce the problem. However currently I'm working on another projects and it would be hard for me to find time to look into this one. As I wrote in the article Windows tries to optimize paintinc non-client area and there are some undocumented places outside WM_NCPAINT where parts of borders are painted. I need to capture all these massages and handle on my own instead of DefProc.

# re: Drawing Custom Borders in Windows Forms 1/25/2006 8:47 PM VJ
I am seeing the same problem as Martin above. The DemonForm size changes each time I build project. ALso using VS2005. Cannot get fixed size for not sizable application Window.


# re: Drawing Custom Borders in Windows Forms 1/25/2006 9:52 PM VJ
Just Confirmed that the form resize problme does not happen in VS2003. There must be something VS2005 is doing in design mode to show the cool new non-client stuff that also causes the size of form to change. Too bad. If anyone finds a fix for this please post...

# re: Drawing Custom Borders in Windows Forms 1/26/2006 9:25 PM Chase
Great work!

It is obvious you put a good amount of time and research into this little project, and the result is very much appreciated.

I have noticed a few things with the project using the latest release under VS 2003 (Win XP SP2); maybe you could tell me what I am doing wrong or at least steer me in the right direction:

1.) If I add a menu to your form, it displays properly in the designer but is painted underneath the titlebar when compiled and executed.

2.) The transparent corners of the upperLeft and upperRight are painting white (not transparent) when compiled and executed.

I have not yet made any source changes, these are just observations from testing the functionality your demo offered.

Thanks for the hard work!

# re: Drawing Custom Borders in Windows Forms 1/28/2006 8:48 PM Joe
Hi
I tried the demo and it is awsome.
Actually I wanted to ask if you were aware of the redimensioning problem ocuring when you press on the Taskbar's application Icon several time.
In fact if you try to do so you'll see the form size increasing, and I couldn't figure out why.
Do you have some clue about the reason?

# re: Drawing Custom Borders in Windows Forms 1/28/2006 9:32 PM Szymon
Joe,
I'm glad you like it.

Regarding 'redimensioning' I'm only aware of some problems when designing forms in VS 2005. Please tell me more of the issue: what .NET and Windows version you use, and do you use XP themes or windows classic.

# re: Drawing Custom Borders in Windows Forms 1/28/2006 10:55 PM Joe
I use XP themes and Visual Studio 2005.
But it isn't happening at design time but at run time:
if I click on the icon in the task bar to reduce or restor the app window, and I repeat the operation several time, the window size increase.
I tried to modify your source code to set the caption height to SystemInformation.CaptionHeight to see if something would change, but without any success.

# re: Drawing Custom Borders in Windows Forms 2/4/2006 8:35 AM Szymon
Hi all,
In case you haven't noticed I just uploaded new release. It fixed several issues reported above (yes, it fixes the redmiensioning bug in designer). So go ahead, download it, and let me know what else needs to be done here.

Thanks for all bug reports.

# re: Drawing Custom Borders in Windows Forms 2/6/2006 7:29 PM Antoine
Hi szymon, great work!
I have a strange problem. When i use your form as mdi child, it seems that i can't touch the header of these forms, i can't move them, minimize or maximize them or even close them.

The only thing it does is when i am at the 3/4 (x-coordinate) at the header (just before the three buttons) the cursor transforms from pointer to WE-resizer...

Has anyone noticed that?!?!

Thanks in advance

# re: Drawing Custom Borders in Windows Forms 2/6/2006 7:54 PM Szymon
Antoine,
As far you are second person I know that want's to use it for MDI forms. To be honest I havent' tested it at all. If time allows I will so what I can do to fix it.

# Problem in rendering Menus 3/12/2006 11:13 AM Anindya Chatterjee
Really nice work!

But I found a littile bit of problem when designing windows form inheriting your class in .net 1.1.

When I add a mainMenu item it get rendered a liitle bit lower of its usual position, just underneath the titlebar! Most amazing thing is that when I run it the menu completely disappeared from the form though it is visible in design mode!!!!

What does the problem lies beneath? PLease any one give me any idea to solve it.
my email: anidotnet@gmail.com

Thnks again

# re: Menus not showing up in .net 1.1 2003 3/25/2006 11:34 AM wing
Hi, I have noticed a few people in here have come accross the same problem as me, main menus not showing up!


They are there, they are just underneath the title bar, which I guess means that the client area is not position in the correct place, or is too big?!?!

I am finding it hard to traverse this code as I do not know much about the windows message loop and underlying API's


If anyone has solved this or could point to the section of code that needs to be modified, could they pls put the answer in here or email me??

jimparslow@hotmail.com


Thanx in advance


Best Regards


wing

# re: Drawing Custom Borders in Windows Forms 3/26/2006 5:55 PM wing
Well... I found the section of code that controls the client area, and changing this had no effect on where the menu was placed....

The menu seems to be attached to the top of the border.


This is a major problem..... and I can't seem to find a work around...

If anyone has managed this... pls let us know!!!

# re: Drawing Custom Borders in Windows Forms 3/27/2006 4:01 AM Szymon
Wing,
This project was orignaly written for .NET 2.0 and I haven't encountered any issues with the new ToolStripMenus. So one solution I recommend is to migrate to 2.0. However, some time ago I recived code for improved menus from Codeable (see discussion above). I haven't included this in my project but if you send me your email I can post it for you.

# re: Drawing Custom Borders in Windows Forms 3/27/2006 8:19 AM wing
Hi Szymon.....

I t would be great if you could email me that code, I have searched for your address but cannot find it...

My email is

jimparslow@hotmail.com
or
jimparslow@gmail.com


either address is good to be sent to.

I eagerly await your response.

Thanx again

wing!!

# To all Having Main Menu issue in 2003 .net V1.1 3/27/2006 9:33 AM wing
I recieved Codeable's code from Szymon, it addresses the problems that I and others have had in using the main menu.


It has a small issue with the "&" before a letter no longer underlining it, so I am unsure if hotkeys will still work... I am going to look into this... but for all who want a copy:

ftp://www.parslow.plus.com/htdocs/VirtualMenu/

It can be found here.

THanx to Codeable for this, and much thanx to Szymon for the initial project!!!

# re: Drawing Custom Borders in Windows Forms 3/27/2006 10:36 AM wing
Scrub that address....

http://www.parslow.plus.com/VirtualMenu/

I forgot to remove the ftp part!! bah!!!

# Mdi Forms having issues 3/28/2006 12:12 PM wing
Some of you have had the same problem as me, and that is the fact that when using Mdi forms the child form seems to lose all of the form resize functionality and the control buttons do not work.

After a little delving into this code, and learning far more about Windows Message loops and wndproc than I ever wanted to know I have found the problem.


The problem is that the code takes the current screen position and then translate that into the current window position, this works for all normal forms, however, it does not work for anything inside the mdi container, as the co-ordinate system is all wrong.

Here is some code that can be added, my project has some panels and menus and stuff, so the locations of these have to be taken into account, and I have not fully worked out how I am going to implement this yet, so this is just a hack!!!


public Point PointToWindow(Point screenPoint)
{
if(this.MdiParent != null)
{
//work out the area

int xPosition = this.Location.X;
int yPosition = this.Location.Y;

Point parentLocation = this.MdiParent.Location;

//Take into account the right menu bar
Form1 myForm = (Form1)this.MdiParent;


//height =
int height = screenPoint.Y - parentLocation.Y - ( myForm .menuHeight * 2) - yPosition;

//width =
int width = screenPoint.X - parentLocation.X - xPosition - myForm .controlWindowWidth;

return new Point(width, height);
}
else
{
return new Point(screenPoint.X - Location.X, screenPoint.Y - Location.Y);
}
}



The stuff from myForm are just offsets into the Mdi client area. You will have to work out this stuff for yourself.


Hope this helps people.

# re: Drawing Custom Borders in Windows Forms 3/28/2006 1:16 PM Mundane
Ah is that what it is......nice one wing I get it now


pffffft

# re: Drawing Custom Borders in Windows Forms 3/30/2006 12:32 PM Goride
Hi,
First of all -> you did a great work !!!
I'm still having problem with the form sizaing up when minimze and maximize (on run time - not designer).
Did you figured this out yet ?

# re: Drawing Custom Borders in Windows Forms 5/10/2006 1:04 PM Padodanle51
Congratulation that's a great project I'm french and your project help me a lot for understand a lot of things
Thanks again

# re: Drawing Custom Borders in Windows Forms 6/12/2006 3:33 PM David
hi,
I Found a Bug:

When in DesignMode you set the WindowState on "Maximized"
the Restore button is not shown (still Maximize).
I have tryed past in Load event this.WindowState = FormWindowState.Maximized; and this is the same problem. Can You Fix it?

Sorry for my English.

David.

# re: Drawing Custom Borders in Windows Forms 6/12/2006 6:45 PM Szymon
David,
Thanks for excellent bug report. As usual with Win32 things happen not in places where you would expect them to happen. Anyway I've already fixed this and uploaded the updated sources.
http://projectdistributor.net/Releases/Release.aspx?releaseId=357
Let me know if it works fine now.

Szymon.

# re: Drawing Custom Borders in Windows Forms 6/15/2006 9:03 AM David
Hi,
Your last release is not Compleable. When you unzip the file to Directotry -> open The solution -> Build Solution - make Error "Resourfe file Properties\Resources.resx" cannot be found.

And I have found a new bug:
Let Find "//TODO: should I also Release the hDC??"
the answer is YES You must in both cases (doubleBufer with/without). This bug is nice to see in TaskManager:
when you let show the GDI object column, you can see that the number of GDI object stil increased while repainting (and while mouseMoving over caption buttons).
Whe you use the "ReleaseDC" all work fine.

David.

# re: Drawing Custom Borders in Windows Forms 6/16/2006 8:49 PM Szymon
David,
You were absolutly right about this GDI leak. And I did another one when drawing buttons. I've added ReleaseDC in both places and it seems to be back in order. Big thanks for reporting this.

You can find updated sources here:
http://projectdistributor.net/Releases/Release.aspx?releaseId=359

This release also introduces form style librares (aka skin files) and built-in form style editor. Hopefuly this weekend I should write an article explaining how it works. But I'm sure you will figure this out easily and I hope you like.

# Problem with Codeables MainMenu implementation 6/19/2006 9:24 AM Cosmo
Hi!

First of all I would like to thank you Szymon für your great solution. I've learned much more from it than I have expected.

I know, you are discussing about the .net2.0 version of this solution. But I only have the .net1.1 available, so I have to use Codeables menu implementation if I want to have a MainMenu in my form. But I have a big problem with it. While the user32 function TrackMenuPoupEx tracks the mouse movement on the MainMenu, it throws an NullReferenceException after a while when moving between serveral menu items. Marshal.GetLastWin32Error says: Win32Error 6 - Which means "The handle is invalid". After this Exception the MainManu only pops up again when I restart the application.

I've experienced this error on different operating systems and am not able to fix it, because I do not have the skills like you and do not know how to deal with the win32 api.

If anyone has the same error, I would be happy to get some support from you. I'll also be glad with a workaround or any hint to more information about that issue.

The exeption message:
System.NullReferenceException: Object reference not set to an instance of an object.
at Kobush.WindowsUI.HookProc.Invoke(
Int32 nCode,
IntPtr wParam,
IntPtr lParam)
at Kobush.WindowsUI.NativeMethods.TrackPopupMenuEx(
IntPtr hmenu,
Int32 fuFlags,
Int32 x,
Int32 y,
IntPtr hwnd,
IntPtr lptpm)
at Kobush.WindowsUI.VirtualMenuItem.DoPopUp(
CustomBorderForm owner,
IntPtr hDC,
CustomBorderAppearance app,
VirtualMenuItem target)
at Kobush.WindowsUI.VirtualMenuItem.PopUp(
CustomBorderForm owner,
IntPtr hDC,
CustomBorderAppearance borderApp)

Many thanks,
cosmo

# re: Drawing Custom Borders in Windows Forms 6/22/2006 7:43 PM codeable
Long time no post here! I haven't worked on this in a while, I do have a fully functional class still functional without those errors... (I use it in the chat program I also haven't touched in months) I'm not sure what I did since it was a long time ago - but I'll look into this weekend, review all the comments since my last post, and show everyone what I have.

# re: Drawing Custom Borders in Windows Forms 6/27/2006 8:15 AM Cosmo
Hi codable,

thanks that you have noticed my bug report. I must add that the exception is released during changing between the menu entries of a main menu element (thus during the TrackPopupMenuEx function blocks). I used the Code from http://www.parslow.plus.com/VirtualMenu. I hope i didn't took the wrong one? At least I haven't found another location of your main menu implementation in this faq.

Sorry for my English.

Best regards,
cosmo

# re: Drawing Custom Borders in Windows Forms 6/27/2006 10:37 PM codeable
I looked over my code! After running into that same exception that was mentioned [and because "fixing" that bug would require me to write around the windows api] I opted to replace my menu implementation with a different model. You can find my source, with a sample here: http://files.codeable.net/skinnedform/.

Please keep in mind that my code was built with v1.1 in mind!

# re: Drawing Custom Borders in Windows Forms 6/28/2006 7:39 AM Cosmo
Thank you for the bug fix Codable! Now everything functions marvelously on WinXP. :)

My request concerned also to the .net 1,1 framework.

I would have to still mark one, when I tried out the SkinnedForm on a Win2000 computer with the last service pack, I have to state that with one click on the form the NonClientArea drawing is dropping out. Also the transparency of the corners disappears after resizing the form.

Looks as if this will become a larger building site. I think it would be too much you to ask to develop the whole solution also for the other operating system versions. It would be really great if you could tell me the resources to your skills concerning the WinAPI programming calls.
But shurely I will have to be occupied with it for a while. (mail@[myDomainName])

If you have still time and desire, to test the SkinnedForm also for Win2000 and Win98, I would be very grateful. If you want to, I also can send you then my way to maitain SkinThemes (similar as in the new 5.0 version of Szymons .NET2.0 implementation.).

Thanks again for your feedback.

Best Regards,
cosmo

# re: Drawing Custom Borders in Windows Forms 6/28/2006 7:48 AM Szymon
Cosmo & Codeable,
Thanks guys for the big effort you put to make it work on other systems. I didn't expect to spin such big discussion. What would you say to join our efforts and maintain this solution as single project? If you agree I will try to setup project for this on SF or CodePlex.

Szymon

# re: Drawing Custom Borders in Windows Forms 6/28/2006 9:43 AM Cosmo
Nice, that sounds good. But it seems that this project must splitt into 2 different subprojects because of the difference between the .net1.1 and the .net2.0 implementations. Codable changed many parts of your Code...

And I still have to learn a lot about the WinAPI, so that I can help you effectively.
But I have a very good working knowledge of C#.NET in Version 1.1.
I could test everything intensivly and can give improvement suggestions.

Best regards,
cosmo

# re: Drawing Custom Borders in Windows Forms 7/12/2006 11:05 AM David
Hi Szymon,
Version growing up but quality go down :-)

I found a new 3 bugs.

1) when I set the WindowState on Maximized - the maximalization work not properly. The window begin not at top the screen and when you Close the form and run again and close and run... the top borde is traweling up and down with fixed steps.

2) When is the WindowState set on Normal - it`s funny to. Try maximize and restore from maximize (manytime) and look on height of the Window - it still increasing the Height of window.

3) The caption buttons are visible in Designer only sometime.

The project is masterpiece but with these few bugs usable.

# re: Drawing Custom Borders in Windows Forms 7/12/2006 11:10 AM Szymon
David,
Thanks for reporting this. Currently I'm in the process of moving the project to CodePlex so we can get some decent version control and issue trucking. I also hope to get some help from you guys :-)

# re: Drawing Custom Borders in Windows Forms 7/27/2006 8:22 AM Krys
Hi,

It will be fine if you can join the Ascend project. Like this there will be an bigger project for .NET controls and surely a "shared library" and surely all the code will be uniform.

It is just an idea ;-)

# re: Drawing Custom Borders in Windows Forms 8/1/2006 7:13 PM Szymon
Krys,
Thanks for your offer. I was honestly considering to contact you and joining Ascend. However today my project was accepted on CodePlex, so for now I will give it a try. Of course all your suggestions and help are welcome. The project is here: http://www.codeplex.com/Wiki/View.aspx?ProjectName=CustomerBorderForm

# re: Drawing Custom Borders in Windows Forms 8/3/2006 7:14 AM Resizing problems
Hi,
I just wanted to let you know that the resizing problem is still there. Start the demo form of the project, resize it so that you can monitor the changes easily and then do minimize/restore for a few times. You can see that the form grows slightly each time.
I've narrowed the problem to WM_NCCALCSIZE message handler. When you remove this handler the form is restored correctly. Also it seems that the difference between the client area as calculated by windows and the client area calculated manualy is exactly the amount that the form is increased by when it is restored.


# re: Drawing Custom Borders in Windows Forms 8/3/2006 7:29 AM Resizing problems
Hi,
Just one more thing. My guess is that when window manager sends WM_NCCALCSIZE message, takes the client rectangle that this message returns, increases it by the size of the non-client area (which, I guess, depends on the window style) and then sets the new size of the window.
One option would be to somehow tell windows the size of the non-client area in relation to the client area so that it recalculates the new size correctly (idealy the window manager should set the size of the form through Form.ClientSize and we would have no problems :( )
Second option is to somehow know that the window manager is setting the size of the form because of the 'restore window' command and then set the correct size ourselves.
Well I do not know that much about WinAPI to know where to start looking, hopefully you will have some ideas.


# re: Drawing Custom Borders in Windows Forms 8/18/2006 1:57 PM Codeable
It seems the nuances of working AROUND the winAPI are starting to catch up with us! Remember, with this model we are still "letting" the OS do most of the work, then we add our own special features. It won't be easy! Bear with us.

# Titlebar Button Behavior 9/19/2006 4:12 PM
I've compiled the code and done some playing around with the resulting window. After a couple of minutes, I realized Minimize, Maximize, and Close button didn't behave like standard ones. Hold the mouse pointer over any of those buttons and press the left mouse button down. You don't even need to release the button (click) for the button to activate. The normal behavior should be to activate only when the button is clicked. That is, the mouse can move anywhere once a "mousedown" is done on a button. If the button is released outside of the button it "mousedowned on", nothing happens. If the button is still being held down and the mouse returns to the button, the button activates on "mouseup".

# re: Title button position on MDI child SOLVED 11/13/2006 9:02 AM Nenad Prekupec
Location of the MDI child is position inside MdiClient control. SO relative positioning must be done according location of that control. How to get that control you can see inside FindMDIClient method written below.
To get position of button inside Caption bar you have to translate coords between screen and client coordinate systems.
Note that docked controls are not inside MdiClient control.

Hope this helps!
Regards,
Neno


public Point PointToWindow(Point screenPoint, ref Size relativePosition)
{
Point pnt;
if (parent.IsMdiChild)
{
FindMDIClient(parent.MdiParent); // better to set this inside ParentChanged event
relativePosition = new Size(new Point(parent.Location.X, parent.Location.Y));
relativePosition.Width += _mdiClient.Location.X; // adjust according MdiClient position
relativePosition.Height += _mdiClient.Location.Y;
pnt = parent.MdiParent.PointToClient(screenPoint);
pnt = parent.PointToScreen(pnt);
pnt = parent.PointToClient(pnt);
}
else
{
relativePosition = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
pnt = parent.PointToClient(screenPoint);
}

pnt -= relativePosition;
return pnt;
}

private MdiClient _mdiClient = null;
private void FindMDIClient(Form parentForm)
{
if (_mdiClient != null)
return;
// Get the MdiClient from the parent form.
for (int i = 0; i < parentForm.Controls.Count; i++)
{
// If the form is an MDI container, it will contain an MdiClient control
// just as it would any other control.
_mdiClient = parentForm.Controls[i] as MdiClient;
if (_mdiClient != null)
{
// The MdiClient control was found.
// ...
//

break;
}
}
}

# re: Drawing Custom Borders in Windows Forms 7/24/2007 1:22 PM Amin J. Ismaili
Hi All,

Well first let me tell that this is a great stuff. It really looks super cool.

But problems starts when I use this solution with MDI forms. The child form shows the custom border and button etc, but nothing works. I cannot move form, nor can I close it.

I read few posts about this above, but could not implement it.

Can anyone send me the code for that?

Thanks
Amin

# re: Drawing Custom Borders in Windows Forms 7/27/2007 6:13 AM Daniel Adams
# re: Drawing Custom Borders in Windows Forms

Changed the point to window location method to below:

public Point PointToWindow(Point screenPoint)
{
Point windowScreenLocation;
Point windowPoint = new Point();

if (this.IsMdiChild)
{
//Convert the windowScreenLocation into the MDI host relative co-ords
//since Location comes in as screen co-ords, and
//Location property is host relative
windowScreenLocation = this.Parent.PointToScreen(this.Location);
}
else
{
//Location is already screen relative
windowScreenLocation = this.Location;
}

//Offset the windowScreenLocation by the form location
windowPoint.X = screenPoint.X - windowScreenLocation.X;
windowPoint.Y = screenPoint.Y - windowScreenLocation.Y;

return (windowPoint);
}

# re: Drawing Custom Borders in Windows Forms 9/11/2007 9:33 AM HossFly
This is an absolutely fabulous project. Every now and then a web search roles up a real gem, and this is one of those times. Please keep up the good work.

Cheers,
HossFly

# re: Drawing Custom Borders in Windows Forms 10/10/2007 9:17 PM muse
There is link ?

# re: Drawing Custom Borders in Windows Forms 1/12/2008 12:41 AM badger2196
Has anyone converted this to C++ code? I would really like a copy if you have

#  Drawing Custom Borders in Windows Forms 2/16/2008 8:42 PM aqeel ahmed
I need to set my custom colors for title bar but my application is in vbdot Net how can i do in Vb DotNet


Regards,


Bhatti

# re: Drawing Custom Borders in Windows Forms 9/18/2008 7:15 AM AM
when i am using NativeMethods, then i am seeing fallowing error : NativeMethods is inaccesible due to its protection lavel.
Am i missing any assambly reference?.

# re: Drawing Custom Borders in Windows Forms 12/10/2008 2:32 AM Nikolay Yakovlev
To fix maximize/restore unexpected behaviour just override SetBoundsCore method in inherited form with this code:

protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
base.SetBoundsCore(x, y, width - 4, height - 34 + ActiveFormStyle.ClientAreaPadding.Vertical, specified);
}

> NativeMethods
You need to use NativeMethods that library contains not System.Windows.Forms.NativeMethods. This is private .NET class and can not be used inside your project.


Ps. Hope this helps

# re: Drawing Custom Borders in Windows Forms 12/10/2008 2:36 AM Nikolay Yakovlev
Oops, sorry.

protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
base.SetBoundsCore(x, y, width - 8 + ActiveFormStyle.ClientAreaPadding.Horizontal, height - 34 + ActiveFormStyle.ClientAreaPadding.Vertical, specified);
}

{8; 34} - Width and height of used window borders in classic theme (it's used to show window context menu)

# re: Drawing Custom Borders in Windows Forms 12/10/2008 1:08 PM vinod
Its not working properly in vista
Have any other updates?????????

# re: Drawing Custom Borders in Windows Forms 12/22/2008 12:38 PM Nikolay Yakovlev
2vinod, Yes.
See http://www.codeplex.com/CustomerBorderForm/WorkItem/View.aspx?WorkItemId=2542

# re: Drawing Custom Borders in Windows Forms 3/9/2009 1:45 PM Abdul Basit
AOA

dear your link to download latest release is not working. it gives an error
.

# re: Drawing Custom Borders in Windows Forms 3/19/2009 5:15 PM Ashutosh
Can you please provide the link download the source code. The link given in article is not working.

# re: Drawing Custom Borders in Windows Forms 8/11/2009 5:04 AM Makaray
Też chciałbym prosić o link do kodów źródłowych...Pozdrawiam

# re: Drawing Custom Borders in Windows Forms 8/11/2009 9:38 AM Szymon
I'm no longer developing this project but it's still hosted on CodePlex at: http://customerborderform.codeplex.com

# re: Drawing Custom Borders in Windows Forms 10/20/2009 8:29 AM none
About NativeMethods... (sorry for my english)
"this is private .NET class and can not be used inside your project." i don`t understant how cand i use this library if i can`t 'use it inside my project' :|

can anyone show a lil bit from a source code (with this NativeMethode)? :| I`m searching from a while and i can`t find anything

or someone that can share with us the sourscode for this problem...


# re: Drawing Custom Borders in Windows Forms 2/12/2010 9:48 AM Jimhack
The download link is broken, does anyone have a copy of source? If so, can you email to james@hcsseducation.co.uk.

# resize problem 9/16/2010 7:49 PM Dmkp
Could you post the fix for the resizing issue? I am still having it. (VS2010) Each time project is rebuilt, the form changes size.

# re: Drawing Custom Borders in Windows Forms 10/27/2010 11:58 PM Srinivas G
The source-code download link is broken. Could you please provide an alternative?

# re: Drawing Custom Borders in Windows Forms 7/12/2011 10:08 AM Simon
Hi,

very interesting article. But you should give us some hints which dll we need to reference. Its currently not working with 37 errors because he can't find the classes.


# re: Drawing Custom Borders in Windows Forms 10/18/2011 11:32 AM Anil
The download link is not working!

# re: Drawing Custom Borders in Windows Forms 11/5/2011 9:12 PM Greg
If you fail to trap the undocumented message WM_NCUAHDRAWCAPTION (0xAE), it is possible that Windows XP will draw the caption buttons over your custom caption. This can often be seen when moving the mouse into a popup window for the first time, although the custom caption may need to be shorter than standard to see it.

Post A Comment
Title:
Name:
Email:
Comment:
Verification: