System windows forms wpf

Windows Presentation Foundation (WPF) предоставляет среду с широкими возможностями для создания приложений. Тем не менее, если имеются существенные преимущества в Windows Forms код, он может быть более эффективным, расширение существующего Windows Forms приложения с WPF , а не переписывание кода с нуля. Распространенный сценарий — при необходимости внедрения одного или нескольких элементов управления, реализованных с WPF в приложении Windows Forms. Дополнительные сведения о настройке элементов управления WPF, см. в разделе Настройка элементов управления.

В этом пошаговом руководстве пошагово продемонстрирует приложение, на котором размещена WPF составного элемента управления для ввода данных в приложении Windows Forms. Составной элемент управления упакован в библиотеку DLL. Эта общая процедура может быть расширена для более сложных приложений и элементов управления. В этом пошаговом руководстве должна быть почти идентично повторяет свойства и функциональные возможности для Пошаговое руководство: Размещение Windows Forms составного элемента управления в WPF. Основным отличием является то, что сценарий размещения выполняется в обратном порядке.

Пошаговое руководство состоит из двух разделов. В первом разделе кратко описывается реализация WPF составного элемента управления. Во втором разделе подробно рассматриваются размещение составного элемента управления в приложении Windows Forms, получение событий от него и доступ к некоторым свойствам элемента управления.

В данном пошаговом руководстве представлены следующие задачи.

  • Реализация составного элемента управления WPF

  • Реализация ведущего приложения Windows Forms

Полный пример кода для задач, приведенных в этом пошаговом руководстве, см. в разделе размещение составного элемента управления WPF в Windows Forms образец.

Предварительные требования

Для выполнения шагов, описанных в этом руководстве, вам понадобится Visual Studio.

Реализация составного элемента управления 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

Создание пользовательского интерфейса

UI Для составного элемента управления реализуется с помощью XAML. Составной элемент управления UI состоит из пяти TextBox элементов. Каждый TextBox имеет связанный элемент TextBlock элемент, который служит в качестве метки. Существует два Button элементов внизу ОК и отменить. При нажатии любой кнопки элемент управления создает пользовательское событие для возвращения сведений в ведущее приложение.

Базовый макет

Различные UI элементы содержатся в Grid элемент. Можно использовать Grid для размещения содержимого составного элемента управления практически таким же способом можно использовать Table элемент в формате HTML. WPF также имеет Table элемент, но Grid проще и лучше выполняются простые задачи компоновки.

В приведенном ниже коде XAML показан базовый макет. Этот XAML определяет общую структуру элемента управления, указав число столбцов и строк в Grid элемент.

В файле MyControl1.xaml замените имеющийся код XAML приведенным ниже кодом.

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://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>

Добавление в сетку элементов TextBlock и TextBox

Поместить UI элемент в сетку посредством присвоения элемента RowProperty и ColumnProperty атрибуты для соответствующего числа строк и столбцов. Следует помнить, что нумерация строк и столбцов ведется от нуля. У вас есть элемент охватывать несколько столбцов, установив его ColumnSpanProperty атрибута. Дополнительные сведения о Grid элементов, см. в разделе Создание элемента сетки.

Следующий XAML показано составного элемента управления TextBox и TextBlock элементы с их RowProperty и ColumnProperty атрибуты, для которых задается правильное расположение элементов в сетке.

В файле 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"/>

Создание стилей элементов пользовательского интерфейса

Многие элементы в форме для ввода данных имеют одинаковый внешний вид. Это означает, что они имеют одинаковые значения нескольких свойств. Вместо того чтобы задавать атрибуты каждого элемента в отдельности, использует предыдущего XAML Style элементы для определения стандартных значений свойств для классов элементов. Такой подход упрощает элемент управления и позволяет изменять внешний вид нескольких элементов посредством одного атрибута стиля.

Style Элементы содержатся в Grid элемента Resources свойство, поэтому они могут использоваться всеми элементами в элементе управления. Если стиль имеет имя, его применить к элементу, добавив Style элементу присвоено имя стиля. Неименованные стили становятся стилями по умолчанию для элемента. Дополнительные сведения о WPF стили, см. в разделе Стилизация и использование шаблонов.

В следующем XAML показан Style элементы для составного элемента управления. Чтобы увидеть, как стили применяются к элементам, см. предыдущий код XAML. Например, последний TextBlock элемент имеет inlineText стиля, а последний TextBox элемент использует стиль по умолчанию.

В файле MyControl1.xaml добавьте следующий XAML сразу после Grid начальный элемент.

<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 и «Отмена»

Завершающими элементами составного элемента управления являются ОК и отменить Button элементы, которые занимают первые два столбца последней строки Grid. Эти элементы используют общий обработчик событий, ButtonClickedи значение по умолчанию Button стиль, определенный в предыдущем XAML.

В файле MyControl1.xaml добавьте следующий XAML после последнего TextBox элемент. 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 событие, которое уведомляет ведущее приложение, что пользователь завершил работу и передает данные на узел.

Элемент управления также предоставляет ряд свойств цвета и шрифтов, которые позволяют изменять внешний вид. В отличие от WindowsFormsHost класс, который используется для размещения элемента управления Windows Forms, ElementHost класс предоставляет элемент управления Background только свойство. Чтобы обеспечить сходство между этим примером кода и примере, рассмотренном в Пошаговое руководство: Размещение Windows Forms составного элемента управления в WPF, элемент управления непосредственно предоставляет остальные свойства.

Базовая структура файла кода программной части

Файл с выделенным кодом состоит из одного пространства имен, MyControls, который будет содержать два класса MyControl1 и MyControlEventArgs.

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

Первый класс, MyControl1, — это разделяемый класс, содержащий код, который реализует функции UI определены в файле MyControl1.xaml. При синтаксическом анализе файле MyControl1.xaml, XAML преобразуется в тот же разделяемый класс, и два разделяемых класса объединяются для формирования скомпилированного элемента управления. По этой причине имя класса в файле кода программной части должно совпадать с именем класса, назначенным в файле MyControl1.xaml, и наследоваться от корневого элемента управления. Второй класс, MyControlEventArgs, является классом аргументов события, который используется для отправки данных на узел.

Откройте файл MyControl1.xaml.cs. Измените существующее объявление класса, чтобы он присваивается следующее имя и наследует от Grid.

public partial class MyControl1 : Grid

Инициализация элемента управления

Приведенный ниже код реализует несколько основных задач:

  • Объявляет частное событие OnButtonClickи его связанный делегат MyControlEventHandler.

  • создает несколько частных глобальных переменных, хранящих данные пользователя. Эти данные предоставляются через соответствующие свойства;

  • Реализует обработчик Init, для элемента управления Loaded событий. Этот обработчик инициализирует глобальные переменные путем присвоения им значений, определенных в файле MyControl1.xaml. Чтобы сделать это, он использует Name назначенное типичному TextBlock элемент, nameLabel, чтобы получить доступ к параметрам свойства этого элемента.

Удалите существующий конструктор и добавьте следующий код, чтобы ваши 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 обработчик событий ButtonClicked. Обе кнопки имеют имя, btnOK или btnCancel, который позволяет обработчику определить, какая кнопка была нажата, путем проверки значения sender аргумент. Обработчик выполняет следующие действия:

  • Создает MyControlEventArgs объект, содержащий данные из TextBox элементов.

  • Если пользователь щелкает отменить кнопку наборов 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; }
    }
}

Постройте решение. В результате сборки будет создана библиотека DLL с именем MyControls.dll.

Реализация ведущего приложения Windows Forms

Windows Forms разместить приложение использует ElementHost объект узла WPF составного элемента управления. Приложение обрабатывает OnButtonClick событий для получения данных из составного элемента управления. Приложение также содержит набор переключателей, которые можно использовать для изменения внешнего вида элемента управления. На рисунке ниже показано приложение.

На следующем рисунке показана составной элемент управления WPF в приложении Windows Forms»

Создание проекта

Для запуска проекта выполните указанные ниже действия.

  1. Запустите Visual Studioи откройте новый проект диалоговое окно.

  2. В Visual C# и категории Windows выберите приложение Windows Forms шаблона.

  3. Присвойте проекту имя WFHost.

  4. В качестве расположения укажите ту же папку верхнего уровня, в которой содержится проект MyControls.

  5. Нажмите кнопку ОК, чтобы создать проект.

