I have a WPF user control that uses a Popup. This control is a plugin and can be loaded in the main AppDomain or in a separate AppDomain, and it is hosted in a Winforms form using ElementHost. When the plugin is loaded in the main AppDomain, and the popup is opened, tabbing between the fields of the popup instead moves focus to the first control of the popup windows parent. When it is loaded in a new AppDomain, the tab behavior works as expected/desired (it cycles through the controls in the popup window).
I have read through many similar, but not quite the same, questions here on SO and elsewhere, but none of the suggestions have helped.
It appears that the tab message is getting handled in the AddInHost (which comes from my use of FrameworkElementAdapters to marshal the WPF control across domain boundaries in out-of-domain case). My ultimate goal is to implement this as a Managed Add-in Framework addin, but I have pared that WAY down to simplify the repro.
In case it helps to have a more complete context, I have a git repo of the simplified repro
What can I do to make this behavior consistent?
WpfUserControl.xaml
<UserControl x:Class="MyPlugin.WpfUserControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" Background="White"> <Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="28" /> <RowDefinition Height="28" /> <RowDefinition Height="28" /> </Grid.RowDefinitions> <TextBox Grid.Row="0" Margin="3" /> <Button x:Name="DropDownButton" Grid.Row="1" Margin="3" HorizontalAlignment="Left" MinWidth="100" Content="Drop Down" Click="DropDownButton_OnClick" /> <Popup Grid.Row="1" x:Name="Popup1" Placement="Right" StaysOpen="True" PlacementTarget="{Binding ElementName=DropDownButton}"> <Border BorderBrush="Black" BorderThickness="1"> <Grid Background="White"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Label Content="Username:" Grid.Row="0" Grid.Column="0" Margin="3" /> <TextBox Grid.Row="0" Grid.Column="1" Margin="3" MinWidth="150" /> <Label Content="Password:" Grid.Row="1" Grid.Column="0" Margin="3" /> <TextBox Grid.Row="1" Grid.Column="1" Margin="3" MinWidth="150" /> <Button x:Name="SaveButton" Grid.Row="2" Grid.Column="1" Margin="3" HorizontalAlignment="Right" Content="Save" Click="SaveButton_OnClick" /> </Grid> </Border> </Popup> <Button x:Name="DoSomethingButton" Grid.Row="2" Margin="3" HorizontalAlignment="Left" MinWidth="100" Content="Do Something" /> </Grid> </UserControl>
Plugin.cs
public class Plugin : MarshalByRefObject { public INativeHandleContract CreateWpfUserControl() { return FrameworkElementAdapters.ViewToContractAdapter(new WpfUserControl()); } }
MainForm.cs (selected bits)
private void LoadPlugin(bool loadInSameAppDomain) { AppDomain appDomain; if (loadInSameAppDomain) { appDomain = AppDomain.CurrentDomain; } else { var appDomainName = Guid.NewGuid().ToString(); _appDomain = AppDomain.CreateDomain(appDomainName, AppDomain.CurrentDomain.Evidence, new AppDomainSetup { ApplicationName = appDomainName, ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory }); appDomain = _appDomain; } _plugin = (Plugin)appDomain.CreateInstanceAndUnwrap("MyPlugin", "MyPlugin.Plugin"); } private void loadPluginButton_Click(object sender, EventArgs e) { LoadPlugin(appDomainCheckBox.Checked); var pluginControl = FrameworkElementAdapters.ContractToViewAdapter(_plugin.CreateWpfUserControl()); elementHost1.Child = pluginControl; UpdateUi(true); }
My somewhat educated, though by no means authoritative, guess is that the issue is that WinForms and WPF expect to have exclusive access to the top window's message pump. Running each in its own AppDomain gives each exclusive control of a parent window and its message pump.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With