演练:在 Windows 窗体中承载 WPF 复合控件

Windows Presentation Foundation (WPF) 提供用于创建应用程序的丰富环境。 但是,如果您在 Windows Forms代码上有大量投入,那么更有效的办法可能是用 WPF 来扩展现有的 Windows Forms应用程序,而不是从头开始重新编写应用程序。 常见的方案是将用 WPF 实现的一个或多个控件嵌入 Windows Forms应用程序。 有关自定义 WPF 控件的更多信息,请参见控件自定义

本演练引导您创建一个应用程序,该应用程序承载 WPF复合控件以在 Windows Forms应用程序程序中执行数据输入。 该复合控件打包到一个 DLL 中。 该一般过程可以扩展到更复杂的应用程序和控件。 本演练的设计在外观与功能上与演练:在 WPF 中承载 Windows 窗体复合控件几乎完全相同。 主要区别在于承载方案是相反的。

本演练分为两部分。 第一部分简要介绍 WPF复合控件的实现。 第二部分详细讨论如何在 Windows Forms应用程序中承载该复合控件、从该控件接收事件以及访问该控件的一些属性。

本演练涉及以下任务:

  • 实现 WPF 复合控件。

  • 实现 Windows 窗体宿主应用程序。

有关本演练中所阐释任务的完整代码清单,请参见 Hosting a WPF Composite Control in Windows Forms Sample(在 Windows 窗体中承载 WPF 复合控件示例)。

系统必备组件

您需要以下组件来完成本演练:

  • Visual Studio 2010.

实现 WPF 复合控件

本示例中使用的 WPF 复合控件是一个简单的数据输入窗体,它接受用户的姓名和地址。 当用户单击两个按钮之一来指示任务已完成时,该控件引发一个自定义事件,将该信息返回到主机。 下图演示呈现的控件。

WPF 复合控件

简单的 WPF 控件

创建项目

若要开始创建项目,请执行以下操作:

  1. 启动 Microsoft Visual Studio,打开**“新建项目”**对话框。

  2. 在 Visual C# 和 Windows 类别中,选择**“WPF 用户控件库”**模板。

  3. 将新项目命名为 MyControls。

  4. 为位置指定一个便于命名的顶级文件夹,如 WindowsFormsHostingWpfControl。 稍后会将宿主应用程序放入此文件夹中。

  5. 单击**“确定”**创建项目。 默认项目包含一个名为 UserControl1 的控件。

  6. 在解决方案资源管理器中,将 UserControl1 重命名为 MyControl1。

您的项目应当具有对以下系统 DLL 的引用。 如果其中有任何 DLL 默认情况下未包括在您的项目中,请将它添加到您的项目中。

  • PresentationCore

  • PresentationFramework

  • 系统

  • WindowsBase

创建用户界面

复合控件的user interface (UI) 是通过Extensible Application Markup Language (XAML) 实现的。 复合控件 UI 由五个 TextBox 元素组成。 每个 TextBox 元素都有一个充当标签的关联 TextBlock 元素。 底部有两个 Button 元素,它们是**“OK”(确定)“Cancel”(取消)**。 当用户单击其中任何一个按钮时,控件将引发一个自定义事件,将该信息返回到主机。

基本布局

多个不同的 UI 元素包含在一个 Grid 元素中。 可以使用 Grid 排列复合控件的内容,此方式与使用 HTML 中的 Table 元素大致相同。 WPF 还有一个 Table 元素,但 Grid 更小巧,因而更适于简单的布局任务。

下面的 XAML 显示了基本布局。 此 XAML 指定 Grid 元素中的列数和行数,从而定义控件的整体结构。

在 MyControl1.xaml 中,将现有 XAML 替换为以下 XAML。

<Grid xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
      x:Class="MyControls.MyControl1"
      Background="#DCDCDC"
      Width="375"
      Height="250"
      Name="rootElement"
      Loaded="Init">

  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>

  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>


...


</Grid>

向 Grid 中添加 TextBlock 和 TextBox 元素

