The blog of dlaa.me

Posts tagged "Silverlight"

Maintaining pretenses with the layout system [LayoutTransform functionality updated for Silverlight 2!]

In the introductory post for LayoutTransformControl and the feature-enhancing follow-up, I gave a variety of examples to convey the need for LayoutTransform. I also noted that Silverlight 2 supports only RenderTransform, but went on to demonstrate how it was possible to use RenderTransform to get LayoutTransform behavior. I'm biased, but I happen think LayoutTransform is a pretty fundamental part of application design - and the feedback I've gotten about LayoutTransformControl suggests that at least some of you agree with me. :)

Soon after the Silverlight 2 release candidate became available, a customer contacted me to ask about updating LayoutTransformControl. I've spent some time doing just that and am happy to share a version that works well on the latest Silverlight 2 bits. The complete implementation - along with demo and test applications - can be found in LayoutTransformControl.cs in the ZIP file attached to this post.

The sample application works the same as it did before (with the updated look-and-feel of the new bits):

LayoutTransformControl Sample Application

It remains my belief that LayoutTransformControl for Silverlight 2 is just as powerful as WPF's LayoutTransform. Your application can take advantage of complete LayoutTransform functionality today - even though the Silverlight platform doesn't support LayoutTransform!

Notes:

  • Unfortunately, Silverlight does not yet behave the same as WPF with regard to automatically calling the property changed handler of a Transform-typed DependencyProperty when any of that Transform's members change. I explain how clean this makes things in my previous post, and outline how I worked around the issue by introducing a set of custom "Ltc" transforms that automatically notify their LayoutTransformControl parent of any changes. But with the release of the Silverlight 2 RC, the Transform class can no longer be derived from - which forces the removal of the "Ltc" Transforms and completely invalidates my workaround. :(
  • There are now just two scenarios for the Transforms provided to a LayoutTransformControl:
    1. The Transforms are static - so change notifications are unnecessary and there's no need to worry about this. (As before, the Motivation project demonstrates this scenario.)
    2. The Transforms are dynamic and it is now necessary to make a call to LayoutTransformControl.TransformUpdated() whenever they change so that LayoutTransformControl can find out and update its rendering. (The Sample project has been changed to demonstrate this scenario.)
  • Silverlight 2 has introduced a feature known as "layout rounding" - it's conceptually very similar to WPF's SnapsToDevicePixels property. (I can't find documentation of the new UIElement.UseLayoutRounding property quite yet, but it's discussed briefly in the Breaking Changes document.) Unlike WPF's SnapsToDevicePixels which takes effect silently (as I understand it), Silverlight's UseLayoutRounding (which defaults to True) does not. Specifically, UseLayoutRounding causes some of the intermediate values used during the measure/arrange pass to change while LayoutTransformControl is busy working its magic. Left alone, these unexpected changes appear to LayoutTransformControl as various special-cases of the layout system and cause incorrect layout for certain special angles/scales/etc. based on whether things get rounded up or down. So LayoutTransformControl disables UseLayoutRounding for itself (which avoids bad layout during scaling) and that automatically inherits to its templated Grid (which avoids bad layout during rotation). Consequently, LayoutTransformControl's rendering behavior remains correct at all times.
  • If UseLayoutRounding is necessary for some element within a LayoutTransformControl, simply set it back to True on that element and things should continue to work as desired. But note that LayoutTransformControl does look at the outermost child element (the value of its .Child property) for some of its calculations, so UseLayoutRounding should not be toggled on that element. If necessary, wrap that element in a container (ex: Grid) and it should then be fine to set UseLayoutRounding on it.

My thanks to everyone who has expressed interest in LayoutTransformControl or is actively using it in their projects! I hope this update continues to serve you well as Silverlight 2 approaches final release.

[LayoutTransformControl.zip]

Preserving access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser updated for the Silverlight 2 RC!]

Last night the Silverlight team released Silverlight 2 Release Candidate 0! The new bits include a minor change with a big impact on SilverlightDefaultStyleBrowser (background reading available here, here, here, and here): the path for default styles has changed from generic.xaml to themes/generic.xaml. So SilverlightDefaultStyleBrowser ends up looking in the wrong place for the RC assemblies and doesn't find any styles. That's not very helpful, so I've just updated SilverlightDefaultStyleBrowser to extract styles from both of these paths (which will continue to work with Beta bits and will also work with the new RC bits).

