WinForms应用程序在用户界面(UI)方面的能力相较于WPF有所限制。但这是否意味着必须将整个应用从WinForms迁移到WPF呢?答案是不一定的。有时候,可以通过使用WPF控件来轻松地为WinForms应用进行“整容”。
顺便说一句,如果想升级Windows Forms应用程序,应该考虑HTML5而不是WPF。虽然个人非常喜欢使用WPF,但是升级到HTML5可以让以更低的成本获得一个多平台的Web应用程序,特别是如果使用转换工具自动进行转换的话。已经完成了多个这样的项目,这肯定比重写为WPF要容易。
然而,与此同时,可以使用“托管”——将WPF控件嵌入到Windows Forms应用程序中,以便在Windows Forms应用程序中利用WPF的丰富性(和经过一些练习后的易用性)。此外,在WPF中创建复杂的控件通常比在Windows Forms中创建它们要少花时间。
MSDN有一篇关于“在Windows Forms中托管WPF复合控件”的文章,但认为它太复杂了。在这里展示的是一个简单的例子,如何在Windows Forms应用程序中托管WPF控件,这种方式简单易学,并且从项目架构的角度来看也更加合适。
让从编写WPF控件开始。目标是一个简单的组合框控件,包含具有“客户”的“网格样式”属性:
<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>
如上XAML代码所示,定义了一个以客户为项目源的组合框。组合框有一个项目模板,用于以“网格样式”展示客户详细信息:在WPF中,这可以通过使用三个标签并将它们的上下文绑定到“客户”类属性来轻松完成。
这一步实际上更简单。创建了一个简单的类,它继承自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; }
}
}
这个类继承自ElementHost对象,该对象将WPF控件作为子元素持有。请注意,已经覆盖了默认的Designer Smart-Tag和DesignerSerializer属性,以防止设计器覆盖Child元素。
首先,只需要将新控件拖放到Windows Forms中:
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;
}
}