在网格中放入一个 UI 元素,并将该元素的 RowProperty 特性和 ColumnProperty 特性设置为适当的行号和列号。 请记住行号和列号是从零开始的。 通过设置元素的 ColumnSpanProperty 特性,可以使该元素跨多列。 有关 Grid 元素的更多信息,请参见如何:创建网格元素

下面的 XAML 演示复合控件的 TextBoxTextBlock 元素及其 RowPropertyColumnProperty 特性(设置这些特性是为了使元素在网格中正确定位)。

在 MyControl1.xaml 中,将以下 XAML 添加到 Grid 元素中。

<TextBlock Grid.Column="0"
      Grid.Row="0" 
      Grid.ColumnSpan="4"
      Margin="10,5,10,0"
      HorizontalAlignment="Center"
      Style="{StaticResource titleText}">Simple WPF Control</TextBlock>

<TextBlock Grid.Column="0"
      Grid.Row="1"
      Style="{StaticResource inlineText}"
      Name="nameLabel">Name</TextBlock>
<TextBox Grid.Column="1"
      Grid.Row="1"
      Grid.ColumnSpan="3"
      Name="txtName"/>

<TextBlock Grid.Column="0"
      Grid.Row="2"
      Style="{StaticResource inlineText}"
      Name="addressLabel">Street Address</TextBlock>
<TextBox Grid.Column="1"
      Grid.Row="2"
      Grid.ColumnSpan="3"
      Name="txtAddress"/>

<TextBlock Grid.Column="0"
      Grid.Row="3"
      Style="{StaticResource inlineText}"
      Name="cityLabel">City</TextBlock>
<TextBox Grid.Column="1"
      Grid.Row="3"
      Width="100"
      Name="txtCity"/>

<TextBlock Grid.Column="2"
      Grid.Row="3"
      Style="{StaticResource inlineText}"
      Name="stateLabel">State</TextBlock>
<TextBox Grid.Column="3"
      Grid.Row="3"
      Width="50"
      Name="txtState"/>

<TextBlock Grid.Column="0"
      Grid.Row="4"
      Style="{StaticResource inlineText}"
      Name="zipLabel">Zip</TextBlock>
<TextBox Grid.Column="1"
      Grid.Row="4"
      Width="100"
      Name="txtZip"/>

设置 UI 元素的样式

数据输入窗体上的许多元素都有类似的外观,这意味着它们在几个属性的设置方面是完全相同的。 上面的 XAML 使用 Style 元素定义元素类的标准属性设置,而不是分别设置每个元素的特性。 此方法降低了控件的复杂度,并使您可以通过一个样式特性改变多个元素的外观。

Style 元素包含在 Grid 元素的 Resources 属性中,因此可供控件中的所有元素使用。 如果对样式进行命名,则可通过添加一个设置为该样式的名称的 Style 元素来将该样式应用于元素。 未命名的样式将成为元素的默认样式。 有关 WPF 样式的更多信息,请参见样式设置和模板化

下面的 XAML 演示复合控件的 Style 元素。 若要查看样式是如何应用于元素的,请参见上面的 XAML。 例如,最后一个 TextBlock 元素具有 inlineText 样式,最后一个 TextBox 元素使用默认样式。

在 MyControl1.xaml 中的 Grid 开始元素之后,紧接着添加以下 XAML。

<Grid.Resources>
  <Style x:Key="inlineText" TargetType="{x:Type TextBlock}">
    <Setter Property="Margin" Value="10,5,10,0"/>
    <Setter Property="FontWeight" Value="Normal"/>
    <Setter Property="FontSize" Value="12"/>
  </Style>
  <Style x:Key="titleText" TargetType="{x:Type TextBlock}">
    <Setter Property="DockPanel.Dock" Value="Top"/>
    <Setter Property="FontWeight" Value="Bold"/>
    <Setter Property="FontSize" Value="14"/>
    <Setter Property="Margin" Value="10,5,10,0"/>
  </Style>
  <Style TargetType="{x:Type Button}">
    <Setter Property="Margin" Value="10,5,10,0"/>
    <Setter Property="Width" Value="60"/>
  </Style>
  <Style TargetType="{x:Type TextBox}">
    <Setter Property="Margin" Value="10,5,10,0"/>
  </Style>
