The blog of dlaa.me

Sharing isn't easy for anyone [Tricks for sharing the same XAML files across Silverlight and WPF]

I casually mentioned two tricks for sharing the same XAML across Silverlight and WPF in the notes of a post a couple weeks ago. I used both techniques in my DataVisualizationDemos project (which compiles for Silverlight and WPF from the same source code) and wanted to call them out for others in similar situations. The feedback I got was quite positive - until Brian Elgaard Bennett tried the subclassing trick with HierarchicalDataTemplate. While there's no reason that shouldn't work, it doesn't.

What's going on is that WPF doesn't support subclassing DataTemplate or any of its children. What's worse, nobody expects this when they bump into it! [I know a few of us have independently reported the same issue to the WPF team - it's kind of like a rite of passage. :) ] So the right people are aware of the problem, and maybe - just maybe - the new XAML parser in .NET 4 will help fix things. But that doesn't matter for WPF 3.5 development today - and because my XAML sharing trick relies on subclassing, the situation seems pretty dire.

 

But I'm stubborn, so I thought about the situation a little and foolishly said I had an idea that should work. Unfortunately - like most of my ideas - it didn't work the first time. Or the second time. Or the third. Or...

I think it was something like the ninety-ninth idea that finally worked.

"Genius is one percent inspiration, ninety-nine percent perspiration." - Thomas Edison

Well, I won't claim this is genius or anything, but the ratio sure seems right. :)

 

[Click here to download the complete source code for the SharingXamlSilverlightWpf sample.]

 

The way I've solved the HierarchicalDataTemplate problem is a lot like the way I went about things before. However, I've implemented it a little differently this time around, so if you want a quick-and-dirty solution and don't care about HierarchicalDataTemplate, then do it the way I did in my previous post. But if you want a more complete, more comprehensive solution, please keep reading...

 

First, here's the XAML we'll be sharing. There's nothing fancy, just some standard controls that are part of the core framework for WPF, and part of the SDK/Toolkit for Silverlight.

<UserControl x:Class="SharingXamlSilverlightWpf_SL.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:systemWindows="clr-namespace:System.Windows;assembly=PresentationFramework"
    xmlns:systemWindowsControls="clr-namespace:System.Windows.Controls;assembly=PresentationFramework">
    <StackPanel>
        <systemWindowsControls:DockPanel>
            <TextBlock Text="Inside a DockPanel"/>
        </systemWindowsControls:DockPanel>

        <systemWindowsControls:Viewbox Height="40">
            <TextBlock Text="Inside a ViewBox"/>
        </systemWindowsControls:Viewbox>

        <systemWindowsControls:TreeView>
            <systemWindowsControls:TreeViewItem
                Header="Inside a TreeView(Item)"
                ItemsSource="{Binding}"
                IsExpanded="True">
                <systemWindowsControls:TreeViewItem.ItemTemplate>
                    <systemWindows:HierarchicalDataTemplate>
                        <TextBlock Text="{Binding}"/>
                    </systemWindows:HierarchicalDataTemplate>
                </systemWindowsControls:TreeViewItem.ItemTemplate>
            </systemWindowsControls:TreeViewItem>
        </systemWindowsControls:TreeView>
    </StackPanel>
</UserControl>
Aside: In the ideal world, we wouldn't need to use an XML namespace prefix under Silverlight and therefore this problem would never come up in the first place. Unfortunately, Silverlight 3 and the Silverlight 4 Beta don't support XmlnsDefinitionAttribute, so the fact that the SDK/Toolkit assemblies already properly implement it doesn't help us.

The interesting thing to note above is that all the relevant controls (DockPanel, Viewbox, TreeView, TreeViewItem, and HierarchicalDataTemplate) have a prefix and that things look just like you'd expect them to under WPF if you went to the trouble of specifying the namespace explicitly. And, in fact, that's all there is for WPF - just be explicit with the XAML and you're done!

So what's the magic that makes this work on Silverlight? Well, it's nothing in the application project there, either - just like with WPF there are no special changes required! However, there is an extra assembly on the Silverlight side...

Before I totally spoil the surprise, here's the code for that assembly:

extern alias SWC;
extern alias SWCT;
using System.Windows.Markup;
using SystemWindows = SWC::System.Windows;
using SystemWindowsControls = SWC::System.Windows.Controls;
using SystemWindowsControlsToolkit = SWCT::System.Windows.Controls;

namespace System.Windows
{
    // Stub class for HierarchicalDataTemplate
    public class HierarchicalDataTemplate : SystemWindows.HierarchicalDataTemplate
    {
    }
}

namespace System.Windows.Controls
{
    // Stub class for DockPanel
    public class DockPanel : SystemWindowsControlsToolkit.DockPanel
    {
    }

    // Stub class for TreeView
    public class TreeView : SystemWindowsControls.TreeView
    {
    }

    // Stub class for TreeViewItem
    public class TreeViewItem : SystemWindowsControls.TreeViewItem
    {
    }

    // Stub class for Viewbox
    // Silverlight's Viewbox is sealed, so simulate it with a ContentControl wrapper
    public class Viewbox : ContentControl
    {
        public Viewbox()
        {
            Template = (ControlTemplate)XamlReader.Load(@"
                <ControlTemplate
                    xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
                    xmlns:controls=""clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"">
                    <controls:Viewbox>
                        <ContentPresenter/>
                    </controls:Viewbox>
                </ControlTemplate>");
        }
    }
}

Aside from some extern alias/using stuff at the top to disambiguate references, there's nothing here we didn't see last time around - in fact, the subclassing trick for HierarchicalDataTemplate looks just like it does for the other classes.

But I thought that trick didn't work for HierarchicalDataTemplate?

Right, it doesn't work on WPF - but this assembly is Silverlight-only.

Then you need a matching assembly for WPF or else the project won't compile because of the XMLNS reference.

Unless the corresponding assembly already exists on WPF.

Oh, no you didn't...

Oh, yes I did! :)

 

The Silverlight-only assembly is named PresentationFramework.dll, the same name as the platform assembly that contains the actual implementations of the controls for WPF. The Silverlight platform doesn't have a PresentationFramework.dll, and this latest trick takes advantage of that fact to sneak one in. Because Silverlight doesn't suffer from the same DataTemplate subclassing bug, it's perfectly okay to subclass HierarchicalDataTemplate there.

So the original subclassing trick does 95% of what we want, the control wrapping trick adds Viewbox, and the PresentationFramework trick adds HierarchicalDataTemplate - which means we're 100% covered!

 

Well, at least until someone contacts me to report another problem. Which would no doubt be interesting to debug and I'd certainly want to have a look.

But not right now. Adding that same-named assembly has left me feeling a little slimy and I think I need a shower before I spend more time on this. :)