Enhancing Usability of Windows Presentation Foundation (WPF) and Windows Forms Applications

  • 2/15/2011

Lesson 3: Integrating Windows Forms Controls and WPF Controls

The WPF suite of controls is very full, and, together with the WPF control customization abilities, you can create a very wide array of control functionality for your applications. Some types of functionality, however, are absent from the WPF elements and can be difficult to implement on your own. Fortunately, WPF provides a method for using Windows Forms controls in your application. Likewise, you can incorporate WPF controls into your Windows Forms applications. In this lesson, you learn how to use Windows Forms controls in WPF applications, and vice versa.

Using Windows Forms Controls in WPF Applications

Although WPF provides a wide variety of useful controls and features, you might find that some familiar functionality you used in Windows Forms programming is not available. Notably absent are controls such as MaskedTextBox and PropertyGrid, as well as simple dialog boxes. Fortunately, you can still use many Windows Forms controls in your WPF applications.

Using Dialog Boxes in WPF Applications

Dialog boxes are one of the most notable things missing from the WPF menagerie of controls and elements. Because dialog boxes are separate user interfaces, however, they are relatively easy to incorporate into your WPF applications.

File Dialog Boxes

The file dialog boxes OpenFileDialog and SaveFileDialog are components that you want to use frequently in your applications. They enable you to browse the file system and return the path to the selected file. The OpenFileDialog and SaveFileDialog classes are very similar and share most important members. Table 9-2 shows important properties of the file dialog boxes, and Table 9-3 shows important methods.

Table 9-2 Important Properties of the File Dialog Boxes

Property

Description

AddExtension

Gets or sets a value indicating whether the dialog box automatically adds an extension to a file name if the user omits the extension.

CheckFileExists

Gets or sets a value indicating whether the dialog box displays a warning if the user specifies a file name that does not exist.

CheckPathExists

Gets or sets a value indicating whether the dialog box displays a warning if the user specifies a path that does not exist.

CreatePrompt

Gets or sets a value indicating whether the dialog box prompts the user for permission to create a file if the user specifies a file that does not exist. Available only in SaveFileDialog.

FileName

Gets or sets a string containing the file name selected in the file dialog box.

FileNames

Gets the file names of all selected files in the dialog box. Although this member exists for both the SaveFileDialog and the OpenFileDialog classes, it is relevant only to the OpenFileDialog class because it is only possible to select more than one file in OpenFileDialog.

Filter

Gets or sets the current file name filter string, which determines the choices that appear in the Save As File Type or Files Of Type box in the dialog box.

InitialDirectory

Gets or sets the initial directory displayed by the file dialog box.

Multiselect

Gets or sets a value indicating whether the dialog box allows multiple files to be selected. Available only in OpenFileDialog.

OverwritePrompt

Gets or sets a value indicating whether the Save As dialog box displays a warning if the user specifies a file name that already exists. Available only in SaveFileDialog.

ValidateNames

Gets or sets a value indicating whether the dialog box accepts only valid Win32 file names.

Table 9-3 Important Methods of the File Dialog Boxes

Method

Description

OpenFile

Opens the selected file as a System.IO.Stream object. For OpenFileDialog objects, it opens a read-only stream. For SaveFileDialog objects, it saves a new copy of the indicated file and then opens it as a read-write stream. You need to be careful when using the SaveFileDialog.OpenFile method to keep from overwriting preexisting files of the same name.

ShowDialog

Shows the dialog box modally, thereby halting application execution until the dialog box has been closed. Returns a DialogResult result.

To use a file dialog box in a WPF application:

  1. In Solution Explorer, right-click the project name and choose Add Reference. The Add Reference dialog box opens.

  2. On the .NET tab, select System.Windows.Forms and then click OK.

  3. In code, create a new instance of the desired file dialog box, as shown here:

    Sample of Visual Basic Code

    Dim aDialog As New System.Windows.Forms.OpenFileDialog()

    Sample of C# Code

    System.Windows.Forms.OpenFileDialog aDialog =
       new System.Windows.Forms.OpenFileDialog();
  4. Use the ShowDialog method to show the dialog box modally. After the dialog box is shown, you can retrieve the file name that was selected from the FileNames property. An example is shown here:

    Sample of Visual Basic Code

    Dim aResult As System.Windows.Forms.DialogResult
    aResult = aDialog.ShowDialog()
    If aResult = System.Windows.Forms.DialogResult.OK Then
       ' Shows the path to the selected file
       MessageBox.Show(aDialog.FileName)
    End If

    Sample of C# Code

    System.Windows.Forms.DialogResult aResult;
    aResult = aDialog.ShowDialog();
    if (aResult == System.Windows.Forms.DialogResult.OK)
    {
       // Shows the path to the selected file
       MessageBox.Show(aDialog.FileName);
    }

WindowsFormsHost

Although using dialog boxes in WPF applications is fairly straightforward, using controls is a bit more difficult. Fortunately, WPF provides an element, WindowsFormsHost, specifically designed to ease this task.

