Hosting WPF Controls in Windows Forms


Enabling you to host WPF controls within your existing Windows Formsbased applications is key to giving you an opportunity to introduce new functionality that requires the capabilities of WPF without forcing you to entirely rewrite your application. This way, even as you work on upgrading an existing application to WPF, you aren’t forced to take on a single large project. As for the integration itself, it isn’t page- or window-based, although you can introduce new WPF windows to an existing application. The integration is focused on enabling you to incorporate new controls into your existing application.

Accordingly, the model is based around the idea that you can encapsulate the functionality of a new WPF control as a user control. This has a couple of key advantages, the first being that if you’ve been working with .NET, you are already familiar with user controls and how they function. Once again, the paradigms of previous user-interface models appear and are reused within WPF. The second big advantage to having this modeled around user controls is that as more of your application moves to WPF, you don’t have to rewrite the user controls you create today when later they are used within a pure WPF environment.

At some point you’ll move the user control outside of the integration ElementHost control that will host it in Windows Forms. However, the underlying control can remain unchanged. To demonstrate integration, it is assumed that you have downloaded and installed the package Visual Studio 2005 Extensions for .NET Framework 3.0 (WCF & WPF), November 2006 CTP, and its prerequisites, as described in Chapter 17. This package updates Visual Studio 2005 and, as illustrated in Figure 17-3 from that chapter, provides three new templates for creating WPF projects. The one you need for Windows Forms Integration is the Windows Control Library.

However, the first step is to open Visual Studio 2005 and go to the New Project dialog. From here, select the Windows category of Templates and create a new Windows Forms application. For example purposes you can name this ProVBWinForm_Interop. As discussed in Chapter 13, Visual Studio uses the template to create a new Windows Forms project. At this point, using the File menu, add a second project to your solution.

This time, select the .NET Framework 3.0 category, and instead of creating a new application create a new Custom Control Library (WPF). For demonstration purposes, use the name InteropControlLibrary. When you are done, the Visual Studio Solution Explorer will look similar to what is shown in Figure 18-1. The next step is to add the customization to the newly created WPF library, after which the Windows Form application will be updated to reference the integration library and the new WPF control.

image from book
Figure 18-1

The first step in customization is to add a splitter to the control’s surface area. You may find that your newly created control has no actual display area. That’s because the control is designed to automatically adapt to display controls placed on it. For this example, edit the XAML for your control and add Height and Width attributes to the user control. You can use a height of 281 pixels and a width of 230 characters. Note that instead of assigning a size to the user control, you could assign a fixed size to the grid with the same results.

Now divide this grid. By selecting the display area for your control, you’ll find that if you move your mouse over the left edge, small horizontal and vertical windows appear around the control. As you move your mouse up and down within this display area, a horizontal line and a positional arrow appear.

On either side of the point representing your mouse are numbers in the small vertical window; these represent the number of pixels in the display above and below your cursor, respectively. By clicking in that window you create a Grid.RowDefinition:

  <Grid.RowDefinitions>    <RowDefinition Height="0.106761565836298*" />    <RowDefinition Height="0.893238434163702*" /> </Grid.RowDefinitions> 

The preceding lines of XAML demonstrate the type of addition to the Grid control definition you should see. Looking at the generated XAML, note that each of the two defined rows represent a percentage of the screen. Thus, as the screen resizes these areas will both increase in height. You can change this default behavior by changing the Height attribute of the row definitions. Instead of using a percentage, you could in fact use an integer value representing a number of pixels; and if only one of the two rows is assigned this Height attribute, then the remaining row automatically expands to fill the remaining display area. The Grid.RowDefinition settings create the boundary between the toolbar and the main display area for the user control shown in Figure 18-1.

The goal is to create a control that has the appearance of a toolbar, with a button and drop-down list box sitting above a text edit control. Once you have the grid row positioned in the upper quarter of the control’s display area, drag a RichTextBox into the bottom portion of the display area. You can give it a set of zero or near-zero margins, and delete any Height and Width properties for this control. This will bind the control to the borders of its display area. For this example, the control’s definition should include the statement Grid.Row="1". Because the grid contains two rows, and because the rows are viewed as a zero-based array of display panes, this places the control in the second pane; without this definition, as you’ll see with the toolbar, items are associated with Grid.Row zero.

The next step is to add a new ToolBar control to the display. Similar to the way in which you just added a RichTextBox control to the lower pane, now add a ToolBar control to the upper window. Again edit the Margin settings, making the ToolBar fill the entire display area. Next, drag a Button control from the toolbox into the display area of the ToolBar.

