HwndHost element and RadDocking

0 Answers 130 Views
Docking
Giuseppe
Top achievements
Rank 1
Giuseppe asked on 12 Mar 2024, 06:16 PM

Hello,

I'm having some rendering issues with a HwndHost element (set as child of a Border object) that I've partially fixed using RadWindowInteropHelper.SetAllowTransparency method in a NonTrasparentWindowsGeneratedItemsFactory class.

However, there is still one problem, namely, when the window, which contains that Border element, is being maximised using the Telerik docking system, it doesn't render the content of the HwndHost element inside the Border (it looks empty). Also I've noticed that when the window is finally maximised, if the pointer goes over that empty area, the window disappears suddenly.

What could it be?

Thanks,

G.

Martin Ivanov
Telerik team
commented on 14 Mar 2024, 11:16 AM

Can you check the following demo and see if it helps? If not, can you send over a project showing how exactly your HwndHost is implemented and how the Telerik components are setup?
Giuseppe
Top achievements
Rank 1
commented on 14 Mar 2024, 03:42 PM | edited

Hello Martin,

Thanks for the reply. I already tried that example but it didn't help much, however I'll give another try. My HwndHost class, called SdlHost, uses an external C/C++ project to handle SDL 2 rendering by Core.View class. The initialisation of the SdlHost class is done roughly here:

 


private void SDLHostElement_SizeChanged(object sender, SizeChangedEventArgs e)
{
    lock (_lockRender)
    {
        if (SDLHostElement.ActualWidth > 0 && SDLHostElement.ActualHeight > 0 && _sdlHost == null)
        {
            SdlHost sdlHost = new SdlHost(SDLHostElement.ActualWidth, SDLHostElement.ActualHeight);
            sdlHost.Loaded += SdlHost_Loaded;
            sdlHost.HwndMouseEnter += SdlHost_HwndMouseEnter;
            sdlHost.HwndMouseMove += SdlHost_HwndMouseMove;
            sdlHost.HwndMouseLeave += SdlHost_HwndMouseLeave;
            sdlHost.HwndKeyDown += SdlHost_HwndKeyDown;

            SDLHostElement.Child = sdlHost;
            _sdlHost = sdlHost;
        }
        else
        {
            _repaint = true;
        }   
    }
}

private void SdlHost_Loaded(object sender, RoutedEventArgs e)
{
    UpdateSDLHostElementParams(SDLHostElementTypeUpdate.All);
}

private void SdlHost_HwndMouseEnter(object sender, HwndMouseEventArgs e)
{
    _sdlHost?.SetCursorCross();
}

private void SdlHost_HwndMouseMove(object sender, HwndMouseEventArgs e)
{
}

private void SdlHost_HwndMouseLeave(object sender, HwndMouseEventArgs e)
{
    _sdlHost?.SetCursorArrow();
}

private void SdlHost_HwndKeyDown(object sender, HwndKeyEventArgs e)
{
}


The SDLHostElement is a Border object inside an UserControl.

 

<Border Name="SDLHostElement" BorderThickness="0" SizeChanged="SDLHostElement_SizeChanged"/>

The MainWindow.xaml of the app looks like this below:

<telerik:RadDocking Grid.Row="1" HasDocumentHost="False">
    <telerik:RadSplitContainer Width="350">
        <telerik:RadPaneGroup>
            <telerik:RadPane Header="{x:Static resources:StringResources.Menu_OnlineHelp}" Name="HelpPane" CanFloat="False" IsHidden="True">
                <wv2:WebView2 Name="HelpBrowser"/>
            </telerik:RadPane>
        </telerik:RadPaneGroup>
    </telerik:RadSplitContainer>
    <telerik:RadSplitContainer>
        <telerik:RadPaneGroup>
            <telerik:RadPane PaneHeaderVisibility="Collapsed">
                <views:ProDockingControl x:Name="DockingControl" />
            </telerik:RadPane>
        </telerik:RadPaneGroup>
        <telerik:RadPaneGroup>
            <telerik:RadPane Header="{x:Static resources:StringResources.EventViewer}" Name="EventViewerPane" CanFloat="False" IsHidden="True">
                <views2:EventViewerWindow/>
            </telerik:RadPane>
        </telerik:RadPaneGroup>
    </telerik:RadSplitContainer>
</telerik:RadDocking>

While that ProDockingControl is this:

 

<telerik:RadDocking x:Name="Docking" HasDocumentHost="False" telerik:RadDocking.FloatingSize="100, 100"
                    BorderThickness="0" Padding="0" BorderBrush="{StaticResource TransparentBrush}" PreviewShowCompass="Docking_PreviewShowCompass" Close="Docking_PaneClose" RetainPaneSizeMode="DockingAndFloating">
    <telerik:RadDocking.DockingPanesFactory>
        <telerik:DockingPanesFactory/>
    </telerik:RadDocking.DockingPanesFactory>
    <telerik:RadDocking.GeneratedItemsFactory>
        <local:NonTrasparentWindowsGeneratedItemsFactory />
    </telerik:RadDocking.GeneratedItemsFactory>
    <telerik:RadSplitContainer InitialPosition="DockedLeft">
        <telerik:RadPaneGroup x:Name="MainGroup" telerik:RadDocking.SerializationTag="MainGroup" />
    </telerik:RadSplitContainer>
</telerik:RadDocking>

