Search code examples
c#wpfmaf

MAF Plugin UI content scrolling outside of bounds in main application


A little bit of background first, I have an application that uses the Microsoft AddIn Framework (MAF) that gets a WPF UI from the plugins (You can follow this Microsoft example to create one). This is up and working fine until the plugin content is large enough that the main form needs to scroll. When this happens, it scrolls outside of the bounds that it should.

Out of bounds scrolling image

In the image, you'll notice that the Plugin Label Top goes over Main Label Top when you scroll down some and at the bottom you'll only see Plugin Label Bottom. My code for the main form is the following:

<Window x:Class="WpfAddinTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfAddinTest"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525" Background="DarkGray">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition/>
            <RowDefinition Height="25"/>
        </Grid.RowDefinitions>

        <Label Grid.Row="0" Content="Main Label Top"/>

        <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
            <ContentControl Name="PluginHolder"/>
        </ScrollViewer>

        <Label Grid.Row="2" Content="Main Label Bottom"/>

    </Grid>
</Window>

ContentControl is what houses the plugin UI, I don't understand why the plugin UI is going over it's parent controls. I've tried housing it in different types of controls, such as a DockPanel and Grid, everything acts the same way.

Is there a special way to get this to function correctly?

If more code is needed, I'm happy to post it, https://github.com/middas/WpfAddInTest is my complete sample project that demonstrates this.

EDIT: Loading up the form in WPF Inspector, all I can see is an AddInHost control, it doesn't show any of the individual controls inside the ContentControl. Does this have something to do with it? WPF Inspector

EDIT 2: In trying anything I can think of, I was thinking that maybe it wasn't getting the right height when it placed the control in, so I had the plugin return the desired height and set the Height of the ContentPlaceholder manually based on what was returned; no luck. Here is what I tried:

I updated the AddIn contracts from GetInt() to GetHeight() and on the Plugin I have this method now:

public double GetHeight()
{
    _Control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

    return _Control.DesiredSize.Height;
}

Then on the hosting form, I have this now:

public MainWindow()
{
    _PluginPath = System.IO.Path.Combine(Environment.CurrentDirectory, "Pipeline");
    _AddInPath = System.IO.Path.Combine(_PluginPath, "AddIns");

    InitializeComponent();

    var warnings = new List<string>(AddInStore.Update(_PluginPath));

    _PluginToken = AddInStore.FindAddIns(typeof(IPlugin), _PluginPath, _AddInPath).FirstOrDefault();
    _Plugin = _PluginToken.Activate<IPlugin>(AddInSecurityLevel.FullTrust);

    var control = _Plugin.GetControl();
    PluginHolder.Height = _Plugin.GetHeight();

    PluginHolder.Content = control;
}

EDIT 3: Attempting to force the ZIndex doesn't seem to affect it either.

Panel.SetZIndex(control, -1);

Solution

  • I believe I've finally figured out the issue. I believe the problem is because the plugin UI is outside of the AppDomain, the ScrollViewer doesn't know how to clip the content properly. What I finally did that worked was create a callback to the main UI that the plugin can use to be given a height that it needs to fit in. If the plugin requires scrolling, the plugin can then handle it with it's own ScrollViewer.

    Here is the updated Contract:

    [AddInContract]
    public interface IPluginContract : IContract
    {
        INativeHandleContract GetControl();
    
        double GetHeight();
    
        void SetHostCallback(IHostCallbackContract callback);
    }
    

    IHostCallbackContract:

    public interface IHostCallbackContract : IContract
    {
        double GetHeight();
    }
    

    Now before the plugin returns the Control it can set the Height given by the main form:

    public FrameworkElement GetControl()
    {
        if (_Callback != null)
        {
            _Control.SetHeight(_Callback.GetHeight());
        }
    
        return _Control;
    }
    

    I have updated my Git repo (https://github.com/middas/WpfAddInTest) with the entire working solution. The only issue with it now is that the scrolling isn't double buffered so it flickers. I'll have to live with that though since it doesn't appear there is a way to fix though due to WPF rendering via DirectX.