</Grid.Resources>

添加“OK”(确定)和“Cancel”(取消)按钮

复合控件的最后两个元素是**“OK”(确定)“Cancel”(取消)**Button 元素,它们占据 Grid 的最后一行的前两列。 这些元素使用常用事件处理程序 ButtonClicked,以及在前面的 XAML 中定义的默认 Button 样式。

在 MyControl1.xaml 中的最后一个 TextBox 元素之后,添加以下 XAML。 复合控件的 XAML 部分现已完成。

<Button Grid.Row="5"
        Grid.Column="0"
        Name="btnOK"
        Click="ButtonClicked">OK</Button>
<Button Grid.Row="5"
        Grid.Column="1"
        Name="btnCancel"
        Click="ButtonClicked">Cancel</Button>

实现代码隐藏文件

代码隐藏文件 MyControl1.xaml.cs 实现三个基本任务:

  1. 处理当用户单击某个按钮时发生的事件。

  2. TextBox 元素中检索数据,并将它们打包在一个自定义的事件参数对象中。

  3. 引发自定义的 OnButtonClick 事件,通知主机用户已完成并将数据传回主机。

该控件还公开许多颜色和字体属性,使您可以更改外观。 与用于承载 Windows Forms控件的 WindowsFormsHost 类不同,ElementHost 类仅公开控件的 Background 属性。 为了保持本代码示例与演练:在 WPF 中承载 Windows 窗体复合控件中讨论的示例的相似性,该控件直接公开其余的属性。

代码隐藏文件的基本结构

代码隐藏文件包含一个命名空间 MyControls,该命名空间包含两个类,即 MyControl1 和 MyControlEventArgs。

namespace MyControls
{
  public partial class MyControl1 : Grid
  {
    //...
  }
  public class MyControlEventArgs : EventArgs
  {
    //...
  }
}

第一个类 MyControl1 是一个分部类,它包含实现在 MyControl1.xaml 中定义的 UI 的功能的代码。 当分析 MyControl1.xaml 时,XAML 会转换为相同的分部类,两个分部类会合并以组成编译后的控件。 因此,代码隐藏文件中的类名必须与分配给 MyControl1.xaml 的类名匹配,并且它必须继承自控件的根元素。 第二个类 MyControlEventArgs 是一个事件参数类,用于将数据发回主机。

打开 MyControl1.xaml.cs。 更改现有类声明,使其具有以下名称并从 Grid 继承。

public partial class MyControl1 : Grid

初始化控件

下面的代码实现几个基本任务:

  • 声明一个私有事件 OnButtonClick 以及它的关联委托 MyControlEventHandler。

  • 创建几个存储用户数据的私有全局变量。 这些数据是通过对应的属性公开的。

  • 为控件的 Loaded 事件实现一个处理程序 Init。 此处理程序通过将 MyControl1.xaml 中定义的值赋给全局变量来初始化这些变量。 为此,它使用指定给典型 TextBlock 元素 nameLabel 的 Name 来访问该元素的属性设置。

删除现有构造函数并向 MyControl1 类添加以下代码。

public delegate void MyControlEventHandler(object sender, MyControlEventArgs args);
public event MyControlEventHandler OnButtonClick;
private FontWeight _fontWeight;
private double _fontSize;
private FontFamily _fontFamily;
private FontStyle _fontStyle;
private SolidColorBrush _foreground;
private SolidColorBrush _background;

private void Init(object sender, EventArgs e)
{
    //They all have the same style, so use nameLabel to set initial values.
    _fontWeight = nameLabel.FontWeight;
    _fontSize = nameLabel.FontSize;
    _fontFamily = nameLabel.FontFamily;
    _fontStyle = nameLabel.FontStyle;
    _foreground = (SolidColorBrush)nameLabel.Foreground;
    _background = (SolidColorBrush)rootElement.Background;
}