Необходимо также добавить ссылки на библиотеку DLL, содержащую MyControl1 и других сборок.

  1. Щелкните правой кнопкой мыши имя проекта в обозревателе решений и выберите добавить ссылку.

  2. Нажмите кнопку Обзор вкладку и перейдите к папке, которая содержит файл MyControls.dll. В данном пошаговом руководстве это папка MyControls\bin\Debug.

  3. Выберите файл MyControls.dll и нажмите кнопку ОК.

  4. Добавьте ссылки на следующие сборки.

    • PresentationCore

    • PresentationFramework

    • System.Xaml

    • WindowsBase

    • WindowsFormsIntegration

Реализация пользовательского интерфейса для приложения

Пользовательский интерфейс приложения Windows Forms содержит несколько элементов управления для взаимодействия с составным элементом управления WPF.

  1. Откройте приложение Form1 в конструкторе Windows Forms.

  2. Увеличьте форму, чтобы разместить элементы управления.

  3. В правом верхнем углу формы добавьте System.Windows.Forms.Panel управления для хранения WPF составного элемента управления.

  4. Добавьте следующий System.Windows.Forms.GroupBox элементов управления в форму.

    name Текста
    groupBox1 Цвет фона
    groupBox2 Цвет переднего плана
    groupBox3 Размер шрифта
    groupBox4 Семейство шрифтов
    groupBox5 Стиль шрифта
    groupBox6 Насыщенность шрифта
    groupBox7 Данные из элемента управления
  5. Добавьте следующий System.Windows.Forms.RadioButton элементы управления System.Windows.Forms.GroupBox элементов управления.

    GroupBox name Текста
    groupBox1 radioBackgroundOriginal До преобразования
    groupBox1 radioBackgroundLightGreen LightGreen
    groupBox1 radioBackgroundLightSalmon LightSalmon
    groupBox2 radioForegroundOriginal До преобразования
    groupBox2 radioForegroundRed Красный
    groupBox2 radioForegroundYellow Желтый
    groupBox3 radioSizeOriginal До преобразования
    groupBox3 radioSizeTen 10
    groupBox3 radioSizeTwelve 12
    groupBox4 radioFamilyOriginal До преобразования
    groupBox4 radioFamilyTimes Times New Roman Cyr
    groupBox4 radioFamilyWingDings WingDings
    groupBox5 radioStyleOriginal Норм.
    groupBox5 radioStyleItalic Italic
    groupBox6 radioWeightOriginal До преобразования
    groupBox6 radioWeightBold Полужирный
  6. Добавьте следующий System.Windows.Forms.Label управляет к последнему System.Windows.Forms.GroupBox. Эти элементы управления отображают данные, возвращенные WPF составного элемента управления.

    GroupBox name Текста
    groupBox7 lblName Имя.
    groupBox7 lblAddress Почтовый адрес:
    groupBox7 lblCity Город:
    groupBox7 lblState Состояние:
    groupBox7 lblZip Почтовый индекс:

Инициализация формы

Код размещения обычно реализуется в виде Load обработчик событий. В следующем коде показан Load обработчик событий, обработчик для WPF составного элемента управления Loaded событий и объявления нескольких глобальных переменных, которые будут использоваться позже.

В конструкторе Windows Forms дважды щелкните форму, чтобы создать Load обработчик событий. В начале файла Form1.cs добавьте следующий код using инструкций.

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 — пользовательское событие, инициируемое составным элементом управления, когда пользователь щелкает ОК или отменить кнопки. Обработайте событие для получения ответа пользователя и сбора всех введенных им данных.

  • Loaded — стандартное событие, вызванное объектом WPF когда он находится полностью загружен. Это событие используется здесь потому, что для примера необходима инициализация нескольких глобальных переменных свойствами элемента управления. Во время в формате Load событий, элемент управления не полностью загружен, и по-прежнему установлены эти значения null. Необходимо подождать до элемента управления Loaded событие возникает для доступа к этим свойствам.

Loaded В приведенном выше коде показан обработчик событий. OnButtonClick Обработчик рассматривается в следующем разделе.

Обработка события OnButtonClick

OnButtonClick Событие возникает, когда пользователь щелкает ОК или отменить кнопки.

Обработчик событий проверяет аргумента события IsOK поля, чтобы определить, какая кнопка была нажата. lbl Данных переменные соответствуют Label элементов управления, которые были описаны выше. Если пользователь нажимает кнопку ОК кнопку, данные из элемента управления TextBox элемента управления назначаются соответствующему Label элемента управления. Если пользователь нажимает кнопку отменить, 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 и нажмите кнопку ОК. Текст отображается в метках. На данном этапе код для обработки переключателей еще не добавлен.

Изменение внешнего вида элемента управления

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.

См. также

  • ElementHost
  • WindowsFormsHost
  • Проектирование XAML в Visual Studio
  • Пошаговое руководство. Размещение составного элемента управления Windows Forms в приложении WPF
  • Пошаговое руководство. Размещение составного трехмерного элемента управления WPF в форме Windows Forms

20Dec

I will describe how we can use windows forms controls in WPF application.
I’ll assume I have a WPF Application and I want to use DateTimePicker Control, how I can do that in WPF Application?

1- Create WPF Application.
2- Add Stackpanel control.
3- in Solution Explorer Add Reference for:

  • System.Windows.Forms
  • WindowsFormsIntegration
using WindowsControls = System.Windows.Forms;
using WindowsIntegration = System.Windows.Forms.Integration;

The below method will Add DateTimePacker Control in WPF:

private void AddDateTimePacker(int x)
{
  for (int i = 0; i < x; i++)
  {
  WindowsIntegration.WindowsFormsHost host = new WindowsIntegration.WindowsFormsHost();
  WindowsControls.DateTimePicker dateTime = new WindowsControls.DateTimePicker();

  dateTime.Name = "DateTimePicker" + i;
  dateTime.Left = 0;
  dateTime.Top = 0;
  dateTime.Text = string.Empty;

  //Add control to stack panel
  host.Child = dateTime;
  stackPanel1.Children.Add(host);
  }
}

Author

Amr Saafan

Founder | CEO | CTO | Project Manager | Product Owner


We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. For more information, see the privacy statement.

Windows forms and WPF Interoperability January 9, 2012

Posted by fofo in Visual Studio 2010, WPF, XAML.

Tags: Interoperability;Windows Forms;
trackback

In this post I will not be investigating at what some people call the “Windows single technology applications” which basically means that we build WinForms apps for the windows operating systems using only Windows Forms controls or alternatively build a windows WPF application using only the built-in or 3rd party WPF controls.

I will be looking into the “Mixed technology applications”. In other words how to build Windows Forms applications using both Windows Forms and WPF controls.Moreover how to build WPF applications using both WPF and Windows Forms controls.I will be using C# to build those applications.

I know that many people since the introduction of  WPF have designed and implemented using this awesome new technology introduced with .NET 3.0.

But what about our existing WinForms applications? Are we going to rewrite everything?What about the controls we built for a WinForms application? Are we going to rewrite those too? What about 3rd party controls we have purchased? We do not want to spend time and money to write functionality that we already have got.

In a nutshell we want to have interoperability and that it is possible with the current technologies.It is possible to reuse than recreate.

Let’s start with our first example. In this example I will show how to add an existing Windows Form to a WPF application.

1) Launch Visual Studio. I will be using Visual Studio 2010 Ultimate edition.

2) Create a new WPF application and give it a name of your choice. Choose C# as the development language.

3) Add a new item to your application, a new Window(WPF). Name it Footballers.xaml

4) Add the following xaml code to the MainWindow.xaml file

<StackPanel>
<Button Content="Log in" Margin="5,25,5,5"
MaxWidth="200" MinHeight="35"
Name="loginButton"
Click="loginButton_Click"/>
<Button Content="Footballers window" Margin="5,25,5,5"
MaxWidth="200" MinHeight="35"
Name="footballersWindowButton"
Click="footballersWindowButton_Click"/>
<Button Content="Footballers form" Margin="5,25,5,5"
MaxWidth="200" MinHeight="35"
Name="footballersFormButton"
Click="footballersFormButton_Click"/>
<Button Content="About" Margin="5,25,5,5"
MaxWidth="200" MinHeight="35"
Name="aboutButton"
Click="aboutButton_Click"/>
</StackPanel>

5) Run your application so you can have a feeling on what we are going to build.

