Walkthrough: Arranging Windows Forms Controls in Windows Presentation Foundation
This walkthrough shows you how to use WPF layout features to arrange Windows Forms controls in a hybrid application.
Tasks illustrated in this walkthrough include:
Creating the project.
Using default layout settings.
Sizing to content.
Using absolute positioning.
Specifying size explicitly.
Setting layout properties.
Understanding z-order limitations.
Docking.
Setting visibility.
Hosting a control that does not stretch.
Scaling.
Rotating.
Setting padding and margins.
Using dynamic layout containers.
For a complete code listing of the tasks illustrated in this walkthrough, see Arranging Windows Forms Controls in Windows Presentation Foundation Sample.
When you are finished, you will have an understanding of Windows Forms layout features in WPF-based applications.
Note: |
---|
The dialog boxes and menu commands you see might differ from those described in Help, depending on your active settings or edition. To change your settings, choose Import and Export Settings on the Tools menu. |
Prerequisites
To complete this walkthrough you will need:
- Development Tools for .NET Framework 3.0, which enable you to create a WPF application project. For information on installing these tools, see Installation Instructions for the Windows SDK.
Creating the Project
To create and set up the project
Create a Windows Application (WPF) project named WpfLayoutHostingWf.
In Solution Explorer, add a reference to the WindowsFormsIntegration assembly, which is named WindowsFormsIntegration.dll.
The default location for this file is %programfiles%\Reference Assemblies\Microsoft\Framework\v3.0\WindowsFormsIntegration.dll.
In Solution Explorer, add a reference to the System.Windows.Forms assembly, which is named System.Windows.Forms.dll. Also add a reference to the System.Drawing assembly, which is named System.Drawing.dll.
Double-click Window1.xaml to open it in the Code Editor.
At the start of the file, map the Windows Forms namespace with the following code.
<Window x:Class="WpfLayoutHostingWfWithXaml.Window1" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" Title="Layout Demo for Interoperability" >
<Window x:Class="Window1" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" Title="Layout Demo for Interoperability" >
Set up the default Grid element by creating five rows and three columns.
<Grid ShowGridLines="true"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions>
<Grid ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions>
Using Default Layout Settings
By default, the WindowsFormsHost element handles the layout for the hosted Windows Forms control.
To use default layout settings
Copy the following code into the Grid element.
<!-- Default layout. --> <Canvas Grid.Row="0" Grid.Column="0"> <WindowsFormsHost Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
<!-- Default layout. --> <Canvas Grid.Row="0" Grid.Column="0"> <WindowsFormsHost Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
Press F5 to build and run the application. The Windows Forms System.Windows.Forms.Button control appears in the Canvas. The hosted control is sized based on its content, and the WindowsFormsHost element is sized to accommodate the hosted control.
Sizing to Content
The WindowsFormsHost element ensures that the hosted control is sized to display its content properly.
To size to content
Copy the following code into the Grid element, after the previous code example.
<!-- Sizing to content. --> <Canvas Grid.Row="1" Grid.Column="0"> <WindowsFormsHost Background="Orange"> <wf:Button Text="Windows Forms control with more content" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas> <Canvas Grid.Row="2" Grid.Column="0"> <WindowsFormsHost FontSize="24" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
<!-- Sizing to content. --> <Canvas Grid.Row="1" Grid.Column="0"> <WindowsFormsHost Background="Orange"> <wf:Button Text="Windows Forms control with more content" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas> <Canvas Grid.Row="2" Grid.Column="0"> <WindowsFormsHost FontSize="24" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
Press F5 to build and run the application. The two new button controls are sized to display the longer text string and larger font size properly, and the WindowsFormsHost elements are resized to accommodate the hosted controls.
Using Absolute Positioning
You can use absolute positioning to place the WindowsFormsHost element anywhere in the user interface (UI).
To use absolute positioning
Copy the following code into the Grid element, after the previous code example.
<!-- Absolute positioning. --> <Canvas Grid.Row="3" Grid.Column="0"> <WindowsFormsHost Canvas.Top="20" Canvas.Left="20" Background="Yellow"> <wf:Button Text="Windows Forms control with absolute positioning" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
<!-- Absolute positioning. --> <Canvas Grid.Row="3" Grid.Column="0"> <WindowsFormsHost Canvas.Top="20" Canvas.Left="20" Background="Yellow"> <wf:Button Text="Windows Forms control with absolute positioning" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
Press F5 to build and run the application. The WindowsFormsHost element is placed 20 pixels from the top side of the grid cell and 20 pixels from the left.
Specifying Size Explicitly
You can specify the size of the WindowsFormsHost element using the Width and Height properties.
To specify size explicitly
Copy the following code into the Grid element, after the previous code example.
<!-- Explicit sizing. --> <Canvas Grid.Row="4" Grid.Column="0"> <WindowsFormsHost Width="50" Height="70" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
<!-- Explicit sizing. --> <Canvas Grid.Row="4" Grid.Column="0"> <WindowsFormsHost Width="50" Height="70" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
Press F5 to build and run the application. The WindowsFormsHost element is set to a size of 50 pixels wide by 70 pixels high, which is smaller than the default layout settings. The content of the Windows Forms control is rearranged accordingly.
Setting Layout Properties
Always set layout-related properties on the hosted control by using the properties of the WindowsFormsHost element. Setting layout properties directly on the hosted control will yield unintended results.
Setting layout-related properties on the hosted control in XAML has no effect.
To see the effects of setting properties on the hosted control
Copy the following code into the Grid element, after the previous code example.
<!-- Setting hosted control properties directly. --> <Canvas Grid.Row="0" Grid.Column="1"> <WindowsFormsHost Width="160" Height="50" Background="Yellow"> <wf:Button Name="button1" Click="button1Click" Text="Click me" FlatStyle="Flat" BackColor="Green"/> </WindowsFormsHost> </Canvas>
<!-- Setting hosted control properties directly. --> <Canvas Grid.Row="0" Grid.Column="1"> <WindowsFormsHost Width="160" Height="50" Background="Yellow"> <wf:Button Name="button1" Click="button1Click" Text="Click me" FlatStyle="Flat" BackColor="Green"/> </WindowsFormsHost> </Canvas>
In Solution Explorer, double-click Window1.xaml.cs to open it in the Code Editor.
Copy the following code into the
Window1
class definition, after theWindow1()
constructor.private void button1Click(object sender, EventArgs e ) { System.Windows.Forms.Button b = sender as System.Windows.Forms.Button; b.Top = 20; b.Left = 20; }
Private Sub button1Click(ByVal sender As Object, ByVal e As EventArgs) Dim b As System.Windows.Forms.Button = sender b.Top = 20 b.Left = 20 End Sub
Press F5 to build and run the application.
Click the Click me button. The
button1Click
event handler sets the Top and Left properties on the hosted control. This causes the hosted control to be repositioned within the WindowsFormsHost element. The host maintains the same screen area, but the hosted control is clipped. Instead, the hosted control should always fill the WindowsFormsHost element.
Understanding Z-Order Limitations
Visible WindowsFormsHost elements are always drawn on top of other WPF elements, and they are unaffected by z-order.
To see z-order limitations
In the Window1.xaml file, copy the following code into the Grid element, after the previous code example.
<!-- Z-order demonstration. --> <Canvas Grid.Row="1" Grid.Column="1"> <Label Content="A WPF label" FontSize="24"/> <WindowsFormsHost Canvas.Top="20" Canvas.Left="20" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
<!-- Z-order demonstration. --> <Canvas Grid.Row="1" Grid.Column="1"> <Label Content="A WPF label" FontSize="24"/> <WindowsFormsHost Canvas.Top="20" Canvas.Left="20" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
Press F5 to build and run the application. The WindowsFormsHost element is painted over the label element.
Docking
WindowsFormsHost element supports WPF docking. Set the Dock attached property to dock the hosted control in a DockPanel element.
To dock a hosted control
Copy the following code into the Grid element, after the previous code example.
<!-- Docking a WindowsFormsHost element. --> <DockPanel LastChildFill="false" Grid.Row="2" Grid.Column="1"> <WindowsFormsHost DockPanel.Dock="Right" Canvas.Top="20" Canvas.Left="20" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </DockPanel>
<!-- Docking a WindowsFormsHost element. --> <DockPanel LastChildFill="false" Grid.Row="2" Grid.Column="1"> <WindowsFormsHost DockPanel.Dock="Right" Canvas.Top="20" Canvas.Left="20" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> </DockPanel>
Press F5 to build and run the application. The WindowsFormsHost element is docked to the right side of the DockPanel element.
Setting Visibility
You can make your Windows Forms control invisible or collapse it by setting the Visibility property on the WindowsFormsHost element. When a control is invisible, it is not displayed, but it occupies layout space. When a control is collapsed, it is not displayed, nor does it occupy layout space.
To set the visibility of a hosted control
Copy the following code into the Grid element, after the previous code example.
<!-- Setting Visibility to hidden and collapsed. --> <StackPanel Grid.Row="3" Grid.Column="1"> <Button Name="button2" Click="button2Click" Content="Click to make invisible" Background="OrangeRed"/> <WindowsFormsHost Name="host1" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> <Button Name="button3" Click="button3Click" Content="Click to collapse" Background="OrangeRed"/> </StackPanel>
<!-- Setting Visibility to hidden and collapsed. --> <StackPanel Grid.Row="3" Grid.Column="1"> <Button Name="button2" Click="button2Click" Content="Click to make invisible" Background="OrangeRed"/> <WindowsFormsHost Name="host1" Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> <Button Name="button3" Click="button3Click" Content="Click to collapse" Background="OrangeRed"/> </StackPanel>
In Solution Explorer, double-click Window1.xaml.cs to open it in the Code Editor.
Copy the following code into the
Window1
class definition.private void button2Click(object sender, EventArgs e) { this.host1.Visibility = Visibility.Hidden; } private void button3Click(object sender, EventArgs e) { this.host1.Visibility = Visibility.Collapsed; }
Private Sub button2Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Me.host1.Visibility = Windows.Visibility.Hidden End Sub Private Sub button3Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Me.host1.Visibility = Windows.Visibility.Collapsed End Sub
Press F5 to build and run the application.
Click the Click to make invisible button to make the WindowsFormsHost element invisible.
Click the Click to collapse button to hide the WindowsFormsHost element from the layout entirely. When the Windows Forms control is collapsed, the surrounding elements are rearranged to occupy its space.
Hosting a Control That Does Not Stretch
Some Windows Forms controls have a fixed size and do not stretch to fill available space in the layout. For example, the MonthCalendar control displays a month in a fixed space.
To host a control that does not stretch
Copy the following code into the Grid element, after the previous code example.
<!-- Hosting a control that does not stretch. --> <!-- The MonthCalendar has a discrete size. --> <StackPanel Grid.Row="4" Grid.Column="1"> <Label Content="A WPF element" Background="OrangeRed"/> <WindowsFormsHost Background="Yellow"> <wf:MonthCalendar/> </WindowsFormsHost> <Label Content="Another WPF element" Background="OrangeRed"/> </StackPanel>
<!-- Hosting a control that does not stretch. --> <!-- The MonthCalendar has a discrete size. --> <StackPanel Grid.Row="4" Grid.Column="1"> <Label Content="A WPF element" Background="OrangeRed"/> <WindowsFormsHost Background="Yellow"> <wf:MonthCalendar/> </WindowsFormsHost> <Label Content="Another WPF element" Background="OrangeRed"/> </StackPanel>
Press F5 to build and run the application. The WindowsFormsHost element is centered in the grid row, but it is not stretched to fill the available space. If the window is large enough, you may see two or more months displayed by the hosted MonthCalendar control, but these are centered in the row. The WPF layout engine centers elements that cannot be sized to fill the available space.
Scaling
Unlike WPF elements, most Windows Forms controls are not continuously scalable. The WindowsFormsHost element scales its hosted control when possible.
To scale a hosted control
Copy the following code into the Grid element, after the previous code example.
<!-- Scaling transformation. --> <StackPanel Grid.Row="0" Grid.Column="2"> <StackPanel.RenderTransform> <ScaleTransform CenterX="0" CenterY="0" ScaleX="0.5" ScaleY="0.5" /> </StackPanel.RenderTransform> <Label Content="A WPF UIElement" Background="OrangeRed"/> <WindowsFormsHost Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> <Label Content="Another WPF UIElement" Background="OrangeRed"/> </StackPanel>
<!-- Scaling transformation. --> <StackPanel Grid.Row="0" Grid.Column="2"> <StackPanel.RenderTransform> <ScaleTransform CenterX="0" CenterY="0" ScaleX="0.5" ScaleY="0.5" /> </StackPanel.RenderTransform> <Label Content="A WPF UIElement" Background="OrangeRed"/> <WindowsFormsHost Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> <Label Content="Another WPF UIElement" Background="OrangeRed"/> </StackPanel>
Press F5 to build and run the application. The hosted control and its surrounding elements are scaled by a factor of 0.5. However, the hosted control's font is not scaled.
Rotating
Unlike WPF elements, Windows Forms controls do not support rotation. The WindowsFormsHost element does not rotate with other WPF elements when a rotation transformation is applied. Any rotation value other than 180 degrees raises the LayoutError event.
To see the effect of rotation in a hybrid application
Copy the following code into the Grid element, after the previous code example.
<!-- Rotation transformation. --> <StackPanel Grid.Row="1" Grid.Column="2"> <StackPanel.RenderTransform> <RotateTransform CenterX="200" CenterY="50" Angle="180" /> </StackPanel.RenderTransform> <Label Content="A WPF element" Background="OrangeRed"/> <WindowsFormsHost Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> <Label Content="Another WPF element" Background="OrangeRed"/> </StackPanel>
<!-- Rotation transformation. --> <StackPanel Grid.Row="1" Grid.Column="2"> <StackPanel.RenderTransform> <RotateTransform CenterX="200" CenterY="50" Angle="180" /> </StackPanel.RenderTransform> <Label Content="A WPF element" Background="OrangeRed"/> <WindowsFormsHost Background="Yellow"> <wf:Button Text="Windows Forms control" FlatStyle="Flat"/> </WindowsFormsHost> <Label Content="Another WPF element" Background="OrangeRed"/> </StackPanel>
Press F5 to build and run the application. The hosted control is not rotated, but its surrounding elements are rotated by an angle of 180 degrees.
Setting Padding and Margins
Padding and margins in WPF layout are similar to padding and margins in Windows Forms. Simply set the Padding and Margin properties on the WindowsFormsHost element.
To set padding and margins for a hosted control
Copy the following code into the Grid element, after the previous code example.
<!-- Padding. --> <Canvas Grid.Row="2" Grid.Column="2"> <WindowsFormsHost Padding="0, 20, 0, 0" Background="Yellow"> <wf:Button Text="Windows Forms control with padding" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas> ... <!-- Padding. --> <!--<Canvas Grid.Row="2" Grid.Column="2"> <WindowsFormsHost Padding="0, 20, 0, 0" Background="Yellow"> <wf:Button Text="Windows Forms control with padding" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>--> ... <!-- Margin. --> <Canvas Grid.Row="3" Grid.Column="2"> <WindowsFormsHost Margin="20, 20, 0, 0" Background="Yellow"> <wf:Button Text="Windows Forms control with margin" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas> ... <!-- Margin. --> <Canvas Grid.Row="3" Grid.Column="2"> <WindowsFormsHost Margin="20, 20, 0, 0" Background="Yellow"> <wf:Button Text="Windows Forms control with margin" FlatStyle="Flat"/> </WindowsFormsHost> </Canvas>
Press F5 to build and run the application. The padding and margin settings are applied to the hosted Windows Forms controls in the same way they would be applied in Windows Forms.
Using Dynamic Layout Containers
Windows Forms provides two dynamic layout containers, FlowLayoutPanel and TableLayoutPanel. You can also use these containers in WPF layouts.
To use a dynamic layout container
Copy the following code into the Grid element, after the previous code example.
<!-- Flow layout. --> <DockPanel Grid.Row="4" Grid.Column="2"> <WindowsFormsHost Name="flowLayoutHost" Background="Yellow"> <wf:FlowLayoutPanel/> </WindowsFormsHost> </DockPanel>
<!-- Flow layout. --> <DockPanel Grid.Row="4" Grid.Column="2"> <WindowsFormsHost Name="flowLayoutHost" Background="Yellow"> <wf:FlowLayoutPanel/> </WindowsFormsHost> </DockPanel>
In Solution Explorer, double-click Window1.xaml.cs to open it in the Code Editor.
Copy the following code into the
Window1
class definition.private void InitializeFlowLayoutPanel() { System.Windows.Forms.FlowLayoutPanel flp = this.flowLayoutHost.Child as System.Windows.Forms.FlowLayoutPanel; flp.WrapContents = true; const int numButtons = 6; for (int i = 0; i < numButtons; i++) { System.Windows.Forms.Button b = new System.Windows.Forms.Button(); b.Text = "Button"; b.BackColor = System.Drawing.Color.AliceBlue; b.FlatStyle = System.Windows.Forms.FlatStyle.Flat; flp.Controls.Add(b); } }
Private Sub InitializeFlowLayoutPanel() Dim flp As System.Windows.Forms.FlowLayoutPanel = Me.flowLayoutHost.Child flp.WrapContents = True Const numButtons As Integer = 6 Dim i As Integer For i = 0 To numButtons Dim b As New System.Windows.Forms.Button() b.Text = "Button" b.BackColor = System.Drawing.Color.AliceBlue b.FlatStyle = System.Windows.Forms.FlatStyle.Flat flp.Controls.Add(b) Next i End Sub
Add a call to the
InitializeFlowLayoutPanel
method in the constructor.public Window1() { InitializeComponent(); this.InitializeFlowLayoutPanel(); }
Public Sub New() InitializeComponent() Me.InitializeFlowLayoutPanel() End Sub
Press F5 to build and run the application. The WindowsFormsHost element fills the DockPanel, and FlowLayoutPanel arranges its child controls in the default FlowDirection.
See Also
Reference
Concepts
Layout Considerations for the WindowsFormsHost Element
Walkthrough: Hosting a Windows Forms Composite Control in Windows Presentation Foundation
Walkthrough: Hosting a Windows Presentation Foundation Control in Windows Forms
Other Resources
Arranging Windows Forms Controls in Windows Presentation Foundation Sample
Migration and Interoperability How-to Topics