处理按钮的 Click 事件

用户通过单击**“OK”(确定)按钮或“Cancel”(取消)**按钮来指示数据输入任务已完成。 两个按钮使用相同的 Click 事件处理程序 ButtonClicked。 两个按钮均有一个名称,即 btnOK 或 btnCancel,这样处理程序就可以通过检查 sender 参数的值来确定单击的是哪个按钮。 处理程序执行以下操作:

  • 创建一个 MyControlEventArgs 对象,该对象包含来自于 TextBox 元素的数据。

  • 如果用户单击**“Cancel”(取消)**按钮,将 MyControlEventArgs 对象的 IsOK 属性设置为 false。

  • 引发 OnButtonClick 事件,该事件通知主机用户已完成并传回所收集的数据。

在 MyControl1 类的 Init 方法后面添加下面的代码。

private void ButtonClicked(object sender, RoutedEventArgs e)
{
    MyControlEventArgs retvals = new MyControlEventArgs(true,
                                                        txtName.Text,
                                                        txtAddress.Text,
                                                        txtCity.Text,
                                                        txtState.Text,
                                                        txtZip.Text);
    if (sender == btnCancel)
    {
        retvals.IsOK = false;
    }
    if (OnButtonClick != null)
        OnButtonClick(this, retvals);
}

创建属性

该类的其余部分仅仅公开与前面讨论的全局变量对应的属性。 当属性更改时,set 访问器通过更改对应的元素属性并更新底层的全局变量来修改控件的外观。

将下面的代码添加到 MyControl1 类中。

public FontWeight MyControl_FontWeight
{
    get { return _fontWeight; }
    set
    {
        _fontWeight = value;
        nameLabel.FontWeight = value;
        addressLabel.FontWeight = value;
        cityLabel.FontWeight = value;
        stateLabel.FontWeight = value;
        zipLabel.FontWeight = value;
    }
}
public double MyControl_FontSize
{
    get { return _fontSize; }
    set
    {
        _fontSize = value;
        nameLabel.FontSize = value;
        addressLabel.FontSize = value;
        cityLabel.FontSize = value;
        stateLabel.FontSize = value;
        zipLabel.FontSize = value;
    }
}
public FontStyle MyControl_FontStyle
{
    get { return _fontStyle; }
    set
    {
        _fontStyle = value;
        nameLabel.FontStyle = value;
        addressLabel.FontStyle = value;
        cityLabel.FontStyle = value;
        stateLabel.FontStyle = value;
        zipLabel.FontStyle = value;
    }
}
public FontFamily MyControl_FontFamily
{
    get { return _fontFamily; }
    set
    {
        _fontFamily = value;
        nameLabel.FontFamily = value;
        addressLabel.FontFamily = value;
        cityLabel.FontFamily = value;
        stateLabel.FontFamily = value;
        zipLabel.FontFamily = value;
    }
}

public SolidColorBrush MyControl_Background
{
    get { return _background; }
    set
    {
        _background = value;
        rootElement.Background = value;
    }
}
public SolidColorBrush MyControl_Foreground
{
    get { return _foreground; }
    set
    {
        _foreground = value;
        nameLabel.Foreground = value;
        addressLabel.Foreground = value;
        cityLabel.Foreground = value;
        stateLabel.Foreground = value;
        zipLabel.Foreground = value;
    }
}

将数据发回主机

文件中的最后一个组件是 MyControlEventArgs 类,它用于将所收集的数据发回主机。

将下面的代码添加到 MyControls 命名空间。 具体实现非常简单,不在这里进一步讨论。

public class MyControlEventArgs : EventArgs
{
    private string _Name;
    private string _StreetAddress;
    private string _City;
    private string _State;
    private string _Zip;
    private bool _IsOK;