6) As you see we have a button that when clicked must launch the Footballers window.This is the code to achieve that.

 private void footballersWindowButton_Click(object sender, RoutedEventArgs e)
{
var fooballers = new Footballers();
fooballers.Show();

}

7) We have also added an “About” button. That will launch an About form. We know that Windows Forms have such functionality and we will utilise it. Add a new item to your application, an About Box (Add –> New Item –>Windows Forms –>About Box).Name it AboutForm.References are added automatically to the application and the Windows Forms designer pops up showing the “AboutForm” form.

8) When you select the AboutForm (Windows Form) you will see in the Toolbox area the Windows Forms controls.When you select the .xaml file in the Toolbox the WPF controls will appear.

9) I need to add login functionality to my application. I will use an existing Windows Forms (Login Form) that I have already created. I will add an existing item (a LoginForm.cs)  file to my application.

10) In the MainWindow.xaml we will add the the code required to launch the Login form and the About form.The code follows.

private void loginButton_Click(object sender, RoutedEventArgs e)
{
var login = new LoginForm();
login.Show();
}

private void aboutButton_Click(object sender, RoutedEventArgs e)
{
var about = new AboutForm();
about.Show();
}

11) Run your application and click the relevant buttons to launch the windows.

12) As you see it is pretty easy to add Window Forms to a WPF application. Maybe they use a different graphical subsystem to render but it is very easy to launch window forms from a WPF application. Now I would like to show you how easy it is to share information between the various forms of the application. I want to store the login name in a variable and show it in the MainWindow. Add a class file to your application. Name it General.cs. This is the code for the static class. Basically we define a string (LoginName).

public static class General
{
public static string LoginName = string.Empty;

}

13) Now we will add some code in the LoginForm.cs that will store the username the user enters in the form.This is the code

private void OK_Click(object sender, EventArgs e)
{

General.LoginName = UsernameTextBox.Text;
this.Close();

}

14) The next step is to change the code in the MainWindow.xaml.cs so that when the

private void loginButton_Click(object sender, RoutedEventArgs e)
{
var login = new LoginForm();

if (login.ShowDialog() == System.Windows.Forms.DialogResult.OK)
MessageBox.Show("Welcome " + General.LoginName);
}

15) Run your application and click the Log in button. Add a new username and a password and click “OK”. You will see the Message box popping up with the name you entered in the login form.With this simple example you see how easy it is to share information among the two forms that are part of different window technologies.

16) Now let’s add some functionality to the button”Footballers Window”.

We will get data from a database and display it in the Footballers.xaml window. But we will need a database first. So in my case I have created a small database with a single table.

This is the T-SQL code you need to execute so you can have the same data.

USE [master]
GO