The version number of SilverlightDefaultStyleBrowser appears in the window's title and the latest release number is 1.0.3191.14705. (Note: I haven't updated the screen shot below which shows the introductory version number.) If installed via ClickOnce, the application should automatically prompt you to upgrade when it detects the update (which typically happens after running the app once or twice). If you're using the standalone EXE, you'll need to update manually.

SilverlightDefaultStyleBrowser Application

Click here or on the image above to install SilverlightDefaultStyleBrowser via ClickOnce with automatic updating.

Click here to download the standalone SilverlightDefaultStyleBrowser executable and source code in a ZIP file.

The RC bits of Silverlight introduce some neat new controls: ComboBox, PasswordBox, and ProgressBar. Go play with them now to see how they work - then use the new SilverlightDefaultStyleBrowser to see how they're styled!

Read a Silverlight book - with Silverlight! [SimpleSilverlightXpsViewer adaptation by Laurence Moroney]

At the bottom of the post on migrating SimpleSilverlightXpsViewer to Silverlight Beta 2, I hinted at "a cool, practical, relevant use of SimpleSilverlightXpsViewer that I'll be able to share sometime soon". "Soon" is now, and I'm happy to point readers at the "Read about Silverlight in XPS" post by Laurence Moroney in which he uses an adaptation of my viewer for showcasing the first three chapters of his new book, Introducing Microsoft Silverlight 2.

Here's what it looks like - just click the image to try it out for yourself:

Screen shot of the XPS Viewer adaptation

In addition to modifying the sample for his purposes, Laurence went further and integrated mouse wheel support: zooming in and out is as easy as spinning the wheel. I've got to agree that it's a very nice usability improvement!

Since releasing the XPS Viewer Sample, a few people have expressed interest in repurposing the code for something similar. Kudos to Laurence for doing a great job with this - and I love that it's being used to help spread the word about Silverlight! :)

Maintaining access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser updated for build 2.0.30523.8 and beyond!]

A few days ago the Silverlight team released a minor update to Beta 2, changing the version number from 2.0.30523.6 to 2.0.30523.8 in the process. The version number is part of Silverlight's install path and SilverlightDefaultStyleBrowser (background reading available here, here, and here) didn't know to look in the new location when automatically importing Styles. As such, when run on machines with the updated Silverlight bits, the list was missing Styles for the core controls like Button, ListBox, etc.. (As I discovered for myself a short while ago when I tried to use SilverlightDefaultStyleBrowser!)

I've just updated SilverlightDefaultStyleBrowser to look in all subdirectories of %ProgramFiles%\Microsoft Silverlight when starting up. Not only does this fix the current problem, but it should save me from having to do anything next time there's an update, as well! :)

The version number of SilverlightDefaultStyleBrowser always appears in the window's title and the latest release number is 1.0.3123.31689. (Note: I haven't updated the screen shot below which shows the introductory version number.) If installed via ClickOnce, the application should automatically prompt you to upgrade once it detects the update (which typically happens after running the app once or twice). If you're using the standalone EXE, you'll need to update manually.

SilverlightDefaultStyleBrowser Application

Click here or on the image above to install SilverlightDefaultStyleBrowser via ClickOnce with automatic updating.

Click here to download the standalone SilverlightDefaultStyleBrowser executable and source code in a ZIP file.

Notes:

  • On most machines it seems that the upgrade to Silverlight version 2.0.30523.8 automatically uninstalled the bits for 2.0.30523.6. However, on some machines (like one of mine!), the uninstall of 2.0.30523.6 was skipped or incomplete. So it's possible that SilverlightDefaultStyleBrowser's new behavior of importing from all directories under %ProgramFiles%\Microsoft Silverlight will import Styles from more than one version of Silverlight. To help avoid confusion - and to support multiple side-by-side installations of Silverlight in the future - I've modified the ToolTip for the ListBox items to display the path of the assembly from which that Style came. So if you see multiple Button Styles in the list, just hover over each of them to see where they came from - then pick the one that matters to you!
  • I've tweaked the ListBox item ToolTip to pop up when the mouse is over any part of the item (instead of just the Style's name). This was a simple matter of adding HorizontalContentAlignment=Stretch to the ListBoxItems via the ListBox's ItemContainerStyle property.