    public MyControlEventArgs(bool result,
                              string name,
                              string address,
                              string city,
                              string state,
                              string zip)
    {
        _IsOK = result;
        _Name = name;
        _StreetAddress = address;
        _City = city;
        _State = state;
        _Zip = zip;
    }

    public string MyName
    {
        get { return _Name; }
        set { _Name = value; }
    }
    public string MyStreetAddress
    {
        get { return _StreetAddress; }
        set { _StreetAddress = value; }
    }
    public string MyCity
    {
        get { return _City; }
        set { _City = value; }
    }
    public string MyState
    {
        get { return _State; }
        set { _State = value; }
    }
    public string MyZip
    {
        get { return _Zip; }
        set { _Zip = value; }
    }
    public bool IsOK
    {
        get { return _IsOK; }
        set { _IsOK = value; }
    }
}

生成解决方案。 此生成行为将生成一个名为 MyControls.dll 的 DLL。

实现 Windows 窗体宿主应用程序

Windows Forms宿主应用程序使用 ElementHost 对象承载 WPF 复合控件。 该应用程序处理 OnButtonClick 事件以接收来自复合控件的数据。 该应用程序还具有一组选项按钮,供您用来修改控件的外观。 下图显示了该应用程序。

在 Windows 窗体应用程序中承载的 WPF 复合控件

Windows 窗体承载 Avalon 控件

创建项目

若要开始创建项目,请执行以下操作:

  1. 启动 Visual Studio,打开**“新建项目”**对话框。

  2. 在 Visual C# 和 Windows 类别中,选择**“Windows 窗体应用程序”**模板。

  3. 将新项目命名为 WFHost。

  4. 为位置指定包含 MyControls 项目的同一顶级文件夹。

  5. 单击**“确定”**创建项目。

您还需要添加对包含 MyControl1 和其他程序集的 DLL 的引用。

  1. 在解决方案资源管理器中右击项目名称,然后选择**“添加引用”**。

  2. 单击**“浏览”**选项卡,然后浏览到包含 MyControls.dll 的文件夹。 对于本演练,该文件夹为 MyControls\bin\Debug。

  3. 选择 MyControls.dll,然后单击**“确定”**。

  4. 添加对下列程序集的引用。

    • PresentationCore

    • PresentationFramework

    • System.Xaml

    • WindowsBase

    • WindowsFormsIntegration

实现应用程序的用户界面

Windows 窗体应用程序的 UI 包含几个用于与 WPF 复合控件交互的控件。

  1. 在 Windows 窗体设计器中打开 Form1。

  2. 扩展窗体以容纳控件。

  3. 在窗体的右上角添加一个 System.Windows.Forms.Panel 控件以容纳 WPF 复合控件。

  4. 将以下 System.Windows.Forms.GroupBox 控件添加到窗体中。

    名称

    文本

    groupBox1

    Background Color

    groupBox2

    Foreground Color

    groupBox3

    Font Size

    groupBox4

    Font Family

    groupBox5

    Font Style

    groupBox6

    Font Weight

    groupBox7

    Data from control

  5. 将以下 System.Windows.Forms.RadioButton 控件添加到 System.Windows.Forms.GroupBox 控件中。

    GroupBox

    名称

    文本

    groupBox1

    radioBackgroundOriginal

    Original

    groupBox1

    radioBackgroundLightGreen

    LightGreen

    groupBox1

    radioBackgroundLightSalmon

    LightSalmon

    groupBox2

    radioForegroundOriginal

    Original

    groupBox2

    radioForegroundRed

    Red

    groupBox2

    radioForegroundYellow

    Yellow

    groupBox3

    radioSizeOriginal

    Original

    groupBox3

    radioSizeTen

    10

    groupBox3

    radioSizeTwelve

    12

    groupBox4

    radioFamilyOriginal

    Original

    groupBox4

    radioFamilyTimes

    Times New Roman

    groupBox4

    radioFamilyWingDings

    WingDings

    groupBox5

    radioStyleOriginal

    Normal

    groupBox5

    radioStyleItalic

    Italic

    groupBox6

    radioWeightOriginal

    Original

    groupBox6

    radioWeightBold

    Bold

  6. 将以下 System.Windows.Forms.Label 控件添加到最后一个 System.Windows.Forms.GroupBox 中。 这些控件显示 WPF 复合控件返回的数据。

    GroupBox

    名称

    文本

    groupBox7

    lblName

    Name:

    groupBox7

    lblAddress

    Street Address:

    groupBox7

    lblCity

    City:

    groupBox7

    lblState

    State:

    groupBox7

    lblZip

    Zip:

初始化窗体

通常在窗体的 Load 事件处理程序中实现承载代码。 下面的代码演示 Load 事件处理程序(WPF 复合控件的 Loaded 事件的处理程序),以及后面使用的几个全局变量的声明。

在 Windows 窗体设计器中,双击窗体创建一个 Load 事件处理程序。 在 Form1.cs 顶部添加以下 using 语句。

using System;
using System.Windows;
using System.Windows.Navigation;
using System.Windows.Controls;
using System.Windows.Media;

namespace MyControls
{
    public partial class MyControl1 : Grid
    {
        public delegate void MyControlEventHandler(object sender, MyControlEventArgs args);
        public event MyControlEventHandler OnButtonClick;
        private FontWeight _fontWeight;
        private double _fontSize;
        private FontFamily _fontFamily;
        private FontStyle _fontStyle;
        private SolidColorBrush _foreground;
        private SolidColorBrush _background;

        private void Init(object sender, EventArgs e)
        {
            //They all have the same style, so use nameLabel to set initial values.
            _fontWeight = nameLabel.FontWeight;
            _fontSize = nameLabel.FontSize;
            _fontFamily = nameLabel.FontFamily;
            _fontStyle = nameLabel.FontStyle;
            _foreground = (SolidColorBrush)nameLabel.Foreground;
            _background = (SolidColorBrush)rootElement.Background;
        }

        private void ButtonClicked(object sender, RoutedEventArgs e)
        {
            MyControlEventArgs retvals = new MyControlEventArgs(true,
                                                                txtName.Text,
                                                                txtAddress.Text,
                                                                txtCity.Text,
                                                                txtState.Text,
                                                                txtZip.Text);
            if (sender == btnCancel)
            {
                retvals.IsOK = false;
            }
            if (OnButtonClick != null)
                OnButtonClick(this, retvals);
        }

        public FontWeight MyControl_FontWeight
        {
            get { return _fontWeight; }
            set
            {
                _fontWeight = value;
                nameLabel.FontWeight = value;
                addressLabel.FontWeight = value;
                cityLabel.FontWeight = value;
                stateLabel.FontWeight = value;
                zipLabel.FontWeight = value;
            }
        }
        public double MyControl_FontSize
        {
            get { return _fontSize; }
            set
            {
                _fontSize = value;
                nameLabel.FontSize = value;
                addressLabel.FontSize = value;
                cityLabel.FontSize = value;
                stateLabel.FontSize = value;
                zipLabel.FontSize = value;
            }
        }
        public FontStyle MyControl_FontStyle
        {
            get { return _fontStyle; }
            set
            {
                _fontStyle = value;
                nameLabel.FontStyle = value;
                addressLabel.FontStyle = value;
                cityLabel.FontStyle = value;
                stateLabel.FontStyle = value;
                zipLabel.FontStyle = value;
            }
        }
        public FontFamily MyControl_FontFamily
        {
            get { return _fontFamily; }
            set
            {
                _fontFamily = value;
                nameLabel.FontFamily = value;
                addressLabel.FontFamily = value;
                cityLabel.FontFamily = value;
                stateLabel.FontFamily = value;
                zipLabel.FontFamily = value;
            }
        }