Unfortunately, the default behavior of the designer doesn’t currently layer this Button within the ToolBar, so edit the XAML again. In this case, remove the closing slash “/” from the ToolBar declaration and then add a separate closing tag for the ToolBar. Place this closing tag on the line below your newly added Button control. Then edit the margin settings for the button so that you bind the display within the toolbar and fill the vertical area of the toolbar. To better manage the size of the Button, add a Width property set to a value such as 75. In addition, change the display value of the text for the button to Edit Note.

Add a ComboBox to the display next to the button. If, when you drag and drop it on the form, it isn’t automatically placed on the toolbar, move the default definition of the ComboBox within the definition of the toolbar. Edit the Margin settings, but in this case the first value isn’t the distance in pixels from the left edge of the display area; instead, it is the number of pixels from the control to the left. Thus, a number such as 5 indicates that the left edge of the ComboBox should be placed 5 pixels from the right edge of the Edit Note button. In order to finish defining the display of the ComboBox, set its remaining margin settings to 0 and assign it a default width of 120 pixels.

The preceding steps define the simple control shown in Figure 18-1; the complete XAML is shown in the following code block. This completes the definition of your sample WPF control library, so compile your application to ensure there aren’t any pending errors.

  <UserControl x:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              Height="281" Width="230">     <Grid><Grid.RowDefinitions>       <RowDefinition Height="0.106761565836298*" />       <RowDefinition Height="0.893238434163702*" />     </Grid.RowDefinitions>     <RichTextBox Margin="1,0,0,0" Name="RichTextBox1" Grid.Row="1" />       <ToolBar Margin="0,0,0,0" Name="ToolBar1" >     <Button HorizontalAlignment="Left" Margin="2,0,0,0" Name="Button1" Width="75">Edit Note</Button>      <ComboBox Margin="5,0,0,0" Width="120"  Name="ComboBox1" />      </ToolBar>   </Grid> </UserControl> 

The next step is to customize the Windows Forms application. Begin by adding the three required references that enable you to embed this control. The first is the Windows.Forms.Integration library, the second is the .NET 3.0 PresentationCore library, and the last is the InteropControlLibrary. As shown earlier, you can access the project properties for your Windows Forms project (in the References tab).

Choose to add references, and in the list of available .NET libraries you’ll find PresentationCore.dll available as a reference. Other presentation libraries are also available from this screen, and you can choose to add these to your project as well.

Using the Browse button, navigate to C:\Program Files\Reference Assemblies\Microsoft\ Framework\v3.0 and add the WindowsFormsIntegration.dll assembly to your project. Finally, switch to the project’s References tab and add a reference to your local project.

Now go to the design view for the Form1.vb file that was created by the Windows Forms template when you created this project. Extend the default size of the design surface with the size of your control in mind. Make sure that the form has enough height to place a command Button below the WPF user control, and drag and drop a button onto your form. This demonstration isn’t going to customize this button or its events, but it does simplify the next step.

Looking through the Toolbox, you will probably find that the ElementHost control associated with the interface library isn’t available. The solution is to add it directly into Forms1.Designer.vb. To access this form, use the Solution Explorer to display all files. Then, looking below Form1.vb, you’ll find the design file. Double-click to open the file and you’ll see the generated partial class definition associated with your form. The next code block contains the bottom half of this file:

     'NOTE: The following procedure is required by the Windows Form Designer     'It can be modified using the Windows Form Designer.     'Do not modify it using the code editor.     <System.Diagnostics.DebuggerStepThrough()> _     Private Sub InitializeComponent()         Me.Button1 = New System.Windows.Forms.Button         Me.ElementHost1 = New System.Windows.Forms.Integration.ElementHost         Me.SuspendLayout()         '         'Button1         '         Me.Button1.Location = New System.Drawing.Point(96, 238)         Me.Button1.Name = "Button1"         Me.Button1.Size = New System.Drawing.Size(75, 23)         Me.Button1.TabIndex = 0         Me.Button1.Text = "Button1"         Me.Button1.UseVisualStyleBackColor = True         '         'ElementHost1         '         Me.ElementHost1.Location = New System.Drawing.Point(0, 0)         Me.ElementHost1.Name = "ElementHost1"         Me.ElementHost1.Size = New System.Drawing.Size(300, 230)         Me.ElementHost1.TabIndex = 1         Me.ElementHost1.BackColor = System.Drawing.SystemColors.Control         '         'Form1         '         Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)         Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font         Me.ClientSize = New System.Drawing.Size(292, 273)         Me.Controls.Add(Me.Button1)         Me.Controls.Add(Me.ElementHost1)         Me.Name = "Form1"         Me.Text = "Form1"         Me.ResumeLayout(False)     End Sub     Friend WithEvents Button1 As System.Windows.Forms.Button     Private WithEvents ElementHost1 As System.Windows.Forms.Integration.ElementHost End Class