I'm sorry for any trouble the recent Silverlight upgrade may have caused! The good news is that this particular problem shouldn't happen again. :) And if it does, you can always use the "Add Assembly" button to manually import Styles from any file(s) you want until I get around to fixing things.

Smaller is better! [A simple step to shrink the download size of Silverlight 2 applications]

When putting content on the web, it's important to get that content to the user as quickly as possible. In general, reducing the amount of data users need to download is a big step in the right direction. As such, the XAP packaging format used by Silverlight 2 wraps everything in a single, compressed archive file that's browser-friendly. At the moment, the compression for XAP files in Beta 2 is not as efficient as it could be, and XAP files tend to be a bit larger than necessary. Until this is addressed in a future release of the Silverlight tools, here is something that's almost as good. :)

XAP files are really just standard ZIP files with a different extension, so we can use ZIP-file tools to improve the XAP situation quite easily. I wrote a simple batch file named XapReZip.cmd (contents below) that can be added to a Visual Studio 2008 Silverlight project to automatically re-compress the project's XAP file more efficiently. All you need to do is add a single command to your project's post-build events (detailed below) and from then on all your XAP files will be even smaller and quicker to download!

To demonstrate its effectiveness, I've run XapReZip against three test applications, two simple demonstration applications of mine, and the current version of two professional-grade applications you're probably already familiar with. The table below shows the starting XAP file size, the size after running XapReZip, the difference between the two, and the percent difference. (All sizes are in bytes.)

Scenario Starting Size After XapReZip Difference Reduction
New Silverlight Project 3,980 3,202 778 20%
... with DatePicker 73,228 56,513 16,715 23%
... and DataGrid 194,452 151,646 42,806 22%
HtmlTextBlock 12,360 10,312 2,048 17%
SimpleSilverlightXpsViewer 13,879 11,408 2,471 18%
NBC Olympics Video Player 789,523 615,239 174,284 22%
Line Rider 789,496 538,784 250,712 32%

Basically, you can expect to see about a 22% reduction in size for XAP files in general - slightly less for code-intensive XAPs and potentially much more for larger, richer XAPs. For example, it looks like Line Rider could shave a quarter of a megabyte off its download size without breaking a sweat - which is even more compelling given how easy it would be! :)

The complete code for XapReZip.cmd is below. As written, XapReZip assumes that Zip.exe and UnZip.exe will be located in the same directory as XapReZip.cmd (this is easy to change) and that all of the files that are part of the XAP are available in the same directory as the XAP file itself (which just happens to be true after compiling with Visual Studio or MSBuild). For the purposes of this demonstration, I've used Zip 2.32 and UnZip 5.52 from the Info-ZIP group because they're free and have a nice license. Of course, you can use whatever tools you're most comfortable with.

@REM XapReZip - Recompresses XAP files smaller

@REM Invoke as a VS post-build event like so:
@REM C:\XapReZip\XapReZip.cmd $(TargetName).xap

@echo off
setlocal

REM Define paths to zip.exe and unzip.exe
set ZIPEXE="%~p0zip.exe"
set UNZIPEXE="%~p0unzip.exe"

REM Define paths for intermediate files
set XAP=%1
set XAPDIR=%~p1
set XAPBAK=%1.bak
set XAPZIP=%1.zip

REM Output a banner message
echo XapReZip: %XAP%

REM Change to XAP file directory
pushd %XAPDIR%

REM Create new XAP file with maximum compression
%UNZIPEXE% -Z -1 %XAP% | %ZIPEXE% -@ -9 -X %XAPZIP%

REM Abort if something went wrong
if ERRORLEVEL 1 goto :DONE

REM Replace original XAP file with smaller one
copy /y %XAP% %XAPBAK% > NUL
copy /y %XAPZIP% %XAP% > NUL
del %XAPZIP%

REM Output a success message
echo XapReZip: Success

:DONE

REM Restore previous directory
popd

endlocal

Adding XapReZip to a Silverlight Project in Visual Studio is easy. Just go to the Project menu, choose Properties (it's the last item), switch to the Build Events tab, and set a post-build event command line like this: C:\XapReZip\XapReZip.cmd $(TargetName).xap. Here's what it looks like in Visual Studio:

Configuring XapReZip as a post-build event in Visual Studio

After you've set that one command, you can forget about it - XapReZip will go to work every time you recompile the application and you'll be able to run and debug it just like always. Except a little smaller. :)