        public SolidColorBrush MyControl_Background
        {
            get { return _background; }
            set
            {
                _background = value;
                rootElement.Background = value;
            }
        }
        public SolidColorBrush MyControl_Foreground
        {
            get { return _foreground; }
            set
            {
                _foreground = value;
                nameLabel.Foreground = value;
                addressLabel.Foreground = value;
                cityLabel.Foreground = value;
                stateLabel.Foreground = value;
                zipLabel.Foreground = value;
            }
        }
    }

    public class MyControlEventArgs : EventArgs
    {
        private string _Name;
        private string _StreetAddress;
        private string _City;
        private string _State;
        private string _Zip;
        private bool _IsOK;

        public MyControlEventArgs(bool result,
                                  string name,
                                  string address,
                                  string city,
                                  string state,
                                  string zip)
        {
            _IsOK = result;
            _Name = name;
            _StreetAddress = address;
            _City = city;
            _State = state;
            _Zip = zip;
        }

        public string MyName
        {
            get { return _Name; }
            set { _Name = value; }
        }
        public string MyStreetAddress
        {
            get { return _StreetAddress; }
            set { _StreetAddress = value; }
        }
        public string MyCity
        {
            get { return _City; }
            set { _City = value; }
        }
        public string MyState
        {
            get { return _State; }
            set { _State = value; }
        }
        public string MyZip
        {
            get { return _Zip; }
            set { _Zip = value; }
        }
        public bool IsOK
        {
            get { return _IsOK; }
            set { _IsOK = value; }
        }
    }
}
using System.Windows;
using System.Windows.Forms.Integration;
using System.Windows.Media;

用下面的代码替换现有 Form1 类的内容。

private ElementHost ctrlHost;
private MyControls.MyControl1 wpfAddressCtrl;
System.Windows.FontWeight initFontWeight;
double initFontSize;
System.Windows.FontStyle initFontStyle;
System.Windows.Media.SolidColorBrush initBackBrush;
System.Windows.Media.SolidColorBrush initForeBrush;
System.Windows.Media.FontFamily initFontFamily;

public Form1()
{
    InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
    ctrlHost = new ElementHost();
    ctrlHost.Dock = DockStyle.Fill;
    panel1.Controls.Add(ctrlHost);
    wpfAddressCtrl = new MyControls.MyControl1();
    wpfAddressCtrl.InitializeComponent();
    ctrlHost.Child = wpfAddressCtrl;

    wpfAddressCtrl.OnButtonClick +=
        new MyControls.MyControl1.MyControlEventHandler(
        avAddressCtrl_OnButtonClick);
    wpfAddressCtrl.Loaded += new RoutedEventHandler(
        avAddressCtrl_Loaded);
}

void avAddressCtrl_Loaded(object sender, EventArgs e)
{
    initBackBrush = (SolidColorBrush)wpfAddressCtrl.MyControl_Background;
    initForeBrush = wpfAddressCtrl.MyControl_Foreground;
    initFontFamily = wpfAddressCtrl.MyControl_FontFamily;
    initFontSize = wpfAddressCtrl.MyControl_FontSize;
    initFontWeight = wpfAddressCtrl.MyControl_FontWeight;
    initFontStyle = wpfAddressCtrl.MyControl_FontStyle;
}

前面的代码中的 Form1_Load 方法演示了承载 WPF 控件的常规过程:

  1. 创建一个新的 ElementHost 对象。

  2. 将控件的 Dock 属性设置为 DockStyle.Fill

  3. ElementHost 控件添加到 Panel 控件的 Controls 集合。

  4. 创建 WPF 控件的一个实例。

  5. 在窗体中承载该复合控件,方法是:将该控件分配给 ElementHost 控件的 Child 属性。

Form1_Load 方法中的余下两行向两个控件事件附加处理程序:

  • OnButtonClick 是一个自定义事件,当用户单击**“OK”(确定)“Cancel”(取消)**按钮时,复合控件将触发该事件。 您处理该事件,以获取用户响应并收集用户指定的任何数据。

  • Loaded 是一个标准事件,当 WPF 控件已完全加载时将引发该事件。 此处使用该事件是因为示例需要使用控件的属性初始化几个全局变量。 在窗体的 Load 事件发生时,控件未完全加载,这些值仍设置为 null。 您需要一直等到控件的 Loaded 事件发生,然后才能访问这些属性。

