XAML Limitations and Windows Forms Controls

(Blogging Tunes: Live - "Throwing Copper")

This is a follow up post to yesterday's post about XAML.  I alluded to the fact that there are some issues related to how XAML was defined that creates some limiations in its use to describe Windows Forms controls in hybrid applications.  So I figured I better elaborate on this subject just a bit.  And remember, I don't have a love affair with XAML, nor do I think it is necessarily the best mechanism to use for working with Windows Forms controls in WPF applications, I just think I owe it to you to provide the information.  You can do with it what you want.

If you remember, we discussed that XAML was actually a general purpose mark-up language that could describe basically any CLR type.  This is what makes it possible to describe Windows Forms controls using XAML.  There was not a conscious effort to provide some kind of extensions to XAML so that it would work with Windows Forms controls or any other CLR types for that matter...that's why it was defined to be fairly generic in that sense.  So given the fact that we can describe Windows Forms controls using XAML, what things do we need to watch out for?

Let's start by examining some of the assumptions made in the design of XAML that can lead to issues in Windows Forms.  The way the XAML parser works, is that tag names that it encounters map directly to CLR types.  So when it sees a tag name such as <Button> it expects there to be a class called 'Button' in the appropriate namespace.  (Remember that we generally use mapping statements in our XAML to reference classes that are outside of the standard WPF namespaces.  That's how we can distinguish between a System.Windows.Controls.Button (WPF) and a System.Windows.Forms.Button (Windows Forms).  Then the parser expects to create an instance of that class that it just encountered and it adds it to the visual tree.  The first limiation to point out here is:

ISSUE: XAML requires classes to have default constructors

Not all classes that you may need to work with in XAML have default constructors and thus, you will have to use the code behind approach to instantiate and work with these classes rather than using XAML.  Take something like the System.Drawing.Font class.  It does not have a default constructor, all 13 overloads of the Font constructor require parameters and therefore you cannot use XAML to instantiate a System.Drawing.Font class.  Another issue relates to the use of reference types vs. value types for properties:

ISSUE: XAML requires properties to be of value types, not reference types

So take for example the 'AcceptButton' property on the System.Windows.Forms.Form class.  This property expects a reference to another object, in this case a button object.  This is not possible using XAML.  The last major issue is related to sub properties and nested classes.

ISSUE: The architecture of some classes make them difficult to work with using XAML

For a good example of this, let's take a look at the System.Windows.Forms.SplitContainer control.  This control consists of two Panel objects (called Panel1 and Panel2) which are created in the SplitContainer's constructor.  These panel objects do not have public accessors.  The panels are where you actually place your controls, so if you wanted to use XAML, you would attempt to do something like:

<?

Mapping XmlNamespace="wfi" ClrNamespace="System.Windows.Forms.Integration" Assembly="WindowsFormsIntegration"?>
<?Mapping XmlNamespace="wf" ClrNamespace="System.Windows.Forms" Assembly="System.Windows.Forms"?>
<Window x:Class="AvalonApplication24.Window1"
     xmlns=https://schemas.microsoft.com/winfx/avalon/2005
     xmlns:x=https://schemas.microsoft.com/winfx/xaml/2005
     xmlns:wfi="wfi"
     xmlns:wf="wf"
     Title="AvalonApplication24"
     >

<

Grid>
<wfi:WindowsFormsHost>
<wf:SplitContainer>
<wf:SplitContainer.Panel1> <-- Error here
<wf:Button Text="WF Button"/>
</wf:SplitContainer.Panel1>
</wf:SplitContainer>
</wfi:WindowsFormsHost>
</Grid>
</Window>

The problem with this, is that this will cause a compiler error becuase the Panel1 property of the SplitContainer is read-only becuase it does not have a public accessor.  So you are simply not able to add controls to the Panels of the SplitContainer which makes it fairly useless in a markup scenario.  You simply have to do this using code.  Now you can add the SplitContainer itself using markup and then just to the rest in code behind.  So you can do something like:

<?Mapping XmlNamespace="wfi" ClrNamespace="System.Windows.Forms.Integration" Assembly="WindowsFormsIntegration"?>
<?Mapping XmlNamespace="wf" ClrNamespace="System.Windows.Forms" Assembly="System.Windows.Forms"?>
<Window x:Class="AvalonApplication24.Window1"
     xmlns=https://schemas.microsoft.com/winfx/avalon/2005
     xmlns:x=https://schemas.microsoft.com/winfx/xaml/2005
     xmlns:wfi="wfi"
     xmlns:wf="wf"
     Title="AvalonApplication24"
     Loaded="WindowLoaded"
>

     <Grid>
<wfi:WindowsFormsHost>
<wf:SplitContainer x:Name="splitContainer1">
               </wf:SplitContainer>
</wfi:WindowsFormsHost>
</Grid>
</Window>

And then in the code behind do something like:

private void WindowLoaded(object sender, RoutedEventArgs e)
{
System.Windows.Forms.Button wfButton = new System.Windows.Forms.Button();
wfButton.Text = "WF Button";
     this.splitContainer1.Panel1.Controls.Add(wfButton);
}

So this was meant to just show you some of the major limitations you may encounter when trying to use XAML to describe Windows Forms controls in hybrid applications.  Remember, code is the best way to work with Windows Forms controls, but many people ask about using XAML so I want to offer that as a solution given the above caveats. 

Happy Friday!  And yeah, I did get really wet riding home yesterday in the rain, thanks for asking...