So if you're eager to keep your Silverlight applications as lean as possible, consider adding XapReZip to your project. It'll probably be the easiest 22% savings you get all day!

[XapReZip.cmd]

The layout system lies have become a bit more elaborate [LayoutTransform functionality updated and enhanced for Silverlight 2 Beta 2!]

In the introductory post for LayoutTransformControl, I showed a trivial use case to demonstrate the need for LayoutTransform in some scenarios. I also noted that Silverlight 2 supports only RenderTransform, but went on to demonstrate how it was possible to use RenderTransform to get LayoutTransform behavior. I described how my LayoutTransformControl sample did just that, but implemented only RotateTransform functionality for its initial release. My impression then - as now - is that rotation is easily the most common use of LayoutTransform. However, there are plenty of scenarios where the other Transform subclasses (scale, skew, matrix, etc.) are required, and I wanted to have a solution for them as well.

So I've added full support for arbitrary transformations to LayoutTransformControl along with updating it for Beta 2! (The complete implementation - along with demo and test applications - can be found in LayoutTransformControl.cs in the attached ZIP.)

Where you might previously have written:

<local:LayoutTransformControl Angle="15">
    <TextBlock Text="I am rotated 15 degrees!"/>
</local:LayoutTransformControl>

You now write:

<local:LayoutTransformControl>
    <local:LayoutTransformControl.Transform>
        <RotateTransform Angle="15"/>
    </local:LayoutTransformControl.Transform>
    <TextBlock Text="I am rotated 15 degrees!"/>
</local:LayoutTransformControl>

Wait a second! That's more typing than before - why is this an improvement? Well, because you can also do this (just as you would on WPF):

<local:LayoutTransformControl>
    <local:LayoutTransformControl.Transform>
        <ScaleTransform ScaleX="1.5" ScaleY="2.5"/>
    </local:LayoutTransformControl.Transform>
    <TextBlock Text="I am a little wider and a lot taller!"/>
</local:LayoutTransformControl>

Or this:

<local:LayoutTransformControl>
    <local:LayoutTransformControl.Transform>
        <MatrixTransform Matrix="-1,0,0,1,0,0"/>
    </local:LayoutTransformControl.Transform>
    <TextBlock Text="I am flipped horizontally!"/>
</local:LayoutTransformControl>

Or even this:

<local:LayoutTransformControl>
    <local:LayoutTransformControl.Transform>
        <TransformGroup>
            <ScaleTransform ScaleX="1.5" ScaleY="2.5"/>
            <SkewTransform AngleX="10"/>
            <MatrixTransform Matrix="-1,0,0,1,0,0"/>
            <RotateTransform Angle="15"/>
        </TransformGroup>
    </local:LayoutTransformControl.Transform>
    <TextBlock Text="I am all of the above - and tilted, too!!"/>
</local:LayoutTransformControl>

So maybe it's a bit of an improvement, after all... :)

Naturally, I updated my sample application to show off the new capabilities:

LayoutTransformControl Sample Application

