在WinForms应用程序中嵌入WPF控件

WinForms应用程序在用户界面(UI)方面的能力相较于WPF有所限制。但这是否意味着需要将整个应用程序从WinForms迁移到WPF呢?答案是不一定的。有时候,可以通过使用WPF控件来轻松地给应用程序一个“整容”。

如果想要升级Windows表单应用程序,可以考虑HTML5而不是WPF。尽管个人非常喜欢使用WPF,但是升级到HTML5可以让以更低的成本获得一个多平台的Web应用程序,特别是如果使用转置工具自动进行升级的话。已经做过很多这样的项目,这确实比重写为WPF要容易得多。

然而,与此同时,可以使用“托管”——将WPF控件嵌入到Windows表单应用程序中,以便在Windows表单应用程序中利用WPF的丰富性(以及经过一些练习后的易用性)。此外,在WPF中创建复杂的控件通常比在Windows表单中创建它们要花费更少的时间。

MSDN有一篇关于“在Windows表单中托管WPF复合控件”的文章,但认为它过于复杂了。在这里展示的是一个简单的例子,如何在Windows表单应用程序中托管WPF控件,这种方式简单易学,并且从项目架构的角度来看也更加合适。

创建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中,这可以通过使用三个标签并将它们的上下文绑定到“Customer”类属性来轻松完成。

public class Customer { public string Name { get; set; } public string Address { get; set; } public string TelephoneNumber { get; set; } }

接下来,需要编写WPF控件的代码后端:

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; } }

如所见,代码后端非常简单,将DataContext设置为this,将Customers列表暴露给外部和XAML数据绑定,并且只将选择索引暴露给外部。

创建Windows表单托管控件

这一步实际上更简单。创建了一个简单的类,它继承自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表单中:

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; } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485