views:

79

answers:

1

I am using WMSysCommand messages to modify Caption bar button ( Maximize / Minimize ) behaivor and recent update requiered to use WMNCHitTest, but I do not want to split these two related messages in multiplie procedures because of lengthy code.

Can I access private declaration ( message ) from other message? And if I can - How to do it?

  procedure TForm1.WMNCHitTest(var Msg: TWMNCHitTest) ;
  begin
    SendMessage(Handle, HTCAPTION, WM_NCHitTest, 0); // or other wParam or lParam ???? 
  end;

  procedure TForm1.WMSysCommand;
  begin
    if (Msg.CmdType = SC_MAXIMIZE or 61488) or (Msg.Result = htCaption or 2) then // if command is Maximize or reciever message of Caption Bar click
    begin
      if CheckWin32Version(6, 0) then
        Constraints.MaxHeight := 507
      else
        Constraints.MaxHeight := 499;
      Constraints.MaxWidth := 0;
    end
    else if (Msg.CmdType = SC_MINIMIZE or 61472) or (Msg.Result = htCaption or 2) then // if command is Minimize
    begin
      if (EnsureRange(Width, 252, 510) >= (510 / 2)) then
        PreviewOpn.Caption := '<'
      else
        PreviewOpn.Caption := '>';
    end;
    DefaultHandler(Msg); // reset Message handler to default ( Application )
  end;

Soo ... do I think correctly and sipmly do not know correct commands or I am thinking total bullsh*t?

Regards. Thanks for any help...

+2  A: 

Your code and text suggest you have several misunderstandings about how message handlers work. First, you ask about accessing private message handlers. You don't need access to private message handlers from parent classes. You can override the handler of any message, regardless of whether the parent class handles that message. Just write your message handler. It will automatically override the parent's handler, even if the parent's handler was private. (In fact, that's why we often declare message handlers private in the first place — descendants can always override them, and since there's no reason to call one directly, there's no reason to make it public.)

It looks like you're trying to get the base class's message-handling behavior by calling DefaultHandler. That will work sometimes, but only by accident. DefaultHandler goes to the base class's message handler. If there are other classes in between the base class and your descendant, a call to DefaultHandler will skip their handlers. Instead of that function, use the inherited directive, just like you would when overriding ordinary methods.

When you want your object to behave as if a message had been sent to it, you don't always have to send it a message with SendMessage. Instead, you can call the object's Perfrom method. All the same message-dispatch operations will occur, but you can skip the Windows message queue.

If you have two methods that should perform many similar tasks, you have a few options:

  1. Copy and paste the code so both functions look similar.
  2. Put all the code in one function, and then call it from the second function.
  3. Put all the code in a new, third function, and then call it from both functions.

The first option isn't usually a good idea. The second option can be good if the first function is guaranteed to always be a subset of the second function. If it needs to do something that the second function won't always want, though, then it's not appropriate to call it from the second function. The third option is what Robert's answer suggests.

The second option might be what you need, if my crystal ball is working properly. I think you wish for your wm_SysCommand handler to do some hit testing, so you want to call the wm_NCHitTest message handler. That's easy.

procedure TForm1.WMSysCommand;
var
  Hit: DWord;
begin
  Hit := Perform(wm_NCHitTest, ...);
  if (Msg.CmdType = SC_MAXIMIZE) or (Hit = htCaption) then // if command is Maximize or reciever message of Caption Bar click
  begin
    if CheckWin32Version(6, 0) then
      Constraints.MaxHeight := 507
    else
      Constraints.MaxHeight := 499;
    Constraints.MaxWidth := 0;
  end
  else if (Msg.CmdType = SC_MINIMIZE) or (Hit = htCaption) then // if command is Minimize
  begin
    if (EnsureRange(Width, 252, 510) >= (510 / 2)) then
      PreviewOpn.Caption := '<'
    else
      PreviewOpn.Caption := '>';
  end;
  inherited;
end;

Notice a few changes I made to your code. First, I use Perform to invoke the object's wm_NCHitTest handler, and I store the result in a variable. I use that variable in the next conditions to check where the mouse was clicked. Second, I removed the or tests from your conditionals. You were combining the named constants with their numeric equivalents, which is pointless and confusing. Third, I replaced the DefaultHandler call with one to inherited.

Beware, though: The wm_SysCommand message is sent for keyboard messages as well as mouse messages. There won't always be a valid hit test. You're probably going about this sys-command handler all wrong, but it's hard to tell what you're really after.

Rob Kennedy
Rob, "When you want your object to behave as if a message had been sent to it, you don't always have to send it a message with SendMessage. (...)"But what happens if I use it within loop with ProcessMessages command? Doesn't it means that Perform() will be in fact acting as exclusive SendMesssage() ?Oh - and numeric equals is just for myself to remember these damn numbers for internal and external exception handling and WER processings ... Actually - I can always check Keys code by Ord() so I do not tink it will be big issue ..
HX_unbanned
ProcessMessages has nothing to do with it. Perform is also useful for invoking message handlers on objects that don't have window handles.
Rob Kennedy