And I updated my cross-platform test suite as well (here's the WPF version):

LayoutTransformControl Test Matrix on WPF

LayoutTransformControl should now be just as powerful as WPF's LayoutTransform is. Your application can take advantage of complete LayoutTransform functionality today - even though the Silverlight platform still doesn't officially support LayoutTransform!

Notes:

  • Improvements to Silverlight in Beta 2 (vs. Beta 1) meant that I was able to remove the lesser of the two Silverlight-specific workarounds present in the Beta 1 code.
  • I removed Button from the test matrix and added two new test cases to cover scenarios where LayoutTransformControl was previously behaving incorrectly. (Unfortunately, the default Button template in Beta 2 still causes the Button to measure/arrange itself incorrectly in some cases. The existing ContentControl test cases already cover correct Button behavior, so there's no loss of coverage.)
  • In WPF, changes to the members of a Transform property (be it an individual Transform or a TransformGroup) automatically cause the Transform property's property changed handler to be invoked (even though OldValue==NewValue). This is a wonderfully elegant way to handle the situation - and unfortunately this behavior isn't present on Silverlight. So LayoutTransformControl needs a way to know when one of its Transform members has been updated and it can't rely on Silverlight to tell it. One solution is to try to create a Binding to the relevant properties of each of the members. Unfortunately, Bindings don't work on Transform-derived classes in Silverlight (my theory: because Transform derives from DependencyObject instead of FrameworkElement where the SetBinding method is implemented). So what I've done is create a set of "Ltc"-prefixed Transform-derived classes that do provide automatic notifications (but still not Binding support - due to the same Silverlight limitation, I suspect). So instead of RotateTransform, you use LtcRotateTransform and get change notifications for free. The "Ltc" Transforms have the same transformation properties (ex: Angle, ScaleX) as their counterparts, so they're simple, plug-compatible replacements - with two caveats: LtcTranslateTransform doesn't exist (because TranslateTransform has no effect during LayoutTransform), and the CenterX/CenterY properties aren't implemented (for the same reason).
  • The "Ltc" Transforms are a pretty good workaround, but you don't have to use them. If the members of the LayoutTransformControl's Transform property never change, then change notification is unnecessary and the standard Transforms can be used without issue. The code in the "Motivation" project (part of the attached solution) demonstrates this approach. If the members of the Transform property do change and you're able to use the "Ltc" transforms, then the right thing should happen automatically. The code in the "Sample" project demonstrates this approach. But if your scenario is such that the members of the Transform property do change and you're unable to use the "Ltc" Transforms, all is not lost! LayoutTransformControl exposes a public TransformUpdated method that you can call to notify it of changes to the members of its Transform property. The code for the test matrix projects uses this last approach.
  • In any kind of project where the goal is to duplicate an existing behavior, it's common to do side-by-side comparisons to find bugs in the new implementation. This is exactly what my WPF test matrix does and it helped me find and fix a number of problems during the development of LayoutTransformControl. However, there typically comes a point at which this comparison process starts revealing differences that turn out to be bugs in the reference implementation. That's usually a pretty good sign that the new implementation is approaching the same level of quality as the old one - and it's a milestone I've hit with LayoutTransformControl. In addition to the "maximal area rectangle" improvement mentioned in the original blog post, LayoutTransformControl exhibits two new, deliberate behavior differences (improvements!) from WPF's LayoutTransform implementation. They're subtle, but they can both be demonstrated in the test matrix. Let me know if you think you've found one! :)
  • The examples above assume the "local" namespace prefix has been mapped to an assembly containing the LayoutTransformControl implementation:

    xmlns:local="clr-namespace:LayoutTransformControlSample;assembly=YourAssemblyName"

My goal with LayoutTransformControl has always been to address a perceived need. It began life as a bit of an experiment, but by now I've become rather fond of the solution and plan to incorporate it into projects of my own. I hope that if you have a need for LayoutTransform behavior in Silverlight, you'll consider LayoutTransformControl, too!

[LayoutTransformControl.zip]

Text; from a slightly different perspective [VerticalTextBlock Control sample for Silverlight 2]

During an informal discussion yesterday, the question of displaying text vertically (i.e., letters stacked on top of each other instead of side-by-side) came up and I offhandedly said it would be pretty easy to do. Later on, I got curious about just how accurate my claim was, so I spent a bit of time on the bus coding up a quick demonstration of what I had in mind. What I ended up with is a simple example, but it's enough that I feel like my flippant estimate wasn't so far off after all. :)

VerticalTextBlock is a simple Control subclass that runs on both Silverlight and WPF. The following XAML (in which the optional parts are italicized):

<local:VerticalTextBlock
    Text="Hello world"
    FontFamily="Arial"
    FontSize="12"
    FontWeight="Bold"
    Background="Orange"/>

Renders as follows (Silverlight on the left, WPF on the right):

VerticalTextBlock Demonstration

The implementation is quite simple: VerticalTextBlock exposes a Text property of type string, the VerticalTextBlock Template contains a TextBlock, and that TextBlock is populated with a series of LineBreak-separated Runs containing one character each.

Here's the complete code that compiles for both platforms:

using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;

namespace VerticalTextBlockSample
{
    public class VerticalTextBlock : Control
    {
        public VerticalTextBlock()
        {
            IsTabStop = false;
            var templateXaml =
                @"<ControlTemplate " +
#if SILVERLIGHT
                    "xmlns='http://schemas.microsoft.com/client/2007' " +
#else
                    "xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " +
#endif
                    "xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>" +
                    "<Grid Background=\"{TemplateBinding Background}\">" +
                        "<TextBlock x:Name=\"TextBlock\" TextAlignment=\"Center\"/>" +
                    "</Grid>" +
                "</ControlTemplate>";
#if SILVERLIGHT
            Template = (ControlTemplate)XamlReader.Load(templateXaml);
#else
            using(var stream = new MemoryStream(Encoding.UTF8.GetBytes(templateXaml)))
            {
                Template = (ControlTemplate)XamlReader.Load(stream);
            }
#endif
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _textBlock = GetTemplateChild("TextBlock") as TextBlock;
            CreateVerticalText(_text);
        }

        private string _text { get; set; }
        private TextBlock _textBlock { get; set; }

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
            "Text", typeof(string), typeof(VerticalTextBlock), new PropertyMetadata(OnTextChanged));
        private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((VerticalTextBlock)o).OnTextChanged((string)(e.NewValue));
        }
        private void OnTextChanged(string newValue)
        {
            CreateVerticalText(newValue);
        }

        private void CreateVerticalText(string text)
        {
            _text = text;
            if (null != _textBlock)
            {
                bool first = true;
                foreach (var c in _text)
                {
                    if (!first)
                    {
                        _textBlock.Inlines.Add(new LineBreak());
                    }
                    _textBlock.Inlines.Add(new Run { Text = c.ToString() });
                    first = false;
                }
            }
        }
    }
}

