the first thing that we need to do (after creating a new WPF project) is add a few references. You can do this by by right-clicking on the references folder in the solution explorer, and choosing "Add Reference":
Then you will get a dialog like this:
There are two .NET components you will want to add - first, System.Windows.Forms
, and then, all the way at the bottom (after you get to this dialog a second time),WindowsFormsIntegration
.
Once those two references are added, we have access to all of the WinForms controls, and access to the components that will allow us to embed them in WPF. So now lets get started on some code. First, we are going to take a look at embedding theDataGridView
using C#, so this means our XAML is going to be extremely simple:
<Window x:Class="WinFormsInWPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Using WinForms In WPF" Height="300" Width="300">
<Grid x:Name="_Container">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="1" Click="ClearClick" HorizontalAlignment="Right">
Clear All Rows
</Button>
</Grid>
</Window>
As you might notice, there is no reference to the DataGridView
anywhere in that XAML. All we have is a grid with two rows, and a button in the second row. This is because we are going to be shoving in the DataGridView
into the first row using C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms.Integration;
namespace WinFormsInWPF
{
public partial class Window1 : Window
{
private System.Windows.Forms.DataGridView _MyDataGrid;
private WindowsFormsHost _MyHost;
public Window1()
{
InitializeComponent();
_MyHost = new WindowsFormsHost();
_MyDataGrid = new System.Windows.Forms.DataGridView();
System.Windows.Forms.DataGridViewColumn col;
col = new System.Windows.Forms.DataGridViewColumn();
col.CellTemplate = new System.Windows.Forms.DataGridViewTextBoxCell();
col.Name = "Col 1";
_MyDataGrid.Columns.Add(col);
col = new System.Windows.Forms.DataGridViewColumn();
col.CellTemplate = new System.Windows.Forms.DataGridViewTextBoxCell();
col.Name = "Col 2";
_MyDataGrid.Columns.Add(col);
_MyDataGrid.Rows.Add(new object[] { "Item 1", "Foo" });
_MyDataGrid.Rows.Add(new object[] { "Item 2", "Bar" });
_MyHost.Child = _MyDataGrid;
_Container.Children.Add(_MyHost);
}
private void ClearClick(object sender, RoutedEventArgs e)
{
_MyDataGrid.Rows.Clear();
}
}
}
here in the constructor (after the InitializeComponent
call - we want all our XAML created stuff to be initialized first), we make a new WindowsFormsHost
(which is from the namespace System.Windows.Forms.Integration
). This object is the glue layer between WPF and WinForms. The WindowsFormsHost
is a FrameworkElement
, so it can be added to anything in WPF that can take an element. But it has a property Child
which takes in asystem.Windows.Forms.Control
- and this is the control that will be embedded.
So we create the WindowsFormsHost
and the DataGridView
, and then populate theDataGridView
with some info. You might be wondering why there is no using
statement for System.Windows.Forms
- and instead all the references are explicit. This is because there are some conflicts between the System.Windows.Forms
and the standard WPF namespaces - so if you do add a using
statement for System.Windows.Forms
, it is very likely ambiguous references will start to crop up in your code. So it is just safer and easier to explicitly reference the System.Windows.Forms
classes.
After the DataGridView
is all set up, we add it as the child for the WindowsFormsHost
, and then we add the WindowsFormsHost
as a child of our Grid
. Interacting with theDataGridView
in code works exactly as you might expect - for instance, the ClearClick
method which clears the current rows in the DataGridView
when the WPF button is clicked. The interaction on the user side of things also now pretty much just works, although there can be some quirks with focus and input that you may have to deal with (and will probably be specific to your particular situation). Here is a picture of the sample app in action:
Now onto how do this in XAML, instead of using C#. Here is our new XAML code:
<Window x:Class="WinFormsInWPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WinForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="Using WinForms In WPF" Height="300" Width="300">
<Grid x:Name="_Container">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<WindowsFormsHost Grid.Row="0">
<WinForms:DataGridView x:Name="_MyDataGrid">
</WinForms:DataGridView>
</WindowsFormsHost>
<Button Grid.Row="1" Click="ClearClick" HorizontalAlignment="Right">
Clear All Rows
</Button>
</Grid>
</Window>
As you can see, it got a bit more complicated. First, we added a new xmlns
attribute to the Window
tag. This pulls in the System.Windows.Forms
under the prefix WinForms
. The other change is the addition of the WindowsFormsHost
component. We add it just like any other WPF FrameworkElement
, but inside we get to declare a WinForms control - in this case a DataGridView
. We give our DataGridView
a name so we can refer to it later, and that‘s it for the XAML side. Clean, isn‘t it?
And the C# side gets cleaned up as well:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WinFormsInWPF
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
System.Windows.Forms.DataGridViewColumn col;
col = new System.Windows.Forms.DataGridViewColumn();
col.CellTemplate = new System.Windows.Forms.DataGridViewTextBoxCell();
col.Name = "Col 1";
_MyDataGrid.Columns.Add(col);
col = new System.Windows.Forms.DataGridViewColumn();
col.CellTemplate = new System.Windows.Forms.DataGridViewTextBoxCell();
col.Name = "Col 2";
_MyDataGrid.Columns.Add(col);
_MyDataGrid.Rows.Add(new object[] { "Item 1", "Foo" });
_MyDataGrid.Rows.Add(new object[] { "Item 2", "Bar" });
}
private void ClearClick(object sender, RoutedEventArgs e)
{
_MyDataGrid.Rows.Clear();
}
}
}
As you can see, all we have to worry about is the filling of the DataGridView
with some data (and it still needs to come after the InitializeComponent
call - otherwise nothing will have been created yet). The only downside to using XAML to embed the WinForms control is that you don‘t have any control over the constructor - the default no argument constructor is always used. Other than that, its nice and keeps the data/display separation paradigm intact.
To host the MaskedTextBox control
- Create a WPF Application project named HostingWfInWpfWithXaml.
- Add references to the following assemblies.
- WindowsFormsIntegration
- System.Windows.Forms
- Open MainWindow.xaml in the WPF Designer.
- In the Window element, add the following namespace mapping. The wf namespace mapping establishes a reference to the assembly that contains the Windows Forms control.
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
- In the Grid element add the following XAML.
The MaskedTextBox control is created as a child of the WindowsFormsHost control.
<Grid> <WindowsFormsHost> <wf:MaskedTextBox x:Name="mtbDate" Mask="00/00/0000"/> </WindowsFormsHost> </Grid>
- Press F5 to build and run the application.