I'm working on a rather large project that uses custom forms and would like to draw the non-client area of these forms myself. I can't use vcl-styles because many of the forms need to have a caption bar and border in a color picked at run time and as far as I know, styles are application wide by design.
What I have accomplished so far is drawing the caption bar and border, drawing on the caption, disabling the minimize, maximize and close buttons and replacing them with my own. I achieved this by intercepting the WM_NCPaint and WM_NCACTIVATE messages and calling my own procedure after the inherited keyword when handling WM_NCACTIVATE and sending a WM_ACTIVATE message without handling the WM_NCPAINT message as such:
SendMessage(Handle, WM_NCACTIVATE, ORD(active), -1);
The reason I did this is because I couldn't get the TMainMenu to paint itself consistently, after stepping through the code it seemed that the WM_NCACTIVATE message being handled correctly paints the main menu. Another approach I tried there was calling UpdateItems on the main menu, but this didn't give any result.
I deactivated the top right buttons by handling the WM_NCHITTEST message as such:
procedure TBasicForm.WMNCHITTEST(var message : TMessage);
begin
inherited;
case message.Result of
HTMINBUTTONE, HTMAXBUTTON, HTCLOSE: message.Result := HTCAPTION;
end;
end;
I got my own buttons (which I draw in the procedure I call when Handling WM_NCACTIVATE) by handling WM_NCLBUTTONDOWN, this is not a perfect solution but, can easily be improved on. (I trust I do not need to elaborate on this.
So far this sounds pretty good, however.
- There is quite a lot of flickering when switching focus between forms certain ways.
- Sometimes the original top right buttons show up, though they don't react to the mouse anymore.
- When closing a form, The caption bar (and only the caption) bar reverts to its old appearance.
The direct question is, how do I solve these three issues? It might be that I'm going about this entirely the wrong way though, in which case the question is, how can I achieve a custom drawn caption bar and border, preferably without meddling with the functionality of the borders and caption bar too much and having the a main menu be drawn properly?
As I said, it's a rather large project with many forms, so changing things around in the form designer is one of the last things I'd consider doing.
To Reproduce the problems I'm experiencing, create a new form and handle WM_NCHITTEST, WM_NCACTIVATE and WM_NCPAINT as I described earlier.
...
procedure WMNCHITTEST(var message : TMessage); message WM_NCHITTEST;
procedure WMNCACTIVATE(var message : TMessage); message WM_NCACTIVATE;
procedure WMNCPAINT(var message : TMessage); message WM_NCPAINT;
...
implementation
...
procedure TBasicForm.WMNCHITTEST(var message : TMessage);
begin
inherited;
case message.Result of
HTMINBUTTONE, HTMAXBUTTON, HTCLOSE: message.Result := HTCAPTION;
end;
end;
procedure TBasicForm.WMNCACTIVATE(var message : TMessage);
begin
inherited;
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := clRed;
Canvas.Rectangle(0, 0, Width, GetSystemMetric(SM_CYCAPTION) + GetSystemMetric(SM_CYBORDER));
Canvas.Rectangle(0, 0, GetSystemMetric(SM_CXBORDER), Height);
Canvas.Rectangle(Width - GetSystemMetric(SM_CXBORDER), 0, Width, Height);
Canvas.Rectangle(Width - GetSystemMetric(SM_CXBORDER), Heigth - GetSystemMetric(SM_CYBORDER), Width, Height);
end;
procedure TBasicForm.WMNCPAINT(var message : TMessage);
begin
SendMessage(Handle, WM_NCACTIVATE, ORD(active), -1);
end;
...
Now, add a second form to the project, make sure both forms are created and switch focus between the two forms repeatedly (also try clicking the second form, then click the custom drawn caption bar of first form), this should result in some flickering and the close, min and max button showing up. closing the form (by pressing alt + f4) should briefly show the original caption bar.