Loaded 事件处理程序显示在前面的代码中。 OnButtonClick 处理程序在下一节讨论。

处理 OnButtonClick

当用户单击**“OK”(确定)“Cancel”(取消)**按钮时,会发生 OnButtonClick 事件。

事件处理程序检查事件参数的 IsOK 字段,确定单击的是哪个按钮。 lbldata 变量与前面讨论的 Label 控件对应。 如果用户单击**“OK”(确定)按钮,来自该控件的 TextBox 控件中的数据将分配给对应的 Label 控件。 如果用户单击“Cancel”(取消)**,则 Text 值将设置为默认字符串。

将下面的按钮单击事件处理程序代码添加到 Form1 类。

void avAddressCtrl_OnButtonClick(
    object sender,
    MyControls.MyControl1.MyControlEventArgs args)
{
    if (args.IsOK)
    {
        lblAddress.Text = "Street Address: " + args.MyStreetAddress;
        lblCity.Text = "City: " + args.MyCity;
        lblName.Text = "Name: " + args.MyName;
        lblState.Text = "State: " + args.MyState;
        lblZip.Text = "Zip: " + args.MyZip;
    }
    else
    {
        lblAddress.Text = "Street Address: ";
        lblCity.Text = "City: ";
        lblName.Text = "Name: ";
        lblState.Text = "State: ";
        lblZip.Text = "Zip: ";
    }
}

生成并运行应用程序。 在 WPF 复合控件中添加一些文本,然后单击**“OK”(确定)**。 该文本会显示在标签中。 此时,尚未添加用于处理单选按钮的代码。

修改控件外观

使用窗体上的 RadioButton 控件,用户可以更改 WPF 复合控件的前景和背景颜色以及几个字体属性。 背景色由 ElementHost 对象公开。 其余属性作为控件的自定义属性公开。

双击窗体上的每个 RadioButton 控件以创建 CheckedChanged 事件处理程序。 使用下面的代码替换 CheckedChanged 事件处理程序。

private void radioBackgroundOriginal_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_Background = initBackBrush;
}

private void radioBackgroundLightGreen_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_Background = new SolidColorBrush(Colors.LightGreen);
}

private void radioBackgroundLightSalmon_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_Background = new SolidColorBrush(Colors.LightSalmon);
}

private void radioForegroundOriginal_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_Foreground = initForeBrush;
}

private void radioForegroundRed_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_Foreground = new System.Windows.Media.SolidColorBrush(Colors.Red);
}

private void radioForegroundYellow_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_Foreground = new System.Windows.Media.SolidColorBrush(Colors.Yellow);
}

private void radioFamilyOriginal_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontFamily = initFontFamily;
}

private void radioFamilyTimes_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontFamily = new System.Windows.Media.FontFamily("Times New Roman");
}

private void radioFamilyWingDings_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontFamily = new System.Windows.Media.FontFamily("WingDings");
}

private void radioSizeOriginal_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontSize = initFontSize;
}

private void radioSizeTen_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontSize = 10;
}

private void radioSizeTwelve_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontSize = 12;
}

private void radioStyleOriginal_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontStyle = initFontStyle;
}

private void radioStyleItalic_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontStyle = System.Windows.FontStyles.Italic;
}

private void radioWeightOriginal_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontWeight = initFontWeight;
}

private void radioWeightBold_CheckedChanged(object sender, EventArgs e)
{
    wpfAddressCtrl.MyControl_FontWeight = FontWeights.Bold;
}

生成并运行应用程序。 单击不同的单选按钮以查看对 WPF 复合控件的影响。

请参见

任务

演练:在 Windows 窗体中承载 3-D WPF 复合控件

参考

ElementHost

WindowsFormsHost

概念

演练:在 WPF 中承载 Windows 窗体复合控件

其他资源

WPF 设计器