(Print this page)

Hosting WPF controls on a Windows Form
Published date: Monday, May 26, 2014
On: Moer and Éric Moreau's web site

Whatever you hear, Windows Forms are not dead. There are so many floating around that this technology will just never die (much like Cobol!).

But Microsoft has slowed down the development on this mature technology. That doesn’t mean that you are limited forever to the controls in your toolbox. Many companies are still building great controls for Windows Forms.

But sometime you find somebody who built a great control in WPF that you would like use in your Windows Forms based application. All is not lost. You can do it and here is how.

The downloadable code

The code is available in both VB and C#.

What you need?

This month demo code has been built using Visual Studio 2013 but can surely be used with older version (probably starting with VS2010 – but not tested).

For the demo, I found a nice free open source charting component called OxyPlot. If you look at the component web site, you will discover that I could have used the Windows Form version of the control but don’t forget that the purpose of this article is to use a WPF control on a Windows Form.

The VS solution

The solution explained here will be composed of 2 projects: a WPF control library and a Windows Forms test application.

The downloadable solution contains 4 projects (2 VB and 2 CS).

The first thing I have done was to create a Windows Forms application. We won’t do anything with the form yet since we need our WPF control before going on with it.

Creating the WPF control

So I have added a WPF User Control Library project to my solution as shown in figure 1.

Figure 1: adding a WPF User Control Library project

The next thing I have was to a reference to the control I wanted to use. This component is offering a NuGet installation so it is easy for us to integrate it into our application. Right-click the WPF project and select “Manage NuGet packages…”. In the dialog, search (top-right textbox) for “oxyplot” and select “OxyPlot.Wpf” from the list as shown in figure 2 and click the Install button.

Figure 2: Adding a reference to the NuGet package

Modify the XAML code to that it looks like figure 3. There should be only 2 changes. The first one is to declare the oxy namespace (xmlns:oxy) and the second one is the control declaration into the grid.

Figure 3: XAML code of the control

Then we need to write some code into the .xaml.vb file to create the MyModel object on which the control is bound in the XAML file:

Option Strict On

Imports OxyPlot

Public Class UserControl1

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        DataContext = Me
    End Sub

    Private _model As PlotModel
    Public Property MyModel() As PlotModel
        Get
            Return _model
        End Get
        Set(value As PlotModel)
            _model = value
        End Set
    End Property


End Class

There are 2 parts to create. The First one is the MyModel property. I declare it with a backing field because I will revisit this property later in this article. The second part is to add “DataContext = Me” as the last line of the constructor.

So far we haven’t done much but it should be enough to create a reusable WPF control usable from a WindowForms application.

Back to the Windows Forms application

The next thing I have done one was to add a reference to the WPF library we just created as shown in figure 4. The library is available under the Solution tab.

Figure 4: add a reference to the WPF library

If you have not done it yet, you will now need to build your solution so that the control just created shows up in the toolbox as shown in figure 5.

Figure 5: The user control appears in the toolbox (after a build)

At this point, you might be tempted to drag an instance of your new control directly to the form. Depending on the control you built, chances are that it might work. In my case, it won’t just work. Instead you fill get an exception like the one shown in figure 6.

Figure 6: Exception when dropping a control instance

When we created the WPF control, we have added a reference to the OxyPlot NuGet package. Wherever you will drag this control, this package will also need to be available. So go ahead and add a reference to the very same OxyPlot.Wpf NuGet package as we did earlier (check figure 2).

You should now safely be able to drag an instance of the control to your Windows Form.

We now have everything except the data we want to display. I have kept this data to be provided by the main application because this one is probably having the data and want to send it to the WPF control.

