RoyalApps.Community.Avalonia contains projects/packages for AvaloniaUI.
This package contains a WinFormsControlHost with a custom lifecycle management. XAML based controls like the TabControl in Avalonia, detach and attach views dynamically when switching between tabs. In general, this is a good approach to make rendering and resource utilization efficient.
Putting a NativeControlHost in such a view can cause issues because every time the view gets detached and another one attached, the native control is destroyed and recreated. If you want to host legacy WinForms controls, for example, you might want to have more control over the lifetime of your user control.
The demo application as shown above, creates a view with a WinFormsControlHost for each tab. The WinForms control (also part of the demo app), simply contains a text box, prints an "instance id" and has a random background color to demonstrate that the instances "survive" and keep the state until manually disposed.
Install the RoyalApps.Community.Avalonia.Windows with NuGet:
Install-Package RoyalApps.Community.Avalonia.Windows
or via the command line interface:
dotnet add package RoyalApps.Community.Avalonia.Windows
You can find the WinFormsControlHost
in the namespace RoyalApps.Community.Avalonia.Windows.NativeControls
:
<UserControl xmlns="https://github.com/avaloniaui"
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:winForms="clr-namespace:InteropDemo.WinForms;assembly=InteropDemo.WinForms"
xmlns:nativeControls="clr-namespace:RoyalApps.Community.Avalonia.Windows.NativeControls;assembly=RoyalApps.Community.Avalonia.Windows"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="InteropDemo.Views.TestView"
Padding="10">
<nativeControls:WinFormsControlHost x:Name="WinFormsControlHost" x:TypeArguments="winForms:TestControl" />
</UserControl>
Note
Since the control is a generic typeWinFormsControlHost<T> where T : System.Windows.Forms.Control
, you need to specify the type of your WinForms control you want to host in thex:TypeArguments
attribute.
By default, the WinFormsControlHost<T>
creates a new instance of your type automatically and keeps track of the instance. You can subclass the control and override the OnCreateWinFormsControl()
method to create and return the instance of the WinForms control yourself.
To configure your control (e.g. set properties), use the OnLoaded()
override:
protected override void OnLoaded()
{
base.OnLoaded();
if (WinFormsControlHost.Control is not { } testControl)
return;
testControl.BackColor = Color.White;
}
In the view model you use as data context where you placed the WinFormsControlHost
, simply implement the IDisposeWinFormsControl
interface:
public partial class TabViewModel : ViewModelBase, IDisposeWinFormsControl
{
[ObservableProperty] private string _caption = "n/a";
public event EventHandler<WinFormsDisposeEventArgs>? DisposeWinFormsControl;
[RelayCommand] public void Close() => App.MainViewModel.RemoveTab(this);
public void RaiseTabClosing()
{
DisposeWinFormsControl?.Invoke(this, new WinFormsDisposeEventArgs(this));
}
}
Simply invoke the DisposeWinFormsControl
event and pass the instance of the view model to the WinFormsDisposeEventArgs
constructor.
The singleton class WinFormsLifetimeManager
keeps the instances of all WinForms controls in a dictionary as long as they are required (not manually disposed).
Note
The view model instance (which implementsIDisposeWinFormsControl
) will be used askey
for the dictionary. Depending on your view model implementation, you might see issues with this approach whenGetHashcode
orEquals
is overridden.
The WinFormsControlHost
inherits from NativeControlHost
and prevents the control from being destroyed. When an instance of the WinForms control is requested for a specific view model, you either get an existing instance if available, or an instance is created for you.
When you are done (e.g. the view model is getting removed for good), you invoke the DisposeWinFormsControl
event on your view model to signal the WinFormsLifetimeManager
the WinForms control can be destroyed/disposed.