XamlCruncher for Silverlight
January 24, 2009
New York, N.Y.
The static XamlReader.Load method — implemented in both the Windows Presentation Foundation and Silverlight — is a wonderfully simple and powerful tool. Give it some XAML and out pops an object. XamlReader.Load was obviously intended for applications to process XAML during runtime, but it has also proved to be a handy development tool.
I've been told that almost every Microsoft programmer who worked on "Avalon" wrote a little program built around XamlReader.Load to interactively write and edit XAML and see the resultant object. The one that made it into the official SDK was called XAMLPad.
I wrote my own XamlReader.Load-based tool called XamlCruncher in connection with my WPF book, Applications = Code + Markup. In fact, I structured the book around XamlCruncher. The whole first half of the book is a tutorial of all the skills necessary to build a Notepad clone, and then in the second half of the book, XamlCruncher is built on top of that.
Like all real WPF programmers, I write all my own XAML, and I've found XamlCruncher to be extremely useful for that purpose, even if I do eventually move the XAML into a Visual Studio project.
I've recently felt a need to have a similar tool for Silverlight, and I'm sure somebody has done it before me, but my program, which I call "XamlCruncher SL," arose from a desire to see how few features a program such as this can have and still be useful. You can run the result here:
Silverlight SL has almost no features at all. (At least for now; I may be enhancing the program if I find it useful.) Basically you enter and edit XAML in the left of the screen and see the result in the right part. The two parts are separated by a splitter. If the XAML passes XamlReader.Load, the edit text is displayed in black; otherwise the text is displayed in red, and the exception message is indicated in the left of the "status bar" at the bottom. Sometimes the error message has a line and column indication. For that reason, the right of the "status bar" displays the current line and column of the caret in the editor.
There is no file "save" or "load" feature in XamlCruncher SL. (Obviously saving to isolated storage is high on my list of probable enhancements.) For now, if you want to save something that you've typed in the editor, select it (Ctrl‑A) and copy it into the clipboard (Ctrl‑C). Then you're on your own. Similarly, to get some existing XAML into the editor, use paste (Ctrl‑V).
XamlCruncher SL has so few features that the feature I consider most important is this: When you press the Tab key in the editor, you aren't navigated to the splitter. Spaces are inserted instead. (It seems trivial but I did have to override OnKeyDown.)
This version of XamlCruncher SL does not install a handler for the Application.UnhandledException event. If the XAML successfully passes XamlReader.Load but there is a runtime error (which in my WPF experience most often occurs during a XAML-based animation), the program will not catch it. For example, try this chunk of XAML in the program:
-
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock Text="Hello, Silverlight!"
RenderTransformOrigin="0.5 0.5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Blue"
FontSize="48"
FontStyle="Italic">
<TextBlock.RenderTransform>
<RotateTransform x:Name="rotate" />
</TextBlock.RenderTransform>
</TextBlock>
<UserControl.Triggers>
<EventTrigger>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="rotate"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:3"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</UserControl.Triggers>
</UserControl>
(Yes, select, copy, and paste.) If you change the Storyboard.TargetName or Storyboard.TargetProperty, the XAML parses OK, but there's a runtime error, and that will be indicated by Internet Explorer with the warning icon in the lower left corner and the text "Error on page." However, SilverlightSL continues to run OK, and if you fix the problem, the animation will resume. (However, the warning icon doesn't go away.) I'll be investigating Application.UnhandledException as a way to avoid this.
If you're new to the joys of interactively writing XAML, keep in mind there are certain restrictions on the XAML that can be parsed by XamlReader.Load. The root element can't contain an x:Class attribute and event handlers can't be specified. However, I've notice something rather odd: Any non-standard attribute prefaced with an 'x' prefix is simply ignored. For example, insert this into any element:
-
x:whatever="who knows?"
and XamlReader.Load doesn't complain.
Here's the source code, and you may ask: "Can I reference my own (or somebody else's) Silverlight DLLs in the XAML I type into XamlCruncherSL?" and the answer is "Yes, because you now have the source code." Here's how to do it:
Suppose you downloaded the stuff in my recent blog entry Canonical Splines in WPF and Silverlight and you've built the solution, and you now have a Silverlight DLL named CanonicalSplineLib.dll in the CanonicalSplineLib\Bin\Debug directory.
With the XamlCruncherSL solution loaded in Visual Studio, in the XamlCruncherSL project, right-click References, click Add Reference, select the Browse tab, navigate to the CanonicalSplineLib.dll file, and select it. Recompile XamlCruncher SL. With Silverlight, DLL's are bound into the XAP file, but they are referenced in XAML the same way as in WPF: With an XML namespace declaration indicating the CLR namespace and the assembly name. Here's a (somewhat modified) version of the XAML I showed in that previous blog entry:
-
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:spline="clr-namespace:CanonicalSplineLib;assembly=CanonicalSplineLib">
<UserControl.Resources>
<Style x:Key="splineStyle"
TargetType="spline:CanonicalSpline">
<Setter Property="Points"
Value="100 75 150 25 200 150 250 25 300 75" />
<Setter Property="Stroke" Value="Blue" />
<Setter Property="StrokeThickness" Value="5" />
</Style>
</UserControl.Resources>
<StackPanel>
<spline:CanonicalSpline Tension="0"
Style="{StaticResource splineStyle}" />
<spline:CanonicalSpline Tension="1"
Style="{StaticResource splineStyle}" />
<spline:CanonicalSpline Tensions="0 1 0 1 0"
Style="{StaticResource splineStyle}" />
</StackPanel>
</UserControl>
In fact, the version of SilverlightSL I've made available online was compiled not only with the CanonicalSpline assembly in it, but also with all four DLLs that are part of the Microsoft Silverlight Toolkit. (The source code does contain references to those assemblies; you'll have to do those yourself based on the location of the DLLs on your machine.) That means you can experiment with stuff like the DockPanel:
-
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"
FontSize="24">
<ctrls:DockPanel>
<TextBlock Text="Docked Top"
HorizontalAlignment="Center"
ctrls:DockPanel.Dock="Top" />
<TextBlock Text="Docked Bottom"
HorizontalAlignment="Center"
ctrls:DockPanel.Dock="Bottom" />
<TextBlock Text="Docked Left"
VerticalAlignment="Center"
ctrls:DockPanel.Dock="Left" />
<TextBlock Text="Docked Right"
VerticalAlignment="Center"
ctrls:DockPanel.Dock="Right" />
<TextBlock Text="Not docked at all!"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</ctrls:DockPanel>
</UserControl>
Get a load of that long XML namespace declaration, and make sure it's all on one line, and notice that it's Microsoft.Windows.Controls rather than System.Windows.Controls.