WindowsFormsHost is a WPF element capable of hosting a single child element that is a Windows Forms control. The hosted Windows Forms control automatically sizes itself to the size of WindowsFormsHost. You can use WindowsFormsHost to create instances of Windows Forms controls declaratively, and you can set properties on hosted Windows Forms declaratively.

Adding a Windows Forms Control to a WPF Application

To use the WindowsFormsHost element in your WPF applications, first you must add a reference to the System.Windows.Forms.Integration namespace to the XAML view in the WindowsFormsIntegration assembly, as shown here. (This line has been formatted to fit on the printed page, but it should be on a single line in your XAML.)

xmlns:my="clr-namespace:System.Windows.Forms.Integration;
   assembly=WindowsFormsIntegration"

If you drag a WindowsFormsHost element from the Toolbox to the designer, this reference is added automatically. You must also add a reference to the System.Windows.Forms namespace, as shown here:

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

Then you can create an instance of the desired Windows Forms control as a child element of a WindowsFormsHost element, as shown here:

<my:WindowsFormsHost Margin="48,106,30,56" Name="windowsFormsHost1">
   <wf:Button Text="Windows Forms Button" />
</my:WindowsFormsHost>

Setting Properties of Windows Forms Controls in a WPF application

You can set properties on a hosted Windows Forms control declaratively in XAML like you would any WPF element, as shown in bold here:

<my:WindowsFormsHost Margin="48,106,30,56" Name="windowsFormsHost1">
   <wf:Button Text="Windows Forms Button" />
</my:WindowsFormsHost>

Although you can set properties declaratively on a hosted Windows Forms control, some of those properties will not have any meaning. For example, properties dealing with layout, such as Anchor, Dock, Top, and Left, have no effect on the position of the Windows Forms control. This is because its container is WindowsFormsHost, and the Windows Forms control occupies the entire interior of that element. To manage layout for a hosted Windows Forms control, set the layout properties of WindowsFormsHost as shown in bold here:

<my:WindowsFormsHost Margin="48,106,30,56" Name="windowsFormsHost1">
   <wf:Button Text="Windows Forms Button" />
</my:WindowsFormsHost>

Setting Event Handlers on Windows Forms Controls in a WPF Application

Similarly, you can set event handlers declaratively in XAML, as shown in bold in the following example:

<my:WindowsFormsHost Margin="48,106,30,56" Name="windowsFormsHost1">
   <wf:Button Click="Button_Click" Name="Button1" />
</my:WindowsFormsHost>

Note that events raised by Windows Forms controls are regular .NET events, not routed events, and therefore they must be handled at the source.

Obtaining a Reference to a Hosted Windows Forms Control in Code

In most cases, using simple declarative syntax with hosted Windows Forms controls is not sufficient; you have to use code to manipulate hosted Windows Forms controls. Although you can set the Name property of a hosted Windows Forms control, that name does not give you a code reference to the control. Instead, you must obtain a reference by using the WindowsFormsHost.Child property and casting it to the correct type. The following code example demonstrates how to obtain a reference to a hosted Windows Forms Button control:

Sample of Visual Basic Code

Dim aButton As System.Windows.Forms.Button
aButton = CType(windowsFormsHost1.Child, System.Windows.Forms.Button)

Sample of C# Code

System.Windows.Forms.Button aButton;
aButton = (System.Windows.Forms.Button)windowsFormsHost1.Child;

Adding a WPF User Control to Your Windows Form Project

You can add preexisting WPF user controls to your Windows Forms project by using the ElementHost control. As the name implies, the ElementHost control hosts a WPF element.

The most important property of ElementHost is the Child property, which indicates the type of WPF control to be hosted by the ElementHost control. If the WPF control to be hosted is in a project that is a member of the solution, you can set the Child property in the Property grid. Otherwise, the Child property must be set to an instance of the WPF control in code, as shown here:

Sample of Visual Basic Code

Dim aWPFcontrol As New WPFProject.UserControl1
ElementHost1.Child = aWPFcontrol

Sample of C# Code

WPFProject.UserControl1 aWPFcontrol = new WPFProject.UserControl1;
ElementHost1.Child = aWPFcontrol;

Practice: Practice with Windows Forms Elements

In this practice, you practice using Windows Forms elements in a WPF application. You create a simple application that uses MaskedTextBox to collect phone numbers and then write a list of phone numbers to a file that you select using a SaveFileDialogBox.

