week 10

the first thing that we need to do (after creating a new WPF project) is add a few references. You can do this by by right-clicking on the references folder in the solution explorer, and choosing "Add Reference":

Visual Studio Add Reference Context
Menu

Then you will get a dialog like this:

Visual Studio Add Reference
Dialog

There are two .NET components you will want to add - first, System.Windows.Forms, and then, all the way at the bottom (after you get to this dialog a second time),WindowsFormsIntegration.

Once those two references are added, we have access to all of the WinForms controls, and access to the components that will allow us to embed them in WPF. So now lets get started on some code. First, we are going to take a look at embedding theDataGridView using C#, so this means our XAML is going to be extremely simple:

<Window x:Class="WinFormsInWPF.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Using WinForms In WPF" Height="300" Width="300">
  <Grid x:Name="_Container">
    <Grid.RowDefinitions>
      <RowDefinition Height="*"></RowDefinition>
      <RowDefinition Height="20"></RowDefinition>
    </Grid.RowDefinitions>
    <Button Grid.Row="1" Click="ClearClick" HorizontalAlignment="Right">
      Clear All Rows
    </Button>
  </Grid>
</Window>

As you might notice, there is no reference to the DataGridView anywhere in that XAML. All we have is a grid with two rows, and a button in the second row. This is because we are going to be shoving in the DataGridView into the first row using C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms.Integration;

namespace WinFormsInWPF
{
  public partial class Window1 : Window
  {
    private System.Windows.Forms.DataGridView _MyDataGrid;
    private WindowsFormsHost _MyHost;

    public Window1()
    {
      InitializeComponent();

      _MyHost = new WindowsFormsHost();
      _MyDataGrid = new System.Windows.Forms.DataGridView();

      System.Windows.Forms.DataGridViewColumn col;
      col = new System.Windows.Forms.DataGridViewColumn();
      col.CellTemplate = new System.Windows.Forms.DataGridViewTextBoxCell();
      col.Name = "Col 1";
      _MyDataGrid.Columns.Add(col);
      col = new System.Windows.Forms.DataGridViewColumn();
      col.CellTemplate = new System.Windows.Forms.DataGridViewTextBoxCell();
      col.Name = "Col 2";
      _MyDataGrid.Columns.Add(col);
      _MyDataGrid.Rows.Add(new object[] { "Item 1", "Foo" });
      _MyDataGrid.Rows.Add(new object[] { "Item 2", "Bar" });

      _MyHost.Child = _MyDataGrid;
      _Container.Children.Add(_MyHost);     
    }

    private void ClearClick(object sender, RoutedEventArgs e)
    {
      _MyDataGrid.Rows.Clear();
    }
  }
}

 

here in the constructor (after the InitializeComponent call - we want all our XAML created stuff to be initialized first), we make a new WindowsFormsHost (which is from the namespace System.Windows.Forms.Integration). This object is the glue layer between WPF and WinForms. The WindowsFormsHost is a FrameworkElement, so it can be added to anything in WPF that can take an element. But it has a property Child which takes in asystem.Windows.Forms.Control - and this is the control that will be embedded.

So we create the WindowsFormsHost and the DataGridView, and then populate theDataGridView with some info. You might be wondering why there is no usingstatement for System.Windows.Forms - and instead all the references are explicit. This is because there are some conflicts between the System.Windows.Forms and the standard WPF namespaces - so if you do add a using statement for System.Windows.Forms, it is very likely ambiguous references will start to crop up in your code. So it is just safer and easier to explicitly reference the System.Windows.Forms classes.

After the DataGridView is all set up, we add it as the child for the WindowsFormsHost, and then we add the WindowsFormsHost as a child of our Grid. Interacting with theDataGridView in code works exactly as you might expect - for instance, the ClearClickmethod which clears the current rows in the DataGridView when the WPF button is clicked. The interaction on the user side of things also now pretty much just works, although there can be some quirks with focus and input that you may have to deal with (and will probably be specific to your particular situation). Here is a picture of the sample app in action:

WPF in WinForms
Screenshot

Now onto how do this in XAML, instead of using C#. Here is our new XAML code:

<Window x:Class="WinFormsInWPF.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:WinForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
    Title="Using WinForms In WPF" Height="300" Width="300">
  <Grid x:Name="_Container">
    <Grid.RowDefinitions>
      <RowDefinition Height="*"></RowDefinition>
      <RowDefinition Height="20"></RowDefinition>
    </Grid.RowDefinitions>
    <WindowsFormsHost Grid.Row="0">
      <WinForms:DataGridView x:Name="_MyDataGrid">
      </WinForms:DataGridView>
    </WindowsFormsHost>
    <Button Grid.Row="1" Click="ClearClick" HorizontalAlignment="Right">
      Clear All Rows
    </Button>
  </Grid>
</Window>

As you can see, it got a bit more complicated. First, we added a new xmlns attribute to the Window tag. This pulls in the System.Windows.Forms under the prefix WinForms. The other change is the addition of the WindowsFormsHost component. We add it just like any other WPF FrameworkElement, but inside we get to declare a WinForms control - in this case a DataGridView. We give our DataGridView a name so we can refer to it later, and that's it for the XAML side. Clean, isn't it?

And the C# side gets cleaned up as well:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WinFormsInWPF
{
  public partial class Window1 : Window
  {
    public Window1()
    {
      InitializeComponent();

      System.Windows.Forms.DataGridViewColumn col;
      col = new System.Windows.Forms.DataGridViewColumn();
      col.CellTemplate = new System.Windows.Forms.DataGridViewTextBoxCell();
      col.Name = "Col 1";
      _MyDataGrid.Columns.Add(col);
      col = new System.Windows.Forms.DataGridViewColumn();
      col.CellTemplate = new System.Windows.Forms.DataGridViewTextBoxCell();
      col.Name = "Col 2";
      _MyDataGrid.Columns.Add(col);
      _MyDataGrid.Rows.Add(new object[] { "Item 1", "Foo" });
      _MyDataGrid.Rows.Add(new object[] { "Item 2", "Bar" });
    }

    private void ClearClick(object sender, RoutedEventArgs e)
    {
      _MyDataGrid.Rows.Clear();
    }
  }
}

As you can see, all we have to worry about is the filling of the DataGridView with some data (and it still needs to come after the InitializeComponent call - otherwise nothing will have been created yet). The only downside to using XAML to embed the WinForms control is that you don't have any control over the constructor - the default no argument constructor is always used. Other than that, its nice and keeps the data/display separation paradigm intact.

 

 

To host the MaskedTextBox control

  1. Create a WPF Application project named HostingWfInWpfWithXaml.

  2. Add references to the following assemblies.

    • WindowsFormsIntegration

    • System.Windows.Forms

  3. Open MainWindow.xaml in the WPF Designer.

  4. In the Window element, add the following namespace mapping. The wf namespace mapping establishes a reference to the assembly that contains the Windows Forms control.

     
    xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
    
  5. In the Grid element add the following XAML.

    The MaskedTextBox control is created as a child of the WindowsFormsHost control.

     
    	<Grid>
    
    		<WindowsFormsHost>
    			<wf:MaskedTextBox x:Name="mtbDate" Mask="00/00/0000"/>
    		</WindowsFormsHost>
    
    	</Grid>
    
  6. Press F5 to build and run the application.

posted @ 2015-05-17 10:59  hailuy  阅读(165)  评论(0编辑  收藏  举报