At the end of the day, VerticalTextBlock is a simple control I wrote to keep myself honest. :) I figured it could be useful to others, so I'm posting about it and attaching a ZIP file with the complete solution+projects so anyone who's interested can try it out!

[VerticalTextBlock.zip]

Proof-of-concept Silverlight XPS reader comes to Beta 2 [SimpleSilverlightXpsViewer sample updated for Silverlight 2 Beta 2!]

Earlier this week I was asked about an update to my SimpleSilverlightXpsViewer sample for the newly released Silverlight 2 Beta 2. (Background reading: Introductory Post, Beta 1 Update.) I spent a bit of time on this just now and migrating SimpleSilverlightXpsViewer to Beta 2 was very straightforward.

SimpleSilverlightXpsViewer Application

I've updated the SimpleSilverlightXpsViewer demonstration page and also the source code download, so you can try things out in your browser and/or download the code to see how it works!

Notes:

  • A project reference to System.Net needed to be added in order for WebClient to resolve properly. (This is covered in the "Breaking Changes" document.)
  • DependencyObject.SetValue no longer seems to automatically convert the type of its value parameter, so there were three places I changed an int to a double for consistency with the underlying DependencyProperty type (ex: "0"->"0.0").
  • The "disappearing page border" issue that showed up in Beta 1 still seems to be present in Beta 2, so I worked around it by changing the "pageGraphic" from a Rectangle[Fill/Stroke] to a Border[BorderBrush]+Grid[Background]. A web search turned up a couple of customer reports of this behavior for Shapes (Path, Rectangle, etc.), so it should be fixed for the final release of Silverlight.
  • I discovered and fixed a bug with the math behind the zoom slider that could cause problems when the browser window was made very small. (This bug was unrelated to Beta 2.)

SimpleSilverlightXpsViewer is a fun project that really seems to resonate with people; folks have told me all kinds of neat ideas they had while playing around with it. In fact, if all goes well, there just might be a cool, practical, relevant use of SimpleSilverlightXpsViewer that I'll be able to share sometime soon... :)

Again with the support for simple HTML display in Silverlight [HtmlTextBlock sample updated for Silverlight 2 Beta 2!]

A customer recently asked about an update to my HtmlTextBlock sample for the newly released Silverlight 2 Beta 2. (Background reading: HtmlTextBlock Announcement, Improvements, Beta 1 Update, Data Binding Support) I'd meant to eventually migrate my samples anyway, so HtmlTextBlock seemed like a great place to begin!

HtmlTextBlock Demonstration

I've updated the HtmlTextBlock demonstration page and also the source code download, so you can try things out in your browser and/or download the code to see how it works!

