Windows forms interop issue when detecting DockSite.ActiveWindow in rafted windows

Docking/MDI for WPF Forum

Posted 7 years ago by Samuele Gantner
Version: 12.1.0561
Platform: .NET 4.5
Environment: Windows 8 (64-bit)
Avatar

Hi,

we're facing some issues related to what appears to be a bug with the detection of DockSite.ActiveWindow in case of WindowsForms interop.

I have a minimalistic sample app I can send you for the repro. Basically it allows to create tabs which have as content a WindowsFormsHost containing a text box.

Repro steps:

  1. Add a 3 tabs
  2. Drag the last 2 tabs outside the window (so that they raft)
  3. Now switch active tab by clicking on the rafted windows

Result:

  • The dock site does not identify the currently selected window correctly
  • dockSite.ActiveWindow refers to a window which is no longer selected

Please, Let me know where I can send you the same app.

The reason why this is causing huge issues is that we implemented (among others) a feature for closing tabs using the key combination Ctrl+W. For this however it is clear that we need to know which tab/window is currently open. If the detection is wrong, we end up closing tabs the user doesn't want to close :)

Thanks,
Sam

Comments (5)

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

Hi Samuele,

WinForms interop has some known problems with focus, and focus events aren't always passed to WPF correctly. ActiveWindow gets updated based on things like the Activate method being called on a window or by focus changes. Because the focus isn't being properly passed to WPF content by WinForms there is no notification to update the ActiveWindow. You should watch for focus entering the interop controls and then call the containing window's Activate(false) method to get it to become the ActiveWindow. This should work around the issue you're having.


Actipro Software Support

Posted 7 years ago by Samuele Gantner
Avatar

Hi,

thanks for your reply. Unfortunately the solution you propose does not work. Activate does not change the ActiveWindow. On the other hand there is a fix that can be part of your library. In fact the DocumentWindowContainer.IsSelected property behaves correctly.

Our current strategy is to attach a DepencencyProperty ValueChanged callback on DocumentWindowContainer.IsSelected, and then use reflection to repair the wrong value in DockSite.ActiveWindow. But of course this is a pretty ugly and fragile trick :)

I would be very happy to send you a solution containing a full example of this.

Thanks,
Sam

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

Hi Samuele,

Hmm, after looking more at how ActiveWindow is updated, it is tied more to where WPF reports focus is, rather than Activate method calls.  ActiveWindow gets updated by watching PreviewGotKeyboardFocus events in some root containers.  So to get it to properly update, we somehow need to work around the WinForms interop issues with keyboard focus and get some element within the same docking window to report it's focused to WPF. 

Instead of calling Activate(false), try calling Activate(true).  That sets focus to a control in the docking window.

If that doesn't work try, calling Focus() right on the WindowsFormsHost.

Or if that doesn't work, try adding something like a Border wrapper around WindowsFormsHost, set Focusable="True" on it and call Focus() or the window's Activate(true) on it.


Actipro Software Support

Posted 7 years ago by Samuele Gantner
Avatar

Hi,

indeed we have a whole bunch of workarounds in place to make sure everything works. But this problem is slighly different as parts of Actipro already understand correctly where the focus is. The key here is that when you click inside some WindowsForms control the rafting window gets focused, and it's IsActive dependency property changes correctly.

So basically here is our workaround:

 

    public class RaftingWindowIsActiveAdjuster
    {
        private static readonly DependencyPropertyDescriptor IsActiveDependencyPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(
            DockingWindowContainer.IsActiveProperty,
            typeof (DockingWindowContainer));

        private static readonly PropertyInfo IsActiveProperty = typeof (DockSite).GetProperty("ActiveWindow");

        private readonly DocumentWindow documentWindow;
        private readonly DockingWindowContainer dockingWindowContainer;

        private RaftingWindowIsActiveAdjuster(DocumentWindow documentWindow, DockingWindowContainer dockingWindowContainer)
        {
            this.documentWindow = documentWindow;
            this.dockingWindowContainer = dockingWindowContainer;
            IsActiveDependencyPropertyDescriptor.AddValueChanged(dockingWindowContainer, (s, args) => OnIsActiveChanged());
        }

        private bool IsActive
        {
            get { return IsActiveDependencyPropertyDescriptor.GetValue(dockingWindowContainer).As().ValueOrDefault; }
        }

        public static void AdjustIsSelected(IRaftingWindowAdapter raftingWindowAdapter)
        {
            raftingWindowAdapter.DocumentWindow
                .WhenHasValue(d => raftingWindowAdapter.DocumentWindowContainer.WhenHasValue(c => new RaftingWindowIsActiveAdjuster(d, c)));
        }

        private void OnIsActiveChanged()
        {
            if (IsActive)
            {
                IsActiveProperty.SetValue(
                    documentWindow.DockSite,
                    documentWindow,
                    BindingFlags.NonPublic | BindingFlags.Instance,
                    null,
                    null,
                    CultureInfo.InvariantCulture);
            }
        }
    }

This said maybe your logic for detecting ActiveWindow could also be based on the IsActive state of DocumentWindow itself?

As a side note in case other people are interested, to ensure correct focus we:

  • Focus the WindowsFormsHost in case of clicks on the DocumentWindow (otherwise the selected style is not applied correctly)
  • Focus the WindowsFormsHost in case of clicks on the RaftingWindow (same issue as above)
  • Do some complex logic to handle Ctrl+Tab (for similar reasons)
Posted 7 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Samuele,

Please do send us your simple sample app, we'd like to see it for reference.  Send it to our support address and make sure you rename the.zip file extension so it doesn't get spam blocked.  Thanks!


Actipro Software Support

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.