Jump to content
JonRobertson

Looking for some guidance on WM_NCCALCSIZE

Recommended Posts

I have very little experience with changing UI at the Windows message level. I am hoping someone can explain a behavior that is confusing to me. The purpose of my question is strictly educational. :classic_smile:

 

In the attached demo project, WM_NCCALCSIZE attempts to change the form's client area to the entire form, without the caption bar. Decreasing NCCalcSizeParams.rgrc[0].Top by 31 (scaled if needed) does what I expect:

 

CaptionBarNotShowing.png.8f413e83843bd02c1de1775ff923605a.png

 

Using a value of 30 gives me this:

CaptionBarShowing.png.f5e2624f8a70c929c82579e5e9a87aa3.png

 

In the second example, Windows does not recognize the caption bar as part of the window. For example, clicking in that area sends the mouse click message to the window under my form. That makes sense to me. What I don't understand is why changing the value that I decrease from .Top from 30 to 31 makes such a difference in whether the painted client area "includes" the caption bar, for lack of a better description. I would expect values between 1 and 30 to gradually hide (?) the caption bar, rather than all or nothing.

 

I also have not figured out how to determine that 31 is the value needed to accomplish the first image. I've tried various system metrics values such as SM_CYCAPTION, SM_CYFIXEDFRAME, and SM_CYDLGFRAME. Even combining SM_CYCAPTION with one of the SM_CY*FRAME values results in a value that is too small.

 

My current interest is in a non-themed or Windows themed application. I have looked at TFormStyleHook.WMNCCalcSize to see how the VCL handles WM_NCCalcSize when themed. But that didn't help me understand the behavior that I'm seeing without themes.

 

Thanks

WMNCCalcSize.zip

Share this post


Link to post
13 minutes ago, Kas Ob. said:

Have you tried WM_GETTITLEBARINFOEX

No, I didn't know about that Windows message. So I learned something today. :classic_smile:

 

The rcTitleBar.height coming back from WM_GETTITLEBARINFOEX is 23, which is the same value that I get from GetSystemMetrics(SM_CYCAPTION). Unfortunately that is not high enough to get the behavior that I'm expecting. Oddly (to me), the height for the various min/max/close buttons comes back as 24.

Share this post


Link to post

Searching the Internet kept landing me on the same approach, using "Self.Height - ClientHeight" the result was 39 pixel, and using print screen and painter to find the title bar height kept giving me 29 pixel, so some subtraction will be needed from the first, which i think it borders....

 

Anyway i found this https://stackoverflow.com/questions/28524463/how-to-get-the-default-caption-bar-height-of-a-window-in-windows

Which is does return 31 pixel, so it might be your solution, or at least it a lead to a solution.

 

Please share your finding with us.

Share this post


Link to post
15 minutes ago, Kas Ob. said:

Nice. I spent several hours searching for answers over the weekend and did not come across that calculation. I completely overlooked SM_CXPADDEDBORDER. That answers the question of how to determine the value to use. This works for me:

procedure TfrmMain.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
  inherited;
  var captionHeight := GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXPADDEDBORDER);
  var Scale := RoundTo(CurrentPPI / Screen.DefaultPixelsPerInch, -2);
  var NCCalcSizeParams := Message.CalcSize_Params;
  Dec(NCCalcSizeParams.rgrc[0].Top, Round(captionHeight * Scale));
end;

Does anyone know why using a value of one less than captionHeight results in the entire caption bar being painted?

 

Thanks again.

Share this post


Link to post

You could try DwmGetWindowAttribute along with DWMWA_CAPTION_BUTTON_BOUNDS to get the title bar height

Share this post


Link to post
13 hours ago, JonRobertson said:

Does anyone know why using a value of one less than captionHeight results in the entire caption bar being painted?

I still thinking about this, and as no one added any input then i add my assumption.

 

I think the logic behind this is very simple, Windows has its default drawing (themed or not) and if you are changing its parameters in whole or in some then you are on your own.

In examples might be easier to explain, like, while you change the height of an item in ListBox or ListView, then the default drawing/rendering for item will perform according to its default no matter what you have set, if you set the item height to 3 pixel then you will see the item drawn by OS stayed as default like 8 pixel and the items will overlap, same if you set them to something like 50 pixel, the system will not check and adjust the font size neither the image or checkboxes...

 

Its like an unspoken rule, if you are changing the height then you have to continue and override the default drawing, and draw your own title/item.. with your own drawing function and your own font, brush, pen....

Share this post


Link to post

One more thing that bothering me, and you might test and confirm as you or somebody have some insight on this:

 

Do the borders scale ?

I mean the border is there for reason and they are one pixel wide, for the window title, but when scaled to 200% do they become 2 pixel ? or they stayed as 1pixel? 

See, windows shadow bothered me in the past and they changed few times over many Windows versions, their scaling too.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×