The current handling of the WM_MOUSEACTIVATE
message in the dxBars unit does not take the MouseActivate
event into account. As a consequence of this it is impossible to control what happens when the user activates the application with a mouse click.
For example, we have an application with a ribbon and a number of forms docked to the main form. The docked forms are called "views".
The buttons on the ribbon are enabled and disabled depending of which view is active. We also have contextual tabs on the ribbon which depends on the active view.
This works fine as long as all views remain docked to the main form but if the user undocks a view, e.g. to move it to another monitor, the current behavior in dxBars breaks our enable/disable logic: As soon as the user clicks a button on the ribbon, the button activates the main form.
This becomes a problem if we for example have two views: One docked and one undocked. With the undocked view active, the user clicks a button. This causes the button to activate the main form which in turn deactivates the undocked view and activate the docked view thus changing the context. In effect it becomes impossible to click any button that depends on the undocked view being active.
Luckily Windows provides a mechanism to solve this problem: The WM_MOUSEACTIVATE
message. WM_MOUSEACTIVATE
is surfaced neatly by the VCL as the MouseActivate
event handler. By handling the MouseActivate
event on the main form we should be able to specify that a mouse click should not activate the window by setting the MouseActivate
to maNoActivate
. In this example we block activation if the form is already active:
Delphiprocedure TMyForm.FormMouseActivate(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y, HitTest: Integer;
var MouseActivate: TMouseActivate);
begin
if (not IsFormActive(Self)) then
MouseActivate := maNoActivate;
end;
Unfortunately this is where dxBars gets in the way. The way WM_MOUSEACTIVATE
is handled in dxBars it is not possible to override the handling with the MouseActivate event handler because dxBars unconditionally activate the window regardless of any prior handling of WM_MOUSEACTIVATE
.
I request that this behavior be changed so dxBars take WM_MOUSEACTIVATE
into account.
The following changes implement the change in the dxBars unit:
Delphi// Do not explicitly activate window in `WM_MOUSEACTIVATE` handler.
// Optional: Take prior handling of `WM_MOUSEACTIVATE` into account.
procedure TdxDockControl.WMMouseActivate(var Message: TWMMouseActivate);
begin
inherited;
if (FBarManager <> nil) and not IsDesigning and (dxBarGetParentPopupWindow(Self, True) = nil) then
begin
{$ifdef PATCHED}
// Only modify result if no one else did (e.g. parent MouseActivate event)
if (Message.Result = 0) then
{$endif PATCHED}
Message.Result := MA_NOACTIVATE;
dxSetZOrder(MainForm.Handle, HWND_TOP {$ifndef PATCHED}, True{$endif PATCHED});
end;
end;
Delphi// Optional: Take prior handling of `WM_MOUSEACTIVATE` into account.
procedure TCustomdxBarControl.WMMouseActivate(var Message: TWMMouseActivate);
begin
inherited;
{$ifdef PATCHED}
// Only modify result if no one else (e.g. parent MouseActivate event) did
if (Message.Result = 0) then
{$endif PATCHED}
Message.Result := MA_NOACTIVATE;
end;
Delphi// Do not activate the window if application is already active.
procedure TdxBarControl.DoBarGetFocus(ASelectedItem: TdxBarItemControl);
...
begin
...
if not IsCustomizing and not (IsPopup and ParentBar.Focused) and (dxBarGetParentPopupWindow(Self, True) = nil) then
// Only activate if application isn't already active. Required in order for custom WM_MOUSEACTIVATE handling to work.
dxSetZOrder(MasterForm.Handle, HWND_TOP, {$ifdef PATCHED}not Application.Active{$else PATCHED}True{$endif PATCHED});
...
end;
Only the modified calls to dxSetZOrder
are required. The rest are just to make custom WM_MOUSEACTIVATE
handling (e.g. MouseActivate event) work as it should.
Hello Anders,
You mentioned an application that has docked forms. Could you please clarify what docking mechanism you use? I would appreciate it if you share a sample project that demonstrates this usage scenario. We will examine this project and your code modifications.
We're using the DevExpress docking library but I don't think the problem has anything to do with that.
I'll try to find time to create a small test case next week.
Thank you for your cooperation, Anders. I look forward to your response.
Test case attached.
The "View B context" contextual tab is made visible.
Hello Anders,
Thank you for sharing the text project. I activated the View B floating panel, then I clicked the "View B" tab. However, the "View B" tab remains active. Is this behavior expected?
Ah, sorry. That's because of the MouseActivate event handler I added a to the main form. Without that event handler you will see the behavior I've described.

You can also just try to press the button on the contextual tab - it's impossible.
Hello Anders,
Thank you for the clarification. The key point is that the View A panel is the only control that can be focused when you focus the form. If you add an additional control on the main form, it will be focused instead of the View A panel. I modified your project to demonstrate this point.
Yes that would work for the test case - but unfortunately the test case is just a very simplified example used to demonstrate the problem. The logic of our actual applications are much more complex. For example not all views are docked forms and activating some other form or control will deactivate the view thus causing the contextual tab to be hidden.
The actual logic is more like this:
procedure TFormMain.DockPanelViewBActivate(Sender: TdxCustomDockControl; Active: Boolean); begin if (Active) then begin ActiveView := ViewB; ActiveControl := SomeDummyControl; // This was your workaround... end else ActiveView := ViewNone; // ...but this will cause the view to hide its context regardless end;
I understand that you're trying to find workarounds to avoid changing the base library but, as far as I've been able to determine, no workarounds are possible in this case because the problem is really caused by dxBars not taking
WM_MOUSEACTIVATE
into account. Fixing that would enable me to selectively not activate the main form on mouse clicks - for example if the user clicks on the ribbon.To my regret, we cannot suggest a solution for this task out of the box. Thank you for sharing details of your research and information about this usage scenario.
This was not a request for help. This is a report of a problem in your library which breaks a mechanism provided by Windows (
WM_MOUSEACTIVATE
) and the VCL (TForm.MouseActivate
event).Your answer appears to indicate that you disagree and are not going to do anything about it. If that is the case, say so directly.