At this point, disregard the note included in this file and edit the file to add the ElementHost control to your form. Working from top to bottom in this file, the first step is to add a new instance of the ElementHost control:

 Me.ElementHost1 = New System.Windows.Forms.Integration.ElementHost

This line assigns an instance of an ElementHost control to the variable ElementHost1, which you will define later in this process. The next step is to copy the information associated with the Button that was added in order to define the characteristics of your newly added control. The definition of the button’s properties provides a template, which you can then customize to define the properties for the host control:

 ' 'ElementHost1 ' Me.ElementHost1.Location = New System.Drawing.Point(0, 0) Me.ElementHost1.Name = "ElementHost1" Me.ElementHost1.Size = New System.Drawing.Size(300, 230) Me.ElementHost1.TabIndex = 1 Me.ElementHost1.BackColor = System.Drawing.SystemColors.Control

As shown, the location of the control can be placed in the upper-left corner of the display; the name of the control is assigned to match the variable name; and the size is set large enough to encapsulate your entire WPF control’s display area. The TabIndex is optional and in this case can be set to 1, which leaves only the back color. Note that instead of the property defined for the button, here the property being referenced is BackColor. In the preceding sample code, it is set to match the default background color of the form, but if you want to highlight the bounds of your ElementHost control you can easily set this to a different color. The next step is to add your new control instance to the array of controls contained on the form:

 Me.Controls.Add(Me.ElementHost1)

This adds your instance of the ElementHost control to the control array referenced by the Windows Form. The final step is to add the declaration of the ElementHost1 variable. This is placed along with the declaration of your button control at the bottom of the file. This declaration needs to include the WithEvents modifier, as you want your application to be able to raise and respond to events associated with the contents of the host control:

 Private WithEvents ElementHost1 As System.Windows.Forms.Integration.ElementHost

Note that we haven’t yet added an instance of the actual WPF control that you actually want to embed in your application. The reason for this has to do with the properties that ElementHost currently exposes in Visual Studio 2005. If you save your changes to Form1.Designer.vb and switch to design view for Form1, your display should look similar to the one shown in Figure 18-2. Note the expanded Properties pane. This is currently set to display/edit the properties for ElementHost1.

image from book
Figure 18-2

This list of properties looks similar to the lists available for other Windows Forms controls. You can modify the color settings of the host control and generally customize its layout details. Visual Studio automatically regenerates the Form1.Designer.vb file. The problem is that the Child property of the ElementHost control is not in this list. This is a problem because if you do attempt to place a reference to that property into the designer-generated code, then whenever anyone changes one of these properties in the designer, Visual Studio won’t regenerate that code block because it doesn’t recognize that property.

The result is that with this version of the WPF extensions for Visual Studio, you need to assign the Child property in the Load event of the form. Thus, double-click on the surface of your form and have Visual Studio generate the Form1_Load event handler. To this event handler add the line of code that instantiates an instance of your InteropControlLibrary UserControl1 object as the Child control for your Element Host control:

  Public Class Form1     Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load         Me.ElementHost1.Child = New InteropControlLibrary.UserControl1()     End Sub End Class 

With the preceding code in place, it’s time to build and run your application. When you review the design display, note that your embedded WPF control does not display in design view because it is not loaded. However, after building and running your control you should see a display similar to the one shown in Figure 18-3.

image from book
Figure 18-3

This illustrates how you can create a new WPF component that can be incorporated into an existing Windows Forms application. Therefore, you can start the process of migrating your application to WPF while still focusing the majority of your available resources on adding new capabilities to your existing application. Migration means you are not forced to attempt to spend the majority of your cycles rewriting your existing interface. Instead, you can integrate these two display methodologies. The preceding example demonstrates one way of working with a WPF control within a Windows Forms application.

Other methods for carrying out the same tasks, including adding WPF controls within the context of the same project, are also possible. Defining WPF controls within a Windows Forms project reduces your ability to migrate your control into a larger WPF model, and requires that you edit the project file to instruct MSBuild to include the WPF targets. Editing your MSBuild file is demonstrated in Chapter 17 if you choose to approach WPF integration using this method. However, because at some point you may have migrated all but just a few key complex controls, you may find that instead you want to host these Windows Forms controls within a WPF page or window. Using the method demonstrated in this chapter makes that transition easy, as you’ll just be hosting Windows Forms controls in WPF.




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net