/****** Object:  Database [football]    Script Date: 01/07/2012 15:22:13 ******/
CREATE DATABASE [football] ON  PRIMARY
( NAME = N'football', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\football.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON
( NAME = N'football_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\football_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO

ALTER DATABASE [football] SET COMPATIBILITY_LEVEL = 100
GO

IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [football].[dbo].[sp_fulltext_database] @action = 'enable'
end
GO

ALTER DATABASE [football] SET ANSI_NULL_DEFAULT OFF
GO

ALTER DATABASE [football] SET ANSI_NULLS OFF
GO

ALTER DATABASE [football] SET ANSI_PADDING OFF
GO

ALTER DATABASE [football] SET ANSI_WARNINGS OFF
GO

ALTER DATABASE [football] SET ARITHABORT OFF
GO

ALTER DATABASE [football] SET AUTO_CLOSE OFF
GO

ALTER DATABASE [football] SET AUTO_CREATE_STATISTICS ON
GO

ALTER DATABASE [football] SET AUTO_SHRINK OFF
GO

ALTER DATABASE [football] SET AUTO_UPDATE_STATISTICS ON
GO

ALTER DATABASE [football] SET CURSOR_CLOSE_ON_COMMIT OFF
GO

ALTER DATABASE [football] SET CURSOR_DEFAULT  GLOBAL
GO

ALTER DATABASE [football] SET CONCAT_NULL_YIELDS_NULL OFF
GO

ALTER DATABASE [football] SET NUMERIC_ROUNDABORT OFF
GO

ALTER DATABASE [football] SET QUOTED_IDENTIFIER OFF
GO

ALTER DATABASE [football] SET RECURSIVE_TRIGGERS OFF
GO

ALTER DATABASE [football] SET  DISABLE_BROKER
GO

ALTER DATABASE [football] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
GO

ALTER DATABASE [football] SET DATE_CORRELATION_OPTIMIZATION OFF
GO

ALTER DATABASE [football] SET TRUSTWORTHY OFF
GO

ALTER DATABASE [football] SET ALLOW_SNAPSHOT_ISOLATION OFF
GO

ALTER DATABASE [football] SET PARAMETERIZATION SIMPLE
GO

ALTER DATABASE [football] SET READ_COMMITTED_SNAPSHOT OFF
GO

ALTER DATABASE [football] SET HONOR_BROKER_PRIORITY OFF
GO

ALTER DATABASE [football] SET  READ_WRITE
GO

ALTER DATABASE [football] SET RECOVERY FULL
GO

ALTER DATABASE [football] SET  MULTI_USER
GO

ALTER DATABASE [football] SET PAGE_VERIFY CHECKSUM
GO

ALTER DATABASE [football] SET DB_CHAINING OFF
GO

USE [football]
GO
/****** Object:  Table [dbo].[footballers]    Script Date: 01/07/2012 15:21:51 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[footballers](
[id] [int] IDENTITY(1,1) NOT NULL,
[firstname] [varchar](50) NOT NULL,
[lastname] [varchar](50) NOT NULL,
[isActive] [bit] NULL,
PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO

SET IDENTITY_INSERT [dbo].[footballers] ON
INSERT [dbo].[footballers] ([id], [firstname], [lastname], [isActive]) VALUES (1, N'Robbie', N'Fowler', 1)
INSERT [dbo].[footballers] ([id], [firstname], [lastname], [isActive]) VALUES (2, N'Steven', N'Gerrard', 1)
INSERT [dbo].[footballers] ([id], [firstname], [lastname], [isActive]) VALUES (3, N'Kenny', N'Dalglish', 0)
SET IDENTITY_INSERT [dbo].[footballers] OFF

}

Connect to the local instance of SQL Server and in a new query window execute the T-SQL code provided above.

17) In the Visual Studio go to Data–>Add New Data Source…Add a new data source.In the wizard select Database and click Next. Then select Dataset and click Next.In the next step create a new connection that points to the football database.Press OK. In the wizard click Next and Next again.In the database objects window choose footballers table and finally Finish.Drag and drop the Data Sources window on the Footballers window.Please have a look at the generated code in the Footballers.xaml and Footballers.xaml.cs files.

In my case the code in the Footballers.xaml.cs is the following.

private void Window_Loaded(object sender, RoutedEventArgs e)
{

WindowsFormToWPF.footballDataSet footballDataSet = ((WindowsFormToWPF.footballDataSet)(this.FindResource("footballDataSet")));
// Load data into the table footballers. You can modify this code as needed.
WindowsFormToWPF.footballDataSetTableAdapters.footballersTableAdapter
footballDataSetfootballersTableAdapter = new WindowsFormToWPF.footballDataSetTableAdapters.footballersTableAdapter();
footballDataSetfootballersTableAdapter.Fill(footballDataSet.footballers);
System.Windows.Data.CollectionViewSource footballersViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("footballersViewSource")));
footballersViewSource.View.MoveCurrentToFirst();
}

The code for the Footballers.xaml follows

<Window.Resources>
<my:footballDataSet x:Key="footballDataSet" />
<CollectionViewSource x:Key="footballersViewSource"
Source="{Binding Path=footballers, Source={StaticResource footballDataSet}}" />
</Window.Resources>
<Grid DataContext="{StaticResource footballersViewSource}">
<DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True" Height="134"
HorizontalAlignment="Left" ItemsSource="{Binding}" Margin="12,21,0,0" Name="footballersDataGrid"
RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="222">
<DataGrid.Columns>
<DataGridTextColumn x:Name="idColumn" Binding="{Binding Path=id}" Header="id" IsReadOnly="True" Width="SizeToHeader" />
<DataGridTextColumn x:Name="firstnameColumn" Binding="{Binding Path=firstname}" Header="firstname" Width="SizeToHeader" />
<DataGridTextColumn x:Name="lastnameColumn" Binding="{Binding Path=lastname}" Header="lastname" Width="SizeToHeader" />
<DataGridCheckBoxColumn x:Name="isActiveColumn" Binding="{Binding Path=isActive}" Header="is Active" Width="SizeToHeader" />
</DataGrid.Columns>
</DataGrid>
</Grid>

18) Run your application and click on the “Footballers window” button to see the new window populated with data from the dataset.

19) Now we need to add some functionality to the fourth button “Footballers form”. Add another form (Windows Form) to your application.Name it FootballersForm.cs

Bring the form on the designer.From the Data Sources window, click Details from the drop-down and then drag and drop it on the FootballersForm window.Have a look at the code in the FootballersForm.cs file. Now we need to add some code to launch this Windows Form window from the MainWindow.xaml file.

This is the code for the footballersFormButton_Click event handling routine.

private void footballersFormButton_Click(object sender, RoutedEventArgs e)
{
var FootballersForm = new FootballersForm();

FootballersForm.Show();
}

20) Run your application and click on all buttons to see all the windows popping up. You can see how we can incorporate Window Forms in a WPF project.

21) At this point I would like to mention something that every developer who will try “Interop” should know. Launch Spy++ and move it over a Windows form window. You will see so many handles available for almost any control on the form. You can check that yourself by launching Spy++ and trying to find the handle for the various controls of the Windows Forms windows. You will see that you will get only one handle for the WPF windows.

Have a look at the pictures below to see what I mean.

22) Another thing worth pointing out is how keyboard input is handled in a Windows form window that is part of a WPF application. Launch your application again. Click on the “Footballers form” window and in the window that pops up, try to use the tab key to move between the fields of the record. You will notice that you cannot do that.That is because the WPF application (host application) receives the keyboard input messages but does not know how to pass that keyboard input message to the Windows Form window. Let’s fix that. First we need to add a reference to the WindowsFormIntegration assembly.Now we will add some code to the App.xaml and App.xaml.cs files.

In the App.xaml file we add the Startup application event.Have a look at the code below.

<Application x:Class="WindowsFormToWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" Startup="Application_Startup"  >

We add the event handling routine for the event in the App.xaml.cs

private void Application_Startup(object sender, StartupEventArgs e)
{
WindowsFormsHost.EnableWindowsFormsInterop();
           //not necessary when running on windows vista or 7. But needed when you run on windows XP
System.Windows.Forms.Application.EnableVisualStyles();
}
}

As you can see I use the WindowsFormsHost class that allows us to host a Windows Forms control on a WPF application.

23)  Launch your application again. Click on the “Footballers form” window and in the window that pops up, try to use the tab key to move between the fields of the record. You will be successful this time.

24) Now we can see another example where you can add an existing WPF window to a Windows Forms application.We need to add some references and Visual studio open the WPF window in the XAML designer.We will use the ElementHost class that can be used to host a Windows Presentation Foundation (WPF) element inside a Windows Forms application.Close this project/application and create a new Windows Forms application.Now we need to add a new WPF window. We are going to use an existing one, the one we created in the previous application, Footballers.xaml. In order to add all the necessary references to the Windows  Forms application, in the Xaml designer drag and drop a label control on the Footballers.xaml.When you do that all the necessary references are added.Some of them are (Accessibility,PresentationCore, PresentationFramework).Now remove the label control.Add a button on the Windows form (main windows form-Form1.cs).In the button1_click event handling routine type

private void button1_Click(object sender, EventArgs e)
{
var footballers = new Footballers();

footballers.Show();
}

25) Now run your application and click on the button. The WPF window will show just fine.Try to move up and down the records using the up and down arrows from the keyboard. That will not work for the same reasons I explained before. The Windows Form (main – host application) receives the input messages from the keyboard but does not know how to pass them on to the WPF window.Let’s fix that.We need to add a reference to our application. We need to add a reference to the WindowsFormsIntegration assembly.In the button1_click event handling routine we need to add another line of code. The complete code follows.

private void button1_Click(object sender, EventArgs e)
{
var footballers = new Footballers();
            System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop(footballers);
footballers.Show();
}

26) Now let’s run the application again. Click on the button and in the WPF window move up and down the records using the up and down arrows from the keyboard. You will be successful this time.

27) We can also add a Windows Forms built-in or user control to a WPF window and vice versa.I will demonstrate that with additional examples.

28) I will be showing your first how to add a Windows Forms control in a WPF window.In order to do that I will be using the WindowsFormsHost class in the System.Windows.Forms.Integration namespace.

29) Create a new WPF application and choose a suitable name for it. Choose C# as the development language. Add references to the System.Windows.Forms and WindowsFormIntegration assemblies.I will be adding in XAML the assembly reference for Window Forms and mscorlib assemblies.

 xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:sys="clr-namespace:System;assembly=mscorlib"

30) Now in the XAML I will be adding a MaskedTextBox control that does not exist in the WPF ecosystem.This is the code in the MainWindow.xaml

<WindowsFormsHost>

   <wf:MaskedTextBox x:Name="maskedTextBox" Mask="(999)-0000000" ValidatingType="{x:Type sys:Int32}" ></wf:MaskedTextBox>

  </WindowsFormsHost>

Run your application and you will see the masked textbox appearing with no problems at all.

31) Finally I am going to provide an example on how to use WPF controls in WinForms Apps.Obviously we need to add references to the needed assemblies.We are going to use the ElementHost control. Add a new folder in your application, a WPF user control.Name it FrameWorkControl.xaml.

32) Αdd a reference to the System.Xaml assembly.

33) The code for the FrameWorkControl.xaml follows. I am basically having a ListBox control with some TextBlock elements inside a stackpanel.

    <Grid>   <ListBox Margin="5" Grid.Row="1" Grid.Column="0"
Name="DotNetListBox"
SelectionChanged="DotNetListBox_SelectionChanged">
<StackPanel Margin="0,0,0,5" Orientation="Horizontal">
<Label Content="ASP.Net" Padding="10" Margin="10" FontFamily="Batang" FontSize="16"></Label>
<TextBlock MaxWidth="500" Margin="10,0,0,0" TextWrapping="Wrap" Tag="ASP.Net"
Text="ASP.NET is the next generation ASP, but it's not an upgraded version of ASP. ASP.NET is an entirely new
technology for server-side scripting" Width="265" Height="66"></TextBlock>
</StackPanel>
<StackPanel Margin="0,0,0,5" Orientation="Horizontal">
<Label Content="WPF" Padding="10" Margin="10" FontFamily="Batang" FontSize="16"></Label>
<TextBlock MaxWidth="500" Margin="10,0,0,0" TextWrapping="Wrap" Tag="WPF"
Text="Windows Presentation Foundation, WPF, provides a unified framework for building applications and
high-fidelity experiences in Windows that blend application UI, documents, and media content" Width="300" Height="94"></TextBlock>
</StackPanel>
</ListBox>
</Grid>

34) For the DotNetListBox_SelectionChanged event handling routine type.

public string SelectedTechnology { get; set; }

        private void DotNetListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
        TextBlock dotnetText = ((StackPanel)DotNetListBox.SelectedValue).Children.OfType<TextBlock>().First();
        this.SelectedTechnology = dotnetText.Tag.ToString();
        }
    }
}

35) Drag and drop an ElementHost control.Click the smarttag icon and in the “Selected Hosted Content:” select the FrameWork control.Now our user control is inside (hosted) inside the Windows Forms application.Resize appropriately.

36) Now add a button in the main form. In the click event handling routine type

private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(string.Format( "You selected {0}",((FrameWorkControl)elementHost1.Child).SelectedTechnology));
        }

Run your application. Select you favourite technology and click the button. Your favourite technology will pop up.

I understand that this is a very long post but hopefully you will find it very interesting.

Leave a comment with your email if you need the source code.

Hope it helps!!!!

One of the earliest-but still important-tenets of software engineering is the concept of reuse with “software building blocks”. In the .NET framework, user controls are the quintessential building blocks, giving you a simple yet powerful way to encapsulate a set of visual components into a more complex one. This holds true both in the WinForms realm and the WPF realm. That is, you can build WinForms user controls and embed them in WinForms applications, and likewise build WPF user controls and embed them in WPF applications. As a designer in at least one of these realms, you likely know this well.

But what is not well known, nor particularly obvious, is that you can build hybrids, embedding WPF controls in a WinForms application or WinForms controls in a WPF application. All you need is a simple adapter when using controls from the opposite realm (Figure 1). I deliberately use the term control in the figure rather than user control because this interoperability is not limited to user controls. You can embed any controls-your custom controls or standard .NET controls-from one technology into the other. This article provides an in-depth look into the symmetries of hybrid applications. As a by-product, the occasional asymmetries become readily apparent. Here is the first one: you can actually embed a broader set of WPF elements into a WinForms application: not just elements that derive from Control, but those that derive from a broader base class called UIElement (see Embedding Standard .NET Components for more on this).

1113-clip_image001.jpg

Figure 1 WPF / WinForms interoperability

With a simple adapter you can embed standard or custom elements from the alternative technology in your application.

  • When is it useful to create a hybrid application?
  • If you want to migrate to WPF but have a huge investment in WinForms libraries.
  • If you want to add WPF power to your existing WinForms applications.
  • If you want some UI component not available in your “native” technology.

Consider these main points when thinking about where your own roadmap should be leading you:

  WPF WinForms
Longevity   WinForms has been around longer, so many applications and libraries utilize it.
Richness   More controls are available in WinForms than WPF
Power WPF is more expressive, flexible, and customizable, including adding 3D graphics and animation.  
Compatibility   WinForms can run on older systems.

But also keep in mind that WPF / WinForms interoperability is not without costs and issues. Rather than me describing potential pitfalls, take a look at what Scott Berry, a developer on Microsoft’s UI Frameworks team specializing in WinForms / WPF interop, has to say in his list of collected Gotchas for Working with Windows Forms/WPF Interop

A Brief Groundwork

To get the most out of this article you should be an experienced .NET developer from either the WinForms realm or the WPF realm. I wrote it as an introductory exposition but it assumes at least a reading-familiarity with both technologies. That is, I do not going into inordinate detail of getting started in WPF or WinForms, but by taking advantage of inherent symmetries of the two, your experience in one technology will aid in understanding the other. After setting the stage in these next two sections, the remainder of this article is organized as two articles in parallel, walking you through hosting a WinForms user control in a WPF application on the left, and hosting a WPF user control in a WinForms application on the right.

Before getting into interoperability, just a word about controls. You use standard controls all the time. In Visual Studio you drag controls-buttons, text boxes, grids, etc.-from the toolbox onto the designer surface to build your user interface. If you find yourself repeating the same group of five controls in each new project, bundle them together as a user control. Once you create such a composite control you can add it back to the toolbox so it becomes just another standard control. You can even repeat the process to make composite controls consisting of other composite controls. (Taken to the extreme, your entire UI could be just a single, gargantuan user control-though I would not recommend that!)

So reuse is the first reason to create your own user controls. The second main reason is encapsulation. Good software design dictates that each element (be it a file, class, structure, etc.) should have high cohesion and loose coupling. As a trivial (and contrived!) example, say the left side of your user interface has a collection of controls all associated with file management and the right side has a collection of controls all related to color adjustments. You could have a flat design where all of these exist just in your main application class, but this cries out for encapsulating the left side controls into one composite control and the right side controls into another. For more on coupling and cohesion, see this post by Sammy Larbi.

This article briefly describes how to build user controls. If you want more in-depth information, though, I refer you to two of my earlier articles on DevX.com (which does require registration nowadays but it is free). Both articles discuss WinForms user controls, but if you are WPF-leaning you can still glean some useful concepts from them.

  • .NET Building Blocks: Custom User Control Fundamentals
  • .NET Building Blocks: Custom User Controls In Practice

Also, these MSDN articles are good starting points as reference material rather than guides:

  • For WPF: Control Authoring Overview
  • For WinForms: Varieties of Custom Controls

A Dual Demo

The .NET documentation repository on MSDN provides, as usual, some valuable insights into the topic of WPF-WinForms interoperability. In fact, the example I use throughout this article originated from two separate articles on MSDN:

  • Hosting a Windows Forms Composite Control in WPF
  • Hosting a WPF Control in Windows Forms

I started with their two sample applications, refactored the code for clarity and simplicity, and combined the two separate projects into a single solution that makes the whole process much more digestible. Furthermore, I restructured the code in parts to be as nearly identical as possible when the originals had variations that were distracting. This lets you see the meaningful differences between the WPF and WinForms technologies. Here are some of the changes I implemented for this article to emphasize parallelism:

  • Combined two separate solutions into a single solution.
  • Converted from Visual Studio 2010 to Visual Studio 2008 to allow access to a broader audience. [3]
  • Renamed projects (MyControls => WFControlLibrary and MyControls => WPFControlLibrary as appropriate).
  • Renamed classes (CustomControl1 => WPFInputControl, MyControl1 => WFInputControl).
  • Converted WPFInputControl (CustomControl1) from a Grid derivative to a true user control.
  • Renamed methods (MyControlEventArgs => InputControlEventArgs, avAddressCtrl_Loaded => Panel_Loaded).
  • Refactored common class into new CommonLibrary (InputControlEventArgs).
  • Refactored code to delineate functionality (input vs. output data flow in WPFInputControl.xaml.cs).
  • Refactored/mirrored code across projects (WPFInputControl.ButtonClicked and WFInputControl.ButtonClicked).
  • Added a simple WPF test container to roughly parallel the built-in WinForms test container.
  • Rewrote property setters in WPFInputControl to eliminate massive code duplication.

Figure 2 The Demo Solution in Visual Studio

The solution of six simple projects lets you
 readily see how to build hybrid applications.

The original two articles had a total of four projects split across two solutions: a WPF host, a WPF control, a WinForms host, and a WinForms control. As shown in Figure 2, the demo application that accompanies this article includes six projects in a single solution. The two additional projects are the CommonLibrary, a library of non-UI classes used by both user controls, and the WpfTestContainer-see Runtime Representation of a WPF User Control. (If you peruse the original two MSDN articles, be aware that there are a few errata that I have noted in footnotes [1] and [2].)

Structure of a UI Component

Structure of a WPF UI Component

The structure of a WPF application consists of three pieces stored in two files (Figure 3). The WPF designer operates on both the design view and the XAML view of the component. These are two views of the same code that is stored in a file with a .xaml suffix.

  1. The design view is a WYSIWYG environment where you may drag items from the toolbox and arrange them graphically.
  2. The XAML view is the generated code equivalent of the design. (Unlike WinForms, WPF code uses XAML, a markup language similar in concept to HTML.) In the WPF designer you can manipulate your layout just as effectively (sometimes more so) in the XAML view as compared to the design view.
  3. Code that you write in C# to support your design, known as the code-behind, is stored in a file with a .xaml.cs suffix. This contains your manually created code, including event handlers and other support code. In WPF applications, anything you can do in XAML you can also do in C#. So it is possible for you to programmatically create all or part of your layout in C#; in practice, however, it is much less common to code visual components in the code-behind compared to WinForms applications.
  4. For completeness, there is actually a fourth file that I do not show in the figure. This has a .g.cs suffix and is not stored in the same directory as the .xaml and .xaml.cs files. It is generated at compile time as is stored in your obj/Debug or obj/Release folder. It is somewhat similar to the code stored in the .designer.cs file of a WinForms application, in that it is auto-generated C# code. You may never need to look at it but I have found an occasional need to peek at it.

Structure of a WinForms UI Component

The structure of a WinForms application consists of three pieces stored in two files (Figure 3). The Windows Forms designer operates on both the design view of the component and the auto-generated code that defines it. These are two views of the same code that is stored in a file with a .designer.cs suffix.

  1. The design view is a WYSIWYG environment where you may drag items from the toolbox and arrange them graphically.
  2. The generated code equivalent of the design view is not shown in the WinForms designer. To view the code, select the .designer.cs file in the solution explorer and open it. (Unlike WPF, WinForms code uses C# to define the design, the same as in the code-behind.) While you can edit this file (and experienced designers frequently do) you need to preserve its structure carefully since later changes in the designer may overwrite your edits if you are not careful.
  3. Code that you write in C# to support your design, known as the code-behind, is stored in a file with a .cs suffix. This contains your manually created code, including event handlers and other support code. In WinForms applications, this file may also-and frequently does-contain visual components that you elect to create programmatically rather than with the designer.

1113-clip_image003.jpg

Figure 3 Structure of UI components in both technologies

The WPF designer works strictly with the .xaml file while the Windows Forms designer works with the .designer.cs file. The code-behind file in each is where you write your support code.

Designing a Simple User Control

Designing a Simple WinForms User Control

The WFControlLibrary project defines a simple collection of Labels and TextBoxes that specify a name and address, along with an OK button to accept the input and a Cancel button to ignore the input (Figure 4). When either button is clicked, the control generates a custom event that may be consumed by a host application. The event includes all the values entered by the user along with a status (the IsOK field) that indicates which button was clicked.

Designing a Simple WPF User Control

The WPFControlLibrary project defines a simple collection of TextBlocks and TextBoxes that specify a name and address, along with an OK button to accept the input and a Cancel button to ignore the input (Figure 4). When either button is clicked, the control generates a custom event that may be consumed by a host application. The event includes all the values entered by the user along with a status (the IsOK field) that indicates which button was clicked.

1113-clip_image005.jpg

Figure 4 Equivalent user controls in the WPF designer and Windows Forms designer

The two controls are semantically equivalent, though the layout varies just a bit. (There is no special reason for the variation; I believe it is due simply to the fact that the original implementations from Microsoft were either done by two different people or at different times.)

The code-behind for the control is shown below, which is the complete code except for the designer-generated portion backing the layout.

The code consists primarily of the described event handler that processes clicks from both buttons. The IsOK field is set true by default (the first argument to the InputControlEventArgs constructor). If the Cancel button was the one clicked, IsOK is reset to false.

public class WFInputControl : UserControl

{

  public WFInputControl()

  {

    InitializeComponent();

  }

  public delegate void

    MyControlEventHandler(

      object sender,

      InputControlEventArgs args);

  public event

    MyControlEventHandler OnButtonClick;

  private void Button_Click(

    object sender, EventArgs e)

  {

    InputControlEventArgs retvals =

      new InputControlEventArgs(true,

        txtName.Text,

        txtAddress.Text,

        txtCity.Text,

        txtState.Text,

        txtZip.Text);

    if (sender == btnCancel)

      retvals.IsOK = false;

    if (OnButtonClick != null)

      OnButtonClick(this, retvals);

  }

}

The only code differences between this user control and the WPF user control are: the class and constructor name, the event handler name, and the type of the event handler’s second argument. The class diagrams shown in Figure 5 highlight the similarities between the two.

The code-behind that manages passing data to a host application is shown below. For simplicity, the user interface (XAML) code is omitted, as is the code-behind for passing data from the host back to the control, which will be discussed later.

The code consists primarily of the described event handler that processes clicks from both buttons. The IsOK field is set true by default (the first argument to the InputControlEventArgs constructor). If the Cancel button was the one clicked, IsOK is reset to false.

public class WPFInputControl : UserControl

{

  public WPFInputControl()

  {

    InitializeComponent();

  }

  public delegate void

    MyControlEventHandler(

      object sender,

      InputControlEventArgs args);

  public event

    MyControlEventHandler OnButtonClick;

  private void Button_Clicked(

    object sender, RoutedEventArgs e)

  {

    InputControlEventArgs retvals =

      new InputControlEventArgs(true,

        txtName.Text,

        txtAddress.Text,

        txtCity.Text,

        txtState.Text,

        txtZip.Text);

    if (sender == btnCancel)

      retvals.IsOK = false;

    if (OnButtonClick != null)

      OnButtonClick(this, retvals);

  }

}

The only code differences between this user control and the WF user control are: the class and constructor name, the event handler name, and the type of the event handler’s second argument. The class diagrams shown in Figure 5 highlight the similarities between the two.

It is important to realize how this code file ties to the XAML file. Notice the class header above, which states that WPFInputControl derives from UserControl. You will see this mirrored exactly in the root element of your XAML:

<UserControl x:Class=

  “WpfControlLibrary.WPFInputControl”

  . . .

Finally, note that you do not strictly need to make your WPF composite class inherit from a UserControl to be eligible to be hosted in a WinForms application. You could use a standard WPF application (with a <Window> root element), a WPF browser application (<Page>), or other containers. The sample code with the MSDN article, for instance, just uses a

<Grid> root element. This flexibility greatly broadens the universe of WPF components that you may embed within a WinForms application.

1113-clip_image006.jpg

Figure 5 Class diagrams for the user controls

The interfaces between the two are very similar: these expose the same, single event; they use the same delegate, and they have no methods other than a simple constructor. The WPF control does, however, need some properties to be exposed as well.

Runtime Representation

Runtime Representation of a WinForms User Control

When you create a WinForms user control using the Windows Forms designer, you see a design-time representation. But it would also be nice to see a runtime representation.

You could create a simple WinForms wrapper application in which you embed your user control. But Visual Studio saves you the effort; it provides a UserControl TestContainer for WinForms development. Simply set your user control library to be your startup project and execute it (F5). Normally you cannot execute a class library, but WinForms user controls are an exception. Visual Studio launches its test container host that contains a dropdown listing all of your user controls in the current library. Select one and it is rendered just as if you had wrapped it into your own host application. Additionally, the test container provides a property pane that gives you access to all of the control’s properties so you can manipulate them and see how your control behaves (see Figure 6).

Runtime Representation of a WPF User Control

When you create a WPF user control using the WPF designer, you see a design-time representation. But it would also be nice to see a runtime representation.

You could create a simple WPF wrapper application in which you embed your user control. (Alas, Visual Studio does not provide any similar TestContainer for WPF development.) So for the purpose of this article, I have included a basic wrapper application called WpfTestContainer (one of the six projects listed in Figure 3). This wrapper lets you see how your user control renders, just as if you had wrapped it into your own host application. It does not, however, provide any property access equivalent to the WinForms support. This WPF Test Container is illustrated in Figure 6.

1113-clip_image008.jpg

Figure 6 TestContainers

The built-in TestContainer for WinForms user controls provides a powerful mechanism that exposes all the controls in your library and all of the selected control’s properties. WPF has no similar mechanism but I provide a basic wrapper in the demo solution.

Hosting a User Control

Hosting a WinForms User Control in a WPF Application

Once you are satisfied with your WinForms user control, you are ready to host it in a WPF application. Unlike native (WinForms) controls, you cannot insert it directly: you must use a  WindowsFormsHost Class as an adapter between the WPF host application and the WinForms user control, as shown in Figure 1.

To add a WindowsFormsHost, then, you can drag it from the toolbox onto the designer or just enter it in XAML. Either way, you then add your foreign (WinForms) user control in XAML. Here is the resultant XAML that instantiates both the WindowsFormsHost and the WFInputControl user control:

<WindowsFormsHost

  id=”wfh””

  DockPanel.Dock=”Top”

  Height=”250″ Width=”375″>

    <myctrl:WFInputControl

      id=”wfInputControl””/>

</WindowsFormsHost>

Just as when working in C#, though, you need to give Visual Studio proper references so it knows what a WFInputControl is, along with its namespace designator, myctrl. Whereas you add using directives in C#, you add namespace declarations to some parent element of your element in XAML (typically the root element, just as you typically put all your using statements at the top of a file). Here is the root element for the WpfHost application; the last line is the namespace declaration that defines the “myctrl” namespace designator and maps it to the WFControlLibrary DLL.

<Window x:Class=”WpfHost.MainWindow”

    xmlns=”http://schemas.microsoft…”

    xmlns:x=”http://schemas.microsoft…”

    xmlns:myctrl=

      “clr-namespace:MyControls;

       assembly=WFControlLibrary”

To make best use of Intellisense, it is better to add these two code fragments in the opposite order: first enter the namespace declaration so when you instantiate the control Intellisense can assist you. Intellisense first assists with the namespace declaration itself: In Figure 7 I typed up to the equals sign (xmlns:local=) whereby Intellisense added the quotes and then popped up the list of available libraries. Note that for the WFControlLibrary to appear in the list you must add to the current project a reference to the assembly containing your user control library (right click References in the solution explorer and choose Add Reference). Selecting an item from this list inserts it with proper syntax, as shown in the code fragment shown above. With the assembly named and referenced in your root XAML element (equivalent to the C# using directive in WinForms), Intellisense can now guide you in instantiating the user control. Referring back to the WindowsFormsHost element shown above, start with no content then start to type the namespace reference you created:

<WindowsFormsHost

  id=”wfh””

  DockPanel.Dock=”Top”

  Height=”250″ Width=”375″>

    <my

</WindowsFormsHost>

At that point Intellisense will popup myctrl: as a choice. Accept that and Intellisense returns immediately showing you available user controls you may select.

Hosting a WPF User Control in a WinForms Application

Once you are satisfied with your WPF user control, you are ready to host it in a WinForms application. Unlike native (WPF) controls, you cannot insert it directly: you must use an ElementHost  as an adapter between the WinForms host application and the WPF user control, as shown in Figure 1.

To add an ElementHost, then, you can drag it from the toolbox onto the designer or enter it in your code-behind file. Either way, you then add your foreign (WPF) user control. If the library containing the control is in the same solution as your application you can do this directly in the Windows Forms designer. Figure 8 shows an example of this: after dragging an ElementHost onto the design surface, it automatically opens its task list so you can select a control to host.

If, as is more typical, the control library is not in your current solution, you can still add the ElementHost in the designer but you must add the user control in your code-behind file. Here is the code to instantiate both the ElementHost and the WPFInputControl from the code-behind file for the demo project:

private ElementHost ctrlHost;

private WPFInputControl wpfInputControl;

// from the load event handler

ctrlHost = new ElementHost();

ctrlHost.Dock = DockStyle.Fill;

panel1.Controls.Add(ctrlHost);

wpfInputControl = new WPFInputControl();

wpfInputControl.InitializeComponent();

ctrlHost.Child = wpfInputControl;

Just as when working in XAML, though, you need to add to the current project a reference to the assembly containing your user control library (right click References in the solution explorer and choose Add Reference) so Visual Studio knows about the namespace you need. With the reference in place, you can then declare the namespaces you need with appropriate using directives at the top of the file (equivalent to the namespace specifiers in the root element in XAML). You will also need a using directive for the namespace of ElementHost. Both of these are shown highlighted here:

using System;

using System.Windows;

using System.Windows.Forms;

using System.Windows.Forms.Integration;

using WpfControlLibrary;

To make best use of Intellisense, it is better to add these two code fragments in the opposite order: first enter the using directives so when you instantiate the controls Intellisense can assist you.

1113-clip_image009.jpg

Figure 7 Intellisense aids WPF namespace declarations

The figure shows the same screenshot in Visual Studio 2008 (top) and Visual Studio 2010 (bottom).

1113-clip_image010.jpg

Figure 8 Designer support for the ElementHost object in the Windows Forms designer

When you insert an ElementHost from the toolbox (left) it automatically opens the task list on the designer surface (you can close it or reopen it with the small caret button marked in red. Within the task list opening the dropdown (also marked in red) lists the available user controls present in any projects within the current solution.

Embedding Standard .NET Components

Embedding Standard .NET Components in a WPF Application

You are not restricted to only embedding custom controls in a WindowsFormsHost control. Here in year 2010 the WinForms library offers more choices of standard controls than WPF. You can easily embed one using the same techniques you just saw.

1. Add a reference to the standard WinForms library (System.Windows.Forms.dll) to your project.

2. Add a namespace declaration to your root XAML element:

xmlns:wf=

  “clr-namespace:System.Windows.Forms;

  assembly=System.Windows.Forms”

3. Specify the control inside your WindowsFormsHost: just type wf: and Intellisense provides a list of all the WinForms controls from which to select. Example:

<wf:MaskedTextBox

  x:Name=”mtbDate”

  Mask=”00/00/0000″/>

In fact, any class that derives from System.Windows.Forms.Control may be embedded in similar fashion.

Embedding Standard .NET Components in a WinForms Application

You are not restricted to only embedding custom controls in an ElementHost control. Here in year 2010 the WPF library provides some powerful controls that are unavailable in the WinForms realm. You can easily embed one using the same techniques you just saw.

1. Add a reference to the standard WPF library (Presentation.Framework.dll) to your project.

2. Add namespace directives to the top of your code-behind file:

using System.Windows;

using System.Windows.Controls;

3. Instantiate the control (or other UIElement-based object) and attach it to the ElementHost in your form’s constructor or load event handler. Example:

UIElement uiElement = new PasswordBox();

elementHost1.Child = uiElement;

In fact, any class that derives from System.Windows.UIElement may be embedded in similar fashion. (UIElement encompasses System.Windows.Controls.Control and is thus a much broader set of elements than may be hosted in a WPF application.)

Design time vs. Runtime Representation

Design time vs. Runtime Representation for a WPF Application

Even with the user control instantiated in your visual tree, only the WindowsFormsHost appears in the WPF designer-it does not actually render the child user control. So to check its placement and appearance you must actually execute the application. Figure 9 shows the WPF designer view and the runtime view.

Design time vs. Runtime Representation for a WinForms Application

If you happen to have your user control library in the same solution and you instantiated it in the Windows Forms designer, the designer renders the user control at design time. But, as stated above, typically you have some external library so you will need to add your user control in code-behind. So to check the placement and appearance of the user control you must actually execute the application. Figure 9 shows the Windows Forms designer view and the runtime view. Note that this particular WinForms application reveals an obscure defect in Visual Studio 2010 where the radio button labels are truncated. [4]

1113-clip_image011.jpg

Figure 9 Rendering embedded controls of the “opposite” technology

In the WPF and Windows Forms designers (top), neither technology has support to show the embedded control. Once you execute (bottom) the controls appear. The one exception is that if your WPF control is in the same solution it will be displayed in the Windows Forms designer.

With the user control now incorporated into your application, you need to “wire it up” so that you can act on its content when a user clicks on one of its buttons. For the purposes of this article, the demo application illustrates how to flow data in both directions: data from the user control (name and address) passes to your application and data from the application (layout setting properties) passes to the control. (Thanks again to the Microsoft team for the first pass of these dual applications!)

Output Data Flow

Output Data Flow: From User Control to WPF Host

The goal here is to take data entered directly in the user control (Figure 10, top) and report it back in the application (Figure 10, bottom). The WinForms user control fires the OnButtonClick event when either the OK button or the Cancel button is clicked (code shown earlier). In the form load event handler for the main application, then, connect a handler to that event on the control. Because the control is hosted in a WindowsFormsHost and not instantiated directly, access the user control from the Child property of its host container. Cast this to the right type then you can assign the event handler just as with any other control.

Output Data Flow: From User Control to WinForms Host

The goal here is to take data entered directly in the user control (Figure 10, top) and report it back in the application (Figure 10, bottom). The WPF user control fires the OnButtonClick event when either the OK button or the Cancel button is clicked (code shown earlier). In the form load event handler for the main application, then, connect a handler to that event on the control. The control is hosted in an ElementHost and may be accessed from the Child property of its host container, just as was done in the WPF application. However, since the WPFInputControl was added to the layout in the code-behind file, it is immediately accessible so can be used directly.

1113-clip_image012.jpg

Figure 10 Focus on data flow from the user control to the application

When you click the OK button in the user control (top) it feeds data back to the application in the panel at the bottom.

private void Init(object sender, EventArgs e)

{

  . . .

  (wfh.Child as WFInputControl)

    .OnButtonClick

      += new WFInputControl

        .MyControlEventHandler(

          Pane1_OnButtonClick);

  . . .

}

The Init method is specified as the form load event handler in the root element in XAML:

<Window x:Class=”WpfHost.MainWindow”

    . . .

    Loaded=”Init”>

Finally, here is the event handler method to receive either button click. It first clears all the text fields (Figure 10, bottom). If the IsOK property is true-indicating the OK button was clicked rather than the Cancel button-it then fills in the displayed fields with data passed in from the event.

private void Pane1_OnButtonClick(

  object sender,

  InputControlEventArgs args)

{

  txtName.Inlines.Clear();

  txtAddress.Inlines.Clear();

  txtCity.Inlines.Clear();

  txtState.Inlines.Clear();

  txtZip.Inlines.Clear();

  if (args.IsOK)

  {

    txtName.Inlines.Add(

      ” ” + args.MyName);

    txtAddress.Inlines.Add(

      ” ” + args.MyStreetAddress);

    txtCity.Inlines.Add(

      ” ” + args.MyCity);

    txtState.Inlines.Add(

      ” ” + args.MyState);

    txtZip.Inlines.Add(

      ” ” + args.MyZip);

  }

}

private void Form_Load(object sender, EventArgs e)

{

  . . .

  wpfInputControl

    .OnButtonClick

      += new WPFInputControl

        .MyControlEventHandler(

          Panel_OnButtonClick);

  . . .

}

The Form_Load method is specified as the form load event handler in the generated code file (Form1.Designer.cs):

private void InitializeComponent()

{

  . . .

  this.Load += new System

    .EventHandler(this.Form1_Load);

  . . .

}

Finally, here is the event handler method to receive either button click. If the IsOK property is true-indicating the OK button was clicked rather than the Cancel button-it fills in the displayed fields (Figure 10, bottom) with data passed in from the event; otherwise, it omits any returned data.

private void Panel_OnButtonClick(

  object sender,

  InputControlEventArgs args)

{

  if (args.IsOK)

  {

    lblAddress.Text =

       “Street: ” + 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: “;

    lblCity.Text = “City: “;

    lblName.Text = “Name: “;

    lblState.Text = “State: “;

    lblZip.Text = “Zip: “;

  }

}

This particular demo control is intended for output data flow, i.e. sending its collected data to its host application. However, for educational purposes it was set up to illustrate input data flow as well, because the mechanisms are quite different. The left hand side of the demo application provides a series of layout property settings that you may adjust at runtime with the various radio buttons provided. As you select different settings you should see them immediately reflected in the user control.

Input Data Flow

Input Data Flow: From User Control to WPF Host

The demo application lets you adjust:

  • Background color of the user control.
  • Foreground (font) color of text labels.
  • Other font properties of both the text labels and the user-supplied text values.

Figure 11 shows the available choices.

Input Data Flow: From User Control to WinForms Host

The demo application lets you adjust:

  • Background color of the user control.
  • Foreground (font) color of text labels.
  • Other font properties of text labels.

Figure 11 shows the available choices.

1113-clip_image013.jpg

Figure 11 Focus on data flow from the application to the user control

Making selections among the radio buttons shown adjusts properties within the respective user controls.

Here is how the on-screen controls change the properties of the embedded user control.

This excerpt from the XAML for the WPF application shows the group of three radio buttons that control the font family. The first one has the IsChecked property set true so that button will be checked on-screen by default. All three buttons specify the same event handler, FontChanged.

<TextBlock

  Style=”{StaticResource titleText}”>

  Font Family

</TextBlock>

<StackPanel Margin=”10,10,10,10″>

  <RadioButton id=”rdbtnOriginalFamily””

     IsChecked=”True”

     Click=”FontChanged”>Original

  </RadioButton>

  <RadioButton id=”rdbtnTimes””

     Click=”FontChanged”>Times New Roman

  </RadioButton>

  <RadioButton id=”rdbtnWingdings””

     Click=”FontChanged”>Wingdings

  </RadioButton>

</StackPanel>

The event handler determines which button was clicked and sets the FontFamily property on the WindowsFormsHost object (wfh) to the appropriate font. (You can see the XAML fragment that declares wfh back at Hosting a WinForms User Control in a WPF Application.)

private void FontChanged(

  object sender, RoutedEventArgs e)

{

  if (sender == rdbtnTimes)

    wfh.FontFamily =

      new FontFamily(“Times New Roman”);

  else if (sender == rdbtnWingdings)

    wfh.FontFamily =

       new FontFamily(“Wingdings”);

  else if (UIIsReady == true)

    wfh.FontFamily =

       initFontFamily;

}

Setting the property of the WindowsFormsHost is all that is needed to propagate the change down to individual components in the WFInputControl; no code support is needed in the control itself, unlike in the WinForms application. The five other properties that you may adjust at runtime all have analogous code so there is no need to show it here. The behavior of all six property setters, however, is not exactly the same: this demo reveals one curious artifact of the property inheritance. Except for the first property, background color, all the remaining properties affect text. So focusing just on the remaining five, setting the foreground color (i.e. the text color) applies only to the Label controls that label each field in the WFInputControl whereas setting any of the other four text properties changes the corresponding properties of both the Label controls and the TextBox controls. [5]

Here is how the on-screen controls change the properties of the embedded user control.

This excerpt from the generated layout code for the WinForms applications shows where each radio button related to font family specifies its separate event handler. (Note that this could have just as easily been done with a single event handler like the WPF application; this is a style preference of the original developer.)

radioForegroundOriginal

  .CheckedChanged += new EventHandler(

  radioForegroundOriginal_CheckedChanged);

radioFamilyWingDings

  .CheckedChanged += new EventHandler(

  radioFamilyWingDings_CheckedChanged);

radioFamilyTimes

  .CheckedChanged += new EventHandler(

  radioFamilyTimes_CheckedChanged);

With separate event handlers the code of each one is much simpler-trivial in fact. The trade-off is that this approach uses three event handlers instead of just one.

private void

  radioFamilyOriginal_CheckedChanged(

    object sender, EventArgs e)

{

  wpfInputControl

    .MyControl_FontFamily =

      initFontFamily;

}

private void

  radioFamilyTimes_CheckedChanged(

    object sender, EventArgs e)

{

  wpfInputControl

    .MyControl_FontFamily =

      new System.Windows.Media

        .FontFamily(“Times New Roman”);

}

private void

  radioFamilyWingDings_CheckedChanged(

    object sender, EventArgs e)

{

  wpfInputControl

    .MyControl_FontFamily =

      new System.Windows.Media

        .FontFamily(“WingDings”);

}

Each event handler sets the appropriate property on the WPF user control directly, in this case the MyControl_FontFamily property, unlike in the WPF application where you need only set properties on the host container. Here is the property definition from the code of the user control.

public FontFamily MyControl_FontFamily

{

  get { return _fontFamily; }

  set

  {

    _fontFamily = value;

    textBlockList.ForEach(

      tb => tb.FontFamily = value);

  }

}

This property setter does two things. First, it remembers the value passed in so that the property getter can return it when asked. Second, it sets the FontFamily property of each TextBlock that labels each field. (Label controls could have served equivalently here.) Five of the six properties use exactly analogous setters. The background color is different, though, because it applies to the control itself rather than to content of an individual TextBlock. Here is its getter and setter:

public SolidColorBrush

  MyControl_Background

{

  get { return _background; }

  set

  {

    _background = value;

    rootElement.Background = value;

  }

}

Going Further

If you have reached this point after completing either or both tracks of this article, you should understand the fundamentals of WPF / WinForms interoperability. Every case is different, though, so chances are you will encounter issues that require more in-depth knowledge. Here are just a few sources that can aid you in your continuing journey.

  • Microsoft’s authoritative Migration and Interoperability section of MSDN
  • Windows Forms – WPF Interoperability FAQ
  • WPF Designer for Windows Forms Developers

[1]  Errata in Hosting a Windows Forms Composite Control in WPF:

  • OKButton_Click and CancelButton_Click methods should be more robust and check whether the OnButtonClick event is null before invoking it.
  • The article claims the user control assembly must have a strong name to be referenced by a WPF application; in practice this does not seem to be the case.
  • The article talks about creating the WPF host application as a WPF Browser Application. The code that accompanies the article, however, uses a WPF Application (i.e. a desktop app).
  • The XAML for the WPF host application is accidentally duplicated in the article.

[2]  Errata in Hosting a WPF Control in Windows Forms:

  • The article talks about creating the WPF user control as a WPF Browser Application. The code that accompanies the article, however, implements it just as a component (with a Grid as the top-level element).

[3] I converted these solutions manually by recreating them since I knew I would be doing quite a bit of reorganization at the same time. If you have substantial projects that you want to back port, however, I found a couple references to convert VS2010 to VS2008 that may be of interest to readers. Convert VS2010 Projects back to VS2008 ones… and Converting a Visual Studio 2010 Project to Visual Studio 2008 .

[4] When you open the main form of the WFHost project, Form1, in the in the Windows Forms designer, it displays just fine in Visual Studio 2008. However, in Visual Studio 2010 all the radio buttons are curiously truncated by 1 or 2 characters! I have submitted this defect to Microsoft Connect (winforms visual designer strangely truncates text-oncertain elements). At the time of writing the report has been accepted and routed to the product team for “triage and resolution”.

[5] The fact that different properties percolate differently through the hosted WinForms user control to its children is an inconsistency in .NET. It may just be my ignorance of the nuances of inheritance-particularly cross-technology inheritance; however, I still believe it is worth reporting so I submitted this to Microsoft Connect (windowsformshosts property settings do not filter down to all expected child components).  At the time of writing the report has been accepted and routed to the product team for “triage and resolution”.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Windows 10 upgrade корпоративная
  • Dinoraptzor org как удалить windows 11
  • Как открыть управление томами в windows 10
  • Usb2 0 ser driver windows 7 x32
  • Daemon tools lite для windows 10 portable