Notes:

  • InitializeFromXaml was removed from Beta 2, so HtmlTextBlock now uses XamlReader.Load to set the equivalent Template in its constructor (and uses OnApplyTemplate to get a reference to the contained TextBlock).
  • Some of the properties HtmlTextBlock used to explicitly expose for TextBlock compatibility have moved to the Control class (from which HtmlTextBlock derives) in Beta 2, so HtmlTextBlock no longer implements them itself (ex: FontFamily, FontSize, Foreground). A handful of TemplateBindings are used to bind these properties of the Template's TextBlock to the corresponding properties on HtmlTextBlock.
  • The parameter list of DependencyProperty.Register changed slightly in Beta 2 and PropertyMetadata is now used to wrap the PropertyChangedCallback parameter.
  • Special-case code for setting certain values of TextBlock.LineHeight is no longer necessary in Beta 2 and has been removed.
  • The unusual "text disappears for certain font/size combinations" behavior that was present in Beta 1 has been fixed by the Silverlight team for Beta 2.

As you might expect, migrating HtmlTextBlock to Beta 2 was fairly straightforward. The majority of the changes arose because it tries to be a TextBlock and some of the text properties shifted around. (Incidentally, I wouldn't expect most applications to bump into this at all.) I hope people find the new sample useful as they come up to speed on Silverlight 2 Beta 2!

Continuing access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser updated for Silverlight 2 Beta 2!]

Silverlight 2 Beta was released earlier today and one of the big changes is the switch to using Visual State Manager for control design/development. Another is that many of the core controls (Button, ListBox, etc.) have moved from the SDK into the Silverlight runtime itself (which nicely avoids the need to include them with every application download). I wanted to be sure that SilverlightDefaultStyleBrowser (background reading available here and here) worked seamlessly with Beta 2, so I made a few quick changes to help users explore the new stuff.

The version number of SilverlightDefaultStyleBrowser always appears in the window's title and the latest release number is 1.0.3079.23243. (Note: I haven't updated the original screen shot which shows the introductory version number.) If installed with ClickOnce, the application should automatically prompt you to upgrade once it detects the update (which typically happens after running the app once or twice). If you're using the standalone EXE then you'll need to update manually. For people who might not be able to upgrade to Beta 2 immediately, there's no need to worry because the new version works with the Beta 1 bits as well.

SilverlightDefaultStyleBrowser Sample page

Click here or on the image above to install SilverlightDefaultStyleBrowser via ClickOnce with automatic updating.

Click here to download the standalone SilverlightDefaultStyleBrowser executable and source code in a ZIP file.

Notes:

  • Now that Blend 2.5 has rich support for Silverlight control design, some of the uniqueness of SilverlightDefaultStyleBrowser has been lost. However, it's still pretty handy for situations where Blend isn't available, so there may still be some residual value here. :)
  • The change I knew I wanted to make was the default search path for control assemblies. In Beta 1, nearly all the controls were part of the SDK, so that was the only place SilverlightDefaultStyleBrowser looked. But with Beta 2, the core controls are part of the Silverlight runtime and are located in the runtime install directory. SilverlightDefaultStyleBrowser now automatically imports assemblies from both locations when it loads.
  • The thing I didn't expect to change was special-casing the XML namespace for the Visual State Manager parts of the new control templates. SilverlightDefaultStyleBrowser uses LINQ to XML for parsing XAML and, by default, LINQ to XML emits namespace attributes at the time of first use (instead of on the top node). As far as I know, this is perfectly valid, but the resulting XAML causes a compile error when pasting such a Style/Template into Page.xaml: "Unexpected PROPERTYELEMENT in parse rule ...". This isn't ideal, so I added a bit of code to introduce the "xmlns:vsm" namespace on the Template node (i.e., before its first use) to avoid this error. (Note: The extra namespace is unnecessary - but harmless - for Beta 1 Templates.)
  • The header text changed from (for example) "System.Windows.Controls.ListBoxItem" to "System.Windows.Controls:ListBoxItem" to better communicate that ListBoxItem came from the System.Windows.Controls assembly (vs. being part of that namespace (which is not always be the case)).
  • If you compile the source code yourself, you may get a compiler warning from the ClickOnce settings in the project files: "Unable to find manifest signing certificate in the certificate store.". Despite turning off ClickOnce manifest signing, it seems to have gotten turned back on somehow. To make the warning go away, it should be enough to go to the Project's Properties page and uncheck the "Sign the ClickOnce manifests" box on the Signing tab. However, I found that unchecking that box didn't always do the trick - what worked for me was to check that box, use the "Create Test Certificate" button to create a dummy certificate, and then uncheck it.

Well, that's all the news about SilverlightDefaultStyleBrowser. Now go download Silverlight 2 Beta 2 and have fun with the new features! :)