Introduction
In this article, I will demonstrate how to create a "grid like" combo-box and host it inside a Windows Form application.
Background
WinForms applications have limited user interface (UI) abilities compared with WPF. Does this justify transposing the whole application from WinForms to WPF?
In my opinion, not always. Sometimes you can easily give a “facelift” to your application using WPF controls.
As a side note, if you want to upgrade your Windows Form application, you should consider HTML5 and not WPF. While I personally love working with WPF, upgrading to HTML5 will get you a multi-platform web application at a lower cost, especially if you do it automatically using transposition tools. I've done many of these projects and it is certainly easier than re-writing it to WPF.
However, in the meantime, you can use “hosting” - embedding a WPF control inside your Windows Form application in order to take advantage of the richness (and easiness - after some practice) of WPF within a Windows Form application. Also, creating complicated controls in WPF usually takes less time than creating them in Windows Form.
MSDN has an article about "Hosting a WPF composite control in Windows Forms" but I believe it to be too complicated.
What I present here is an easy example of hosting a WPF control inside Windows Forms application in a way that is simple to follow and implement, and is also more suitable from the perspective of project architecture.
Stages
- Creating the WPF "Grid like" combo-box
- Creating the Windows form hosting Control
- Adding the control to Windows Form application
1. Creating WPF "Grid like" Combo-box
Let us start by writing the WPF control.
Our aim is a simple control of a combo-box containing "grid-like" properties of a “customer”:
XAML
<UserControl x:Class="WindowsFormsControlLibrary1.ComboBoxWithGrid"
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"
d:DesignHeight="50" d:DesignWidth="250">
<Grid>
<ComboBox x:Name="comboBox"
Margin="4"
ItemsSource="{Binding Customers}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label BorderThickness="1,1,0,1"
BorderBrush="Black" Content="{Binding Path=Name}" />
<Label BorderThickness="1,1,0,1"
BorderBrush="Black" Content="{Binding Path=Address}" />
<Label BorderThickness="1"
BorderBrush="Black" Content="{Binding Path=TelephoneNumber}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</UserControl>
As we can see in the XAML above, we define a combo-box with customer as an item source. The combo-box has an item template for presenting the customer details in a "grid-like" style:
In WPF, it is easily done using three labels and binding their context into the "Customer
" class properties.
Customer Class
public class Customer
{
public string Name { get; set; }
public string Address { get; set; }
public string TelephoneNumber { get; set; }
}
Then, all we have left to do is to write the WPF control code behind:
public partial class ComboBoxWithGrid : UserControl
{
public ComboBoxWithGrid()
{
InitializeComponent();
SelectedIndex = 0;
this.DataContext = this;
}
public int SelectedIndex
{
get
{
return comboBox.SelectedIndex;
}
set
{
comboBox.SelectedIndex = value;
}
}
public List<Customer> Customers { get; set; }
}
As we can see, the code behind is very simple, setting the DataContext
as this, exposing the Customers
list, for outside & for the XAML data binding, and also exposing the select
index for outside only.
2. Creating the Windows Form Hosting Control
This step is actually easier. I am creating a simple class that inherits from System.Windows.Forms.Integration.ElementHost
:
[Designer("System.Windows.Forms.Design.ControlDesigner, System.Design")]
[DesignerSerializer("System.ComponentModel.Design.Serialization.TypeCodeDomSerializer , System.Design", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design")]
public class ComboBoxWithGrid_WinformsHost : System.Windows.Forms.Integration.ElementHost
{
protected ComboBoxWithGrid m_WPFComboBoxWithGrid = new ComboBoxWithGrid();
public ComboBoxWithGrid_WinformsHost()
{
base.Child = m_WPFComboBoxWithGrid;
}
public int SelectedIndex
{
get
{
return m_WPFComboBoxWithGrid.SelectedIndex;
}
set
{
m_WPFComboBoxWithGrid.SelectedIndex = value;
}
}
public List<Customer> Customers
{
get
{
return m_WPFComboBoxWithGrid.Customers;
}
set
{
m_WPFComboBoxWithGrid.Customers = value;
}
}
}
This class inherits from ElementHost
object that holds the WPF control as a child.
Please notice that I have override back to default the Designer Smart-Tag and the DesignerSerializer
attributes in order to prevent an override of the Child
element by the Designer.
3. Adding the Control to Windows Form Application
First, we just need to drag & drop the new control inside our Windows Form:
And then, connect the customer list to the control by code-behind:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var customers = new List<Customer>();
for (int i = 0; i < 10; i++)
{
customers.Add(new Customer() { Name = "Name" + i,
Address = "Address" + i, TelephoneNumber = "TelephoneNumber" + i });
}
this.comboBoxWithGrid_WinformsHost1.Customers = customers;
this.comboBoxWithGrid_WinformsHost1.SelectedIndex = 6;
}
}