Finally I'm using the Telerik.UI.for.Wpf.60 (2023.1.315) package.

G.

 

Martin Ivanov
Telerik team
commented on 19 Mar 2024, 01:39 PM

Thank you for the additional information. However, my knowledge on this type of HwndHost is a bit limited. Would it be possible to wrap this in a runnable solution that I can test locally?
Giuseppe
Top achievements
Rank 1
commented on 17 Jun 2024, 03:46 PM | edited

Hello Martin,

Sorry for the delayed reply, I've been working on different aspects of my project in the meantime, however now I need to sort out this issue once at all. I've tried that demo project with my SDL code and it works, basically the issue seems to be related to the AllowTransparency property of the popups managed by the AutoHideArea styles. Is there a way to set it without defining an entire custom AutoHideArea template like that used by WinFormsInsideDocking_WPF sample project? I'm working with the default templates...

 

Thanks,

G.

Martin Ivanov
Telerik team
commented on 18 Jun 2024, 10:45 AM

To change the AllowTransparency setting, you can add a global event handler for the Loaded event of the AutoHideArea controls. In the event handler, you can access the Popup element and set the property.

static MainWindow()
{
    EventManager.RegisterClassHandler(typeof(AutoHideArea), AutoHideArea.LoadedEvent, new RoutedEventHandler(OnAutoHideAreaLoaded));
}

private static void OnAutoHideAreaLoaded(object sender, RoutedEventArgs e)
{
    var autoHideArea = (AutoHideArea)sender;
    var popup = autoHideArea.FindChildByType<Popup>();
    popup.AllowsTransparency = false;
}

Giuseppe
Top achievements
Rank 1
commented on 18 Jun 2024, 11:09 AM

Thank you very much Martin, now it looks much better.

I've found another related issue in the meantime (this happens in WinFormsInsideDocking project, too): when a pane is unpinned and I open it through the tab on the left side, if I move the mouse over the SDL area, the pane disappears again. Could it be something about the way how HwndHost manages the mouse events or something else?

Thanks,

G.

Martin Ivanov
Telerik team
commented on 18 Jun 2024, 01:52 PM

This happens when the AutoHideArea recieves mouse leave event, which happens also if you host non-WPF content in the undocked pane and mouse over it. There is no workaround for this behavior, except from changing the flyout behavior of the AutoHideArea from hover to click.
Giuseppe
Top achievements
Rank 1
commented on 18 Jun 2024, 03:06 PM | edited

Thanks, I've tried both behaviours as well as implementing a custom one, but no luck. However, there is an interesting thing: in my WndProc method the Win32 SetFocus call interferes with Telerik... below the piece of code where I call that native function:

case NativeMethods.WM_MOUSEMOVE:
    // If the application isn't in focus, we don't handle this message
    if (!_applicationHasFocus)
    {
        break;
    }

    // record the prevous and new position of the mouse
    _mouseState.ScreenPosition = PointToScreen(new Point(
        NativeMethods.GetXLParam((int)lParam),
        NativeMethods.GetYLParam((int)lParam)));

    _mouseState.WindowPosition = new Point(NativeMethods.GetXLParam((int)lParam),
        NativeMethods.GetYLParam((int)lParam));

    if (!_mouseInWindow)
    {
        _mouseInWindow = true;

        RaiseHwndMouseEnter(new HwndMouseEventArgs(_mouseState));

        // Track the previously focused window, and set focus to this window.
        _hWndPrev = NativeMethods.GetFocus();
        NativeMethods.SetFocus(_hwndHost);

        // send the track mouse event so that we get the WM_MOUSELEAVE message
        var tme = new NativeMethods.TRACKMOUSEEVENT
        {
            cbSize = Marshal.SizeOf(typeof(NativeMethods.TRACKMOUSEEVENT)),
            dwFlags = NativeMethods.TME_LEAVE,
            hWnd = hwnd
        };
        NativeMethods.TrackMouseEvent(ref tme);
    }

    if (_mouseState.WindowPosition != _previousPosition)
    {
        RaiseHwndMouseMove(new HwndMouseEventArgs(_mouseState));
    }

    _previousPosition = _mouseState.WindowPosition;

    break;

case NativeMethods.WM_MOUSELEAVE:

    // If we have capture, we ignore this message because we're just
    // going to reset the cursor position back into the window
    if (_isMouseCaptured)
    {
        break;
    }

    // Reset the state which releases all buttons and 
    // marks the mouse as not being in the window.
    ResetMouseState();

    RaiseHwndMouseLeave(new HwndMouseEventArgs(_mouseState));

    NativeMethods.SetFocus(_hWndPrev);

    break;
If I remove both calls, then all the issue finally looks fixed.
Martin Ivanov
Telerik team
commented on 19 Jun 2024, 11:59 AM

I am glad to hear that you found a solution. As for why exactly this happens, I am afraid that I can't tell. However, if omitting the SetFocus call in your code is troublesome and you manage to isolate the issue, feel free to share a project here.
Giuseppe
Top achievements
Rank 1
commented on 19 Jun 2024, 12:11 PM

Thanks Martin. I got this bit of code from another project and I suppose the SetFocus calls were placed for specific purposes that aren't longer needed in my current project.

No answers yet. Maybe you can help?

Tags
Docking
Asked by
Giuseppe
Top achievements
Rank 1
Share this question
or