How to always have active window highlighted?

Docking/MDI for WPF Forum

Posted 3 years ago by Tvema
Version: 16.1.0631
Avatar

Hello.

I have multiple scenarios where active window loses highlighted header. For example, my application has a standad wpf toolbar outside the docking site. Whenever I click a button on this toolbar, active ToolWindow loses its highligted header and border, making it impossible to tell which window is currently active. I experience similar problems when ToolWindow has an interop content. It looks like whenever active ToolWindow loses wpf focus highlighting disappears. I don't see how this can be a desired behaviour, but it is probably not an accident. So, my question is: 

Is there a way to always have active window highlighted? I am not happy with default behaviour, and I want to tie highligting to IsActive property ignoring the wpf focus completely.

[Modified 3 years ago]

Comments (10)

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

The active window highlighting tracks the focus throughout the app.  There isn't a way to change that.  That being said, there are some things you can do to help.

For the menu/toolbar, that's a good suggestion.  We worked on it and now have it not deactivating when other focus scopes (like menu/toolbar) get focus.  If you'd like a preview build to test with this functionality, please write our support address and reference this thread.

For the interop controls, please take a look at our Interop Compatibility topic in the Docking/MDI documentation.  That has properties you can set to make sure you don't run into airspace issues and to help track focus within them properly.  We have to have those workarounds since WPF doesn't track the focus into interop controls correctly.


Actipro Software Support

Posted 3 years ago by Tvema
Avatar

And why does it track focus? I can't think of a single reason why this would be useful. It obviously creates a lot of problems (at least in my case), but what are the benefits? It feels like I am missing something. Is there a use case where you do not what to have your active window highlighted?

I have read Interop Compatibility topic, and I've set the suggested properties, but I still had some problems with focus. If I remember correctly, it was with hosted winforms toolbar inside the DockingWindow: clicking a button inside this toolbar did not highlight the window. But I need to doublecheck that.

That is why I was actually trying an alternative approach, where I just hittest mouse clicks against open DocumentWindows and set active window this way. It worked surprisingly well with interop content, but thats when I encountered this issue with focus. Sometimes interop content captures the focus and even though the active window was changed correctly, user can't see that.

[Modified 3 years ago]

Posted 3 years ago by Tvema
Avatar

Also, if it's OK to ask that: what actually happens when I set InteropFocusTracking.IsEnabled="True"? I was thinking that it probably wires some mouse events from hosted content to host itself, but does it? Or is it more complicated than that?

Posted 3 years ago by Tvema
Avatar

I have just confirmed that if I put winforms toolbar

new ToolStrip(new ToolStripItem[] {new ToolStripButton {Text = @"Button"}});

inside the ToolWindow and then click the button, the window does not get focus and is not highlighted even if I set InteropFocusTracking.IsEnabled="True" on WindowsFormsHost. I didn't have a chance to test every form I have yet, but I get the feeling that this is not the last problem I'm going to encounter with interop content. So I would rather not deal with focus at all.

If default behaviour is not going to change anytime soon (if ever), do you think it is possible for me to override default window templates? Are there any examples of how to do that? I want to change the trigger which changes title color, so it binds directly to IsActive property, without wpf focus being involved. Do I need to purchase the source code in order to view default templates? I would rather not rewrite templates from scratch, because I am perfectly happy with default ones except for those couple of lines I want to change.

[Modified 3 years ago]

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Tvema,