EXERCISE Using Windows Forms Elements

  1. Create a new WPF application.

  2. From the Toolbox, drag a WindowsFormsHost element onto the design surface and size it to the approximate size of an average text box.

  3. In XAML view, add the following line to the Window tag to import the System.Windows.Forms namespace:

    xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
  4. Modify WindowsFormsHost in XAML so that it encloses a child MaskedTextBox. When finished, your code should look like this:

    <WindowsFormsHost Margin="26,30,125,0" Name="windowsFormsHost1"
       Height="27" VerticalAlignment="Top">
       <wf:MaskedTextBox />
    <WindowsFormsHost>
  5. Set the name of MaskedTextBox to MaskedTextBox1 and set the Mask property to (000)-000-0000, as shown here:

    <wf:MaskedTextBox Name="MaskedTextBox1" Mask="(000)-000-0000" />
  6. In XAML, add the following two buttons as additional children of the Grid:

    <Button Height="23" Margin="21,76,125,0" Name="Button1"
       VerticalAlignment="Top">Add to collection</Button>
    <Button Margin="21,0,125,118" Name="Button2" Height="22"
       VerticalAlignment="Bottom">Save collection to file</Button>
  7. In the code window, add variables that represent a generic List of string objects and a Windows Forms SaveFileDialog element, as shown here:

    Sample of Visual Basic Code

    Dim PhoneNumbers As New List(Of String)
    Dim aDialog As System.Windows.Forms.SaveFileDialog

    Sample of C# Code

    List<string> PhoneNumbers = new List<String>();
    System.Windows.Forms.SaveFileDialog aDialog;
  8. In the designer, double-click the button labeled Add To Collection to open the default Click event handler. Add the following code:

    Sample of Visual Basic Code

    Dim aBox As System.Windows.Forms.MaskedTextBox
    aBox = CType(windowsFormsHost1.Child, System.Windows.Forms.MaskedTextBox)
    PhoneNumbers.Add(aBox.Text)
    aBox.Clear()

    Sample of C# Code

    System.Windows.Forms.MaskedTextBox aBox;
    aBox = (System.Windows.Forms.MaskedTextBox)windowsFormsHost1.Child;
    PhoneNumbers.Add(aBox.Text);
    aBox.Clear();
  9. In the designer, double-click the button labeled Save Collection To File to open the default Click event handler. Add the following code:

    Sample of Visual Basic Code

    aDialog = New System.Windows.Forms.SaveFileDialog
    aDialog.Filter = "Text Files | *.txt"
    aDialog.ShowDialog()
    Dim myWriter As New System.IO.StreamWriter(aDialog.FileName, True)
    For Each s As String In PhoneNumbers
       myWriter.WriteLine(s)
    Next
    myWriter.Close()

    Sample of C# Code

    aDialog = new System.Windows.Forms.SaveFileDialog();
    aDialog.Filter = "Text Files | *.txt";
    aDialog.ShowDialog();
    System.IO.StreamWriter myWriter = new
       System.IO.StreamWriter(aDialog.FileName, true);
    foreach(string s in PhoneNumbers)
       myWriter.WriteLine(s);
    myWriter.Close();
  10. Press F5 to build and run your application. Add a few phone numbers to the collection by filling in MaskedTextBox and pressing the Add To Collection button. Then press the Save Collection To File button to open SaveFileDialogBox, and then select a file and save the list.

Lesson Summary

  • Windows Forms dialog boxes can be used in WPF applications as they are. Dialog boxes can be shown modally by calling the ShowDialog method. Although they can be used without problems in WPF applications, Windows Forms dialog boxes typically use Windows Forms types. Therefore, conversion might be necessary in some cases.

  • WPF provides the WindowsFormsHost element to host Windows Forms controls in the user interface. You can obtain a reference to the hosted Windows Forms control in code by casting the WindowsFormsHost.Child property to the appropriate type.

  • Windows Forms provides the ElementHost control to host a WPF element.

Lesson Review

You can use the following questions to test your knowledge of the information in Lesson 3: Integrating Windows Forms Controls and WPF . The questions are also available on the companion CD if you prefer to review them in electronic form.

  1. Look at the following XAML sample:

    <my:WindowsFormsHost Margin="31,51,118,0" Name="windowsFormsHost1"
       Height="39" VerticalAlignment="Top">
       <wf:MaskedTextBox Name="MaskedTextBox1" />
    </my:WindowsFormsHost>

    Assuming that the namespaces for both of these objects are referenced and imported properly, which of the following code samples set(s) the background of MaskedTextBox to black? (Choose all that apply.)

    1. <my:WindowsFormsHost Background="Black" Margin="31,51,118,0"
         Name="windowsFormsHost1" Height="39" VerticalAlignment="Top">
         <wf:MaskedTextBox Name="MaskedTextBox1" />
      </my:WindowsFormsHost>
    2. <my:WindowsFormsHost Margin="31,51,118,0" Name="windowsFormsHost1"
         Height="39" VerticalAlignment="Top">
         <wf:MaskedTextBox Name="MaskedTextBox1" BackColor="Black" />
      </my:WindowsFormsHost>
    3. Sample of Visual Basic Code

      MaskedTextBox1.BackColor = System.Drawing.Color.Black

      Sample of C# Code

      MaskedTextBox1.BackColor = System.Drawing.Color.Black;
    4. Sample of Visual Basic Code

      Dim aMask as System.Windows.Forms.MaskedTextBox
      aMask = CType(windowsFormsHost1.Child, System.Windows.Forms.MaskedTextBox)
      aMask.Backcolor = System.Drawing.Color.Black

      Sample of C# Code

      System.Windows.Forms.MaskedTextBox aMask;
      aMask = (System.Windows.Forms.MaskedTextBox)windowsFormsHost1.Child;
      aMask.BackColor = System.Drawing.Color.Black;