WPF-Win32 Interop Part 1: Hosting WinForms Controls (DataGridView) in WPF Windows

It's no secret that Win32 has been an extremely successful GUI platform over the past 15 or so years. The overwhelming majority of desktop components and applications for Windows in existence today are Win32-based. So although WPF is (compared to Win32) a vastly superior platform, we did not really expect everybody to turn around a rewrite all of their existing code and applications in WPF. We knew that we had to provide a bridge between Win32 and WPF to allow easy migration between the two platforms.

There are quite a few ways in which you can use Win32 content in your WPF applications and I will cover them in several posts. This series of posts is based on the material available on the WPF/Win32 interoperability portal on MSDN.

I'll start by covering the simplest scenario of using Win32 content in a WPF application: hosting of a WinForms control in a WPF window. Since WPF currently does not have a DataGrid control out of the box, the code below will demonstrate hosting of the WinForms DataGridView control in a WPF application . A VS2005 project with the full source code is attached here.   

WinForms Control (DataGridView) in a WPF Window

In traditional Win32 programming user controls (buttons, listboxes, etc.) are created through a call to the CreateWindow or CreateWindowEx APIs. Every control has a unique (per session) identifier of type HWND (a handle to a Win32 window) and is recognized as a separate window (albeit a child window) by the window manager of the OS.

Windows Forms (aka WinForms) is a GUI framework, released as part of .NET 1.0, which provides a cleaner programming model around traditional Win32 that conforms to the paradigms of the .NET framework. Fundamentally, WinForms is a wrapper of Win32, so WinForms controls conform to the same WPF hosting restrictions as any other Win32 control.

Hosting of a WinForms user control in a WPF window is accomplished using the following steps:

1. In the XAML file declaring the UI of your WPF window, add a WindowsFormsHost element. As content of the WindowsFormsHost element, add the WindowsForms control you want to instantiate (DataGridView in this particular example):

<Window

x:Class="WinFormsControlInWpfWindow.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:wf ="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"

Title="WinForms Control (DataGridView) in a WPF Window"

Height="400" Width="750"

>

<StackPanel>

<Button Click="ButtonInsertSongOnClick">_Insert Song</Button>

<Button Click="ButtonDeleteSongOnClick">_Delete Song</Button>

<WindowsFormsHost>

<wf:DataGridView

x:Name="dataGridView"

Location="0, 0"

ColumnHeadersVisible="True"

SelectionMode="FullRowSelect"

MultiSelect="False"

SelectionChanged="DataGridViewOnSelectionChanged"

/>

</WindowsFormsHost>

</StackPanel>

</Window>

2. Note how the XAML file above defines the name space for your WinForms control (the “wf” namespace) and the assembly where the WinForms class is implemented:

<Window

...

xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"

...

>

...

</Window>

The directive above points to System.Windows.Forms, as that’s where the standard WinForms controls are implemented.

Note also that the DataGridView control is instantiated through XAML (see http://msdn2.microsoft.com/en-us/library/ms742875.aspx for XAML instantiation of WinForms controls). This is possible, because XAML provides a generic means of instantiating any .NET class and because of an interop technology known as Property Mapping:

<Window ...>

...

<WindowsFormsHost>

<wf:DataGridView

x:Name="dataGridView"

Location="0, 0"

ColumnHeadersVisible="True"

SelectionMode="FullRowSelect"

MultiSelect="False"

SelectionChanged="DataGridViewOnSelectionChanged"

/>

</WindowsFormsHost>

...

</Window>

The mark-up above is equivalent to the following code (see http://msdn2.microsoft.com/en-us/library/ms751761.aspx for code instantiation of WinForms controls):

...

public partial class MainWindow : System.Windows.Window

{

public MainWindow()

{

InitializeComponent();

}

private void WindowLoaded(object sender, RoutedEventArgs args)

{

WindowsFormsHost host = new WindowsFormsHost();

DataGridView dataGridView = new DataGridView();

dataGridView.Location = new Point(0, 0);

dataGridView.ColumnsHeadersVisible = true;

dataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;

dataGridView.MultiSelect = false;

dataGridView.SelectionChanged += DataGridViewOnSelectionChanged;

host.child = dataGridView;

...

}

...

}

Communicating WinForms Events to the WPF Window

WinForms events are convenient .NET wrappers around the underlying Win32 events. Because WinForms events conform to the .NET event programming model, you can directly reuse them in your WPF application.

You attach event handlers to a WinForms control the same way you would attach event handlers to a WPF control, i.e. in XAML mark-up:

<Window ...>

...

<WindowsFormsHost>

<wf:DataGridView

...

SelectionChanged="DataGridViewOnSelectionChanged"

/>

</WindowsFormsHost>

...

</Window>

Or, in code:

public partial class MainWindow : System.Windows.Window

{

...

private void WindowLoaded(object sender, RoutedEventArgs args)

{

...

dataGridView.SelectionChanged += DataGridViewOnSelectionChanged;

...

}

...

}

Then, you provide the implementation of the event handler in code:

public partial class MainWindow : System.Windows.Window

{

...

private void DataGridViewOnSelectionChanged(object sender, EventArgs args)

{

// Implementation of the event handler goes here

}

...

}

Tab and Accelerators Support for the Hosted WinForms Control

The WindowsFormsHost takes care of adding tab and accelerator support behind the scenes, so the hosted WinForms control is fully accessible without the need for any additional code. This is not necessarily the case when hosting unmanaged Win32 controls -- a topic which I will cover in upcoming posts.

WinFormsDataGridViewInWpfWindow.zip