OxyPlot offers a great online documentation with a bunch of example (plot and code - http://www.oxyplot.org/ExampleBrowser/). I randomly took 2 examples from that site to include them into the sample.

The first example draws 2 line with different colors:

Private Function Example1() As PlotModel
    Dim plotModel1 As New PlotModel()
    plotModel1.LegendSymbolLength = 24
    plotModel1.Title = "TwoColorLineSeries"
    Dim linearAxis1 As New LinearAxis()
    linearAxis1.ExtraGridlines = New Double() {0, 0}
    linearAxis1.ExtraGridlines(0) = 0
    plotModel1.Axes.Add(linearAxis1)
    Dim linearAxis2 As New LinearAxis()
    linearAxis2.Position = AxisPosition.Bottom
    plotModel1.Axes.Add(linearAxis2)
    Dim twoColorLineSeries1 As New TwoColorLineSeries()
    twoColorLineSeries1.Color2 = OxyColors.LightBlue
    twoColorLineSeries1.Color = OxyColors.Red
    twoColorLineSeries1.MarkerSize = 4
    twoColorLineSeries1.MarkerStroke = OxyColors.Black
    twoColorLineSeries1.MarkerStrokeThickness = 1.5
    twoColorLineSeries1.MarkerType = MarkerType.Circle
    twoColorLineSeries1.StrokeThickness = 3
    twoColorLineSeries1.Smooth = True
    twoColorLineSeries1.Title = "Temperature at Eidesmoen, December 1986."
    twoColorLineSeries1.Points.Add(New DataPoint(1, 5))
    twoColorLineSeries1.Points.Add(New DataPoint(2, 0))
    twoColorLineSeries1.Points.Add(New DataPoint(3, 7))
    twoColorLineSeries1.Points.Add(New DataPoint(4, 7))
    twoColorLineSeries1.Points.Add(New DataPoint(5, 4))
    twoColorLineSeries1.Points.Add(New DataPoint(6, 3))
    twoColorLineSeries1.Points.Add(New DataPoint(7, 5))
    twoColorLineSeries1.Points.Add(New DataPoint(8, 5))
    twoColorLineSeries1.Points.Add(New DataPoint(9, 11))
    twoColorLineSeries1.Points.Add(New DataPoint(10, 4))
    twoColorLineSeries1.Points.Add(New DataPoint(11, 2))
    twoColorLineSeries1.Points.Add(New DataPoint(12, 3))
    twoColorLineSeries1.Points.Add(New DataPoint(13, 2))
    twoColorLineSeries1.Points.Add(New DataPoint(14, 1))
    twoColorLineSeries1.Points.Add(New DataPoint(15, 0))
    twoColorLineSeries1.Points.Add(New DataPoint(16, 2))
    twoColorLineSeries1.Points.Add(New DataPoint(17, -1))
    twoColorLineSeries1.Points.Add(New DataPoint(18, 0))
    twoColorLineSeries1.Points.Add(New DataPoint(19, 0))
    twoColorLineSeries1.Points.Add(New DataPoint(20, -3))
    twoColorLineSeries1.Points.Add(New DataPoint(21, -6))
    twoColorLineSeries1.Points.Add(New DataPoint(22, -13))
    twoColorLineSeries1.Points.Add(New DataPoint(23, -10))
    twoColorLineSeries1.Points.Add(New DataPoint(24, -10))
    twoColorLineSeries1.Points.Add(New DataPoint(25, 0))
    twoColorLineSeries1.Points.Add(New DataPoint(26, -4))
    twoColorLineSeries1.Points.Add(New DataPoint(27, -5))
    twoColorLineSeries1.Points.Add(New DataPoint(28, -4))
    twoColorLineSeries1.Points.Add(New DataPoint(29, 3))
    twoColorLineSeries1.Points.Add(New DataPoint(30, 0))
    twoColorLineSeries1.Points.Add(New DataPoint(31, -5))
    plotModel1.Series.Add(twoColorLineSeries1)
    Return plotModel1
End Function

This method can now be called from the form’s constructor:

Sub New()
    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    UserControl11.MyModel = Example1()
End Sub

If you now hit F5, you should see something like figure 7:

Figure 7: a first example running in our application

Now the next step is to change the content of chart while the application is running.

I thought it would have been as easy as adding 2 buttons to my form and using this code:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    UserControl11.MyModel = Example1()
End Sub

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    UserControl11.MyModel = Example2()
End Sub

But it is not that easy. The code compiles correctly but nothing happens at run time when you click your buttons. The reason why it isn’t working is related to the DataBinding of WPF. We need to add a couple of lines to notify the control when the bound properties is modified.

We first need to revisit the WPF control to add implement the INotifyPropertyChanged interface. The new code now looks like this:

Option Strict On
 
Imports System.ComponentModel
Imports OxyPlot
 
Public Class UserControl1
    Implements INotifyPropertyChanged
 
    Public Sub New()
 
        ' This call is required by the designer.
        InitializeComponent()
 
        ' Add any initialization after the InitializeComponent() call.
        DataContext = Me
    End Sub
 
    Private _model As PlotModel
    Public Property MyModel() As PlotModel
        Get
            Return _model
        End Get
        Set(value As PlotModel)
            _model = value
            OnPropertyChanged("MyModel")
        End Set
    End Property
 
 
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
 
    Protected Overloads Sub OnPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
 
End Class

Now you can run the application again and test your 2 buttons. The application will now run as expected.

Conclusion

This article shows you how to create a WPF control that can be used from a Windows Forms application. Using this simple technic, you can now offer WPF controls hosted in your Windows Forms and even interact with them.




(Print this page)