Docking windows become active by nature when focus is moved inside of them.  That has always worked very well in the many years this product has been out and prevents anyone from having to capture mouse clicks (which also won't be reliable if you move focus with keyboard or programmatically) to determine which docking window is active.

The only real problems we've had in the past were with interop controls (due to bugs in core WPF not reporting focus properly with them) and that's why we added the InteropFocusTracking.IsEnabled attached property that you put on your interop hosts (like WindowsFormsHost) to help work around focus tracking in interop controls.  The logic behind that attached property has evolved over time and has handled any scenarios customers have encountered thus far.  I can put a WinForms Button, ToolBar w/ToolBarButton, or even a WebBrowser in a WindowsFormsHost with that attached property and it will track focus properly.  It seems that you found some edge case related to ToolStrip where it doesn't work though.

You can write our support address to request the default styles/templates but those won't help you in this scenario since all the logic for managing active window tracking is purely in codebehind.

The InteropFocusTracking has a lot of logic behind it but it effectively is hooking onto the host's Windows messages and watching for WM_ACTIVATE, WM_MOUSEACTIVATE, and WM_SETFOCUS.  Based on those scenarios, it will take action to report to the containing docking window when focus moves within it.

When debugging with a ToolStrip/ToolStripButton in place, it seems that the control must be eating WM_MOUSEACTIVATE and thus our helper class never gets notified of the mouse click.  Perhaps they do that so that tool strips on inactive Forms can be clicked without causing the Form to be inactive.  If you can find a way to have one of those messages above NOT get eaten by that particular WinForms control, then that would be the best solution here and should get things working with InteropFocusTracking. 

When looking at ToolStrip.WndProc in Reflector, you can see that WM_MOUSEACTIVATE is handled there and returns MA_ACTIVATEANDEAT in some cases.  Perhaps you could make a class that overrides ToolStrip and that method and coerces things so that MA_ACTIVATEANDEAT results are changed to MA_ACTIVATE.  That might be a way to solve the issue.


Actipro Software Support

Posted 3 years ago by Tvema
Avatar

Thank you for detailed reply. I will try the approach you suggested and will contact you regarding preview build we discussed earlier. I mean it still makes no sense to me that highlighting is not synchronized with IsActive property, but I guess I'll just have to learn how to deal with it.

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Tvema,

Actually the ToolWindowContainer title bar highlighting IS triggered by the IsActive property, all of which occurs in its default template.  The problem here is more that focus entering the WinForms ToolStrip is completely hidden from WPF and from our InteropFocusTracking due to WM_MOUSEACTIVATE being eaten.  So we never get told focus enters the ToolWindowContainer and thus ToolWindowContainer.IsActive remains false.  That's why I suggested changing things so WM_MOUSEACTIVATE is published up, which should hopefully allow us to detect it with InteropFocusTracking.


Actipro Software Support

Posted 3 years ago by Tvema
Avatar

But thats not what I observe. When I click the button outside the DockSite, DockSite.ActiveWindow does not change, DockSite.WindowDactivatedEvent does not trigger, and IsActive property on last active window is still true. Yet the highlighting disappears.

Same thing happened when I was experimenting with hit-testing. I could easily change active windows when user clicked inside the interop content by calling DocumentWindow.Activate(false) on window which passed a hit-test. This window then had its IsActive property set to true. Yet again, it had no highlighting.

So there must be more to it, than a direct binding to IsActive property.

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Yes, the difference is that the title bar highlights are based on ToolWindowContainer.IsActive and not on whether the selected window is marked active.  ToolWindowContainer.IsActive watches focus and works with InteropFocusTracking to help manage appearance with WPF's interop control focus issues.  It all works well except for the side case of the WinForms ToolStrip since that eats the WM_MOUSEACTIVATE message.

DocumentWindow.Activate(false) means to make sure the document window is in the UI and its tab is selected but doesn't move focus.  If you passed true to it, then that window would become the active window.


Actipro Software Support

Posted 3 years ago by Tvema
Avatar

I am going to share my experience in case someone else encounters similar issues:

1) 16.1.0633 build feels like a half-measure. It does address some of the issues with focus, but not all of them. For example, now when you click on Toolbar outside the dock site, active window remains highlighted. But if you click on regular button instead - highlighting disappears. Same thing happens when you click on focusable custom control. Maybe it will get better in future versions, but as of now highlighting logic does not make any sense to me.

2) I was able to override default templates in order to get the behaviour I need though. It looks like you need to override three templates. First, you need to override AdvancedTabItem style. Add the following trigger to template:

<DataTrigger Binding="{Binding IsActive}" Value="True">
   <Setter TargetName="TabChrome" Property="UntintedBackground" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BackgroundActiveSelected}" />
   <Setter TargetName="TabChrome" Property="UntintedBorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderBrushActiveSelected}" />
   <Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ForegroundActiveSelected}" />
</DataTrigger>

 It will bind highlighting of document window to its IsActive property, instead of binding it to HighlightKind property of AdvancedTabItem. Then you need to do the same for ToolWindowContainer. Replace:

<Trigger Property="IsActive" Value="True">

 with

<DataTrigger Binding="{Binding SelectedWindow.IsActive, RelativeSource={RelativeSource Self}}" Value="True">

Finally, you might also want to override AdvancedTabControl style, if you what the color of highlighted border to match the color of active tab. Add:

<DataTrigger Binding="{Binding SelectedWindow.IsActive, RelativeSource={RelativeSource TemplatedParent}}" Value="True">
    <Setter TargetName="HighlightBorder" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=HighlightBrushActive}" />
</DataTrigger>

 Thats about it. I did not test this solution extensively, but so far it works. I wish there was an easier way to get the same result. Copy-pasting 700 lines of styles just to alter a few lines of code is really tiresome.

3) I did not succeed in making InteropFocusTracking work with System.Windows.Forms.ToolStrip. If anyone has a working solution, please post the code here or e-mail it to me (or both). Solution which involves hit-testing works though. In my case it looks like this:

//this is a handler for native mouse events (you can use SetWindowsHookEx to get those)
private void OnHookTriggerd(MouseHookEventArgs args)
{
   foreach (var window in _windows.Where(x => x.IsLoaded))
   {
      if (PresentationSource.FromVisual(window) == null) continue;
      var point = window.PointFromScreen(args.Position);
      if (VisualTreeHelper.HitTest(window, point) == null) continue;
      //"false" is important, we do not want to steal focus from focusable content
      window.Activate(false);
      return;
   }
}

 There are a few limitations. First, you have to override templates as shown above, in order to also get the correct highlighting. Second, if user manages to somehow move focus directly to ToolStrip from another window without using a mouse - this solution won't work for obvious reasons.

[Modified 3 years ago]

The latest build of this product (v2019.1 build 0681) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.