The blog of dlaa.me

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! :)

Lying to the layout system for a good cause [Bringing LayoutTransform to Silverlight 2!]

People who want to rotate visual elements in Silverlight 2 are likely to use RotateTransform within RenderTransform - but they may not always get the results they expect! For example, using RenderTransform to achieve the following effect:

Sweet

Actually renders like this:

Whoops

But the problem isn't with RenderTransform - it's with using the wrong tool for the job! By design, RenderTransform applies its transformations (a rotation in this case) after the layout system has performed its measure/arrange pass. So when the elements in the example are being measured and arranged, the text is still horizontal. It's only after everything has been positioned that the text is finally rotated - and ends up in the "wrong" place. While it's possible to correct for this discrepancy by hard-coding all the relevant offsets in the XAML (very brittle and error-prone) or by adjusting all the offsets in code (only slightly more flexible - and a lot more work), these aren't great alternatives.

The right tool for the job is LayoutTransform which applies its transformations before the layout pass. With LayoutTransform, the text in the example is already rotated by the time the elements are measured and arranged, and the desired effect can be achieved quite simply.

But there's a catch: LayoutTransform doesn't exist in Silverlight 2 (Beta 1)...

However, there's no reason to let that stop us. Rotation is rotation whenever it happens, so maybe there's a way to get the already-optimized RenderTransform implementation to do the real work earlier in the layout pass. Unfortunately, we can't change when RenderTransform is applied.

But it turns out that we can tell a very carefully crafted set of lies to the layout system during the measure/arrange pass in order to convince it to lay things out as if it supported LayoutTransform - then we let RenderTransform do the work of actually rotating the content. The result is that we've got something that looks like LayoutTransform and behaves like LayoutTransform - so it might as well be LayoutTransform! :)

I've done just this and the result is something I've called LayoutTransformControl. The complete implementation can be found in LayoutTransformControl.cs in the attached ZIP. The XAML for LayoutTransformControl is quite simple and follows the well-known WPF Decorator model (ex: Border, Viewbox):

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

Note: This assumes the "local" namespace prefix has been mapped to an assembly containing the LayoutTransformControl implementation:

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

In fact, the first picture of this post (the one that looked right!) was done with LayoutTransformControl. But it's easy to get simple scenarios right... So I also wrote a sample application that lets you interactively change the rotation angle and swap in different content:

LayoutTransformControl Sample Application

Attribution: The XAML example came from a post on designerslove.net; the image is from the set of stock Windows Vista wallpapers.

But it's easy to get a sample right... So I also wrote a test harness to exercise a handful of interesting elements in most of the interesting constraint scenarios and show RenderTransform along with LayoutTransformControl:

LayoutTransformControl Test Matrix on Silverlight

But the rules of layout are sufficiently complex and subtle that it's hard to tell if LayoutTransformControl is behaving properly without knowing how it's supposed to behave... So the test harness (and LayoutTransformControl!) also runs under WPF where LayoutTransform is supported and can be used to visually verify that LayoutTransformControl is doing what it should by comparing the bottom two rows:

LayoutTransformControl Test Matrix on WPF

Whew! :)

So - after all that posturing and seemingly comprehensive test coverage, you might expect me to be confident that LayoutTransformControl behaves correctly under all circumstances. Well... no. LayoutTransform is conceptually simple, but exhibits all kinds of weird and unexpected behaviors in practice. I've lost count of the number of times I had to stare at a bit of LayoutTransform output and figure out why it's correct - sometimes more than once for the same output! Add to that the fact that LayoutTransformControl is doing everything outside the core layout system, and I'm kind of surprised any of this works... :)

I do believe that LayoutTransformControl behaves correctly in all scenarios I've subjected it to, but I would not be surprised at all to hear about other scenarios where it breaks down. If you think you've found such a scenario, please let me know and I'll try to figure out what might be going on. (But before you do, please check the behavior on WPF - that's the first thing I'll do anyway!)

Notes:

  • This release of LayoutTransformControl supports only rotation because that's by far the most common scenario. However, I'm prepared to add support for scale, skew, and matrix if folks find this useful. (Party trivia: Translation has no effect during LayoutTransform.)
  • For the most part, using LayoutTransformControl is simply a matter of wrapping the desired content and setting the angle. But it's important to note that some properties will need to be moved from the child control to LayoutTransformControl. For example, when positioning things within a Grid, the Grid.Column and Grid.Row attached properties need to be set on LayoutTransformControl in order to be recognized by the parent Grid.
  • Examination of the test matrices above reveals two things that deserve more explanation:
    • Thing 1: The sizing of the Buttons in the Silverlight test matrix is different than in the WPF matrix. At first glance, the sizing seems to be wrong, but that's not actually the case. The default Button style in Silverlight Beta 1 has a bug that interferes with proper layout (and is completely unrelated to LayoutTransformControl). This can be seen with a simple example; adding the following to a new project should show a small, centered Button just like it does on WPF:
      <Button Content="Button" HorizontalAlignment="Center" VerticalAlignment="Center"/>
      However, on Silverlight Beta 1 the result is a very large Button instead. The problem is that the template in the default style includes the following:
      <Path Stretch="Fill" .../>
      The Stretch on that Path element causes the Button to measure much larger than it should (and a similar issue with the Line element affects the default ListBoxItem style). It's because the Button behaves as it does that sizing with LayoutTransformControl looks like it does. It's comforting to note that applying the Silverlight Button style to a WPF Button and using the real LayoutTransform gives the same wrong-looking behavior.
    • Thing 2: The rotated rectangle used by LayoutTransformControl for its child content is slightly different than that used by LayoutTransform under certain circumstances. This causes the child control to layout in a slightly longer, narrower rectangle and can be seen in the first column of the WPF test matrix. Perhaps surprisingly, this difference is deliberate. When determining the parameters of the rotated rectangle, the WPF algorithm finds the largest rectangle possible (as measured by area). It just so happens that the algorithm I came up with manages to find an even larger rectangle in certain cases. :) As far as I've seen, the algorithm in LayoutTransformControl is never worse - and sometimes better - so I decided it's a winner.
  • The example elements in the test matrix were chosen as follows:
    • Button - Represents the classic layout-friendly control which has a preferred size, but is willing to go smaller or larger as space permits
    • ContentControl - Containing a Border and a TextBlock, this simple Button-like layout acts like a Button should on Silverlight
    • TextBlock - Interesting for layout because it refuses to render smaller than the size it asked for
  • I've omitted any technical discussion of how LayoutTransformControl works from this blog post. For now, curious readers can refer to the commented source code. If there's interest, I'll write a follow-up post explaining what's really going on under the covers.

LayoutTransformControl was an interesting project that demonstrates once again the power and versatility of the WPF/Silverlight layout system. LayoutTransform and RenderTransform are like peanut butter and jelly - and I'm glad to help reunite them on Silverlight. If you've got a layout problem and RenderTransform isn't doing what you need, maybe LayoutTransformControl is the solution!

[LayoutTransformControl.zip]

IValueConverter: The Swiss Army Knife of Bindings [PropertyViewer sample is a WPF/Silverlight visualization and debugging aid!]

If you've made much use of data binding in WPF or Silverlight, you've probably come across the IValueConverter interface. IValueConverter sits between the data source and destination and gives the developer a chance to examine/alter/replace the data as it flows through the converter. It's been my experience that IValueConverter is a powerful and versatile tool for application developers.

As part of a recent project, I wanted to display some of an object's properties in a simple list. Specifically, I had an instance and I wanted to display a list of "Property Name: Property Value" entries for each property of that object. My background with ListBox led me to naturally think of using ItemsControl for the basis of my property viewer; the ItemsControl content model (with its support for DataTemplates) seemed like a natural fit.

Aside: One of the key things XAML enables is a distinct separation of implementation from representation. By explicitly separating most aspects of how an application looks (XAML) from how it works (code) as part of the developer/designer workflow, WPF and Silverlight help to enforce a level of encapsulation (in the object-oriented programming sense) that makes programs easier to write and maintain. In the ideal world, a program's functionality is entirely expressed in its code - and so it's possible for others to completely change that application's appearance without knowing or caring how it's implemented. Just like the web has CSS restyling contests, WPF has reskinning competitions!

So I knew I wanted to use ItemsControl and I knew I wanted to keep the UI aspects of the property viewer in XAML-space (what it looked like, what properties it displayed, etc.). I started looking at how IValueConverter might help and that led to the solution I describe here. PropertyViewer ends up being quite simple to use - and a good demonstration of the power of IValueConverter!

Here's the comment header from the code:

/// <summary>
/// IValueConverter implementation that expands some/all of an object's
/// public properties as a collection of bindable name/value instances.
/// </summary>
/// <remarks>
/// * Bindable instances are of type PropertyDetails, a contained class
///   that exposes a property's Name and Value as properties.
/// * All public properties are enumerated by default; ConverterParameter
///   can be used to specify which properties will be enumerated (and in
///   which order) by passing a space-delimited list of names.
/// * If DisplayNameAttribute is associated with a property, DisplayName
///   will be used instead of the property name.
/// * PropertyViewer can be used for simple data visualization, debugging
///   of Bindings, and more.
/// </remarks>
/// <example>
/// For ItemsControl/ListBox (accessing the object via DataContext):
///   ItemsSource="{Binding Converter={StaticResource PropertyViewer}}"
/// As above, but with a custom property list:
///   ItemsSource="{Binding Converter={StaticResource PropertyViewer},
///     ConverterParameter='PropertA PropertyB'}}"
/// For ItemsControl/ListBox, but specifying the object via Source:
///   ItemsSource="{Binding Source={StaticResource ObjectInstance}
///     Converter={StaticResource PropertyViewer}}"
/// </example>
public class PropertyViewer : IValueConverter
{ ... }

Like the comment says, it's easy to hook up a PropertyViewer:

<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyViewer}}"/>

Customizing the PropertyViewer is also easy:

<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyViewer},
    ConverterParameter='Species EatsBugs RelativeMass'}">

Because the end result is a collection of objects, all the existing ItemsControl knowledge and techniques can be used to completely customize the look and feel of the property viewer!

The examples above came from a simple application I wrote to demonstrate PropertyViewer in action. By design, the exact same XAML and code work on both WPF and Silverlight. To prove it, I attached the complete source code for the combined Visual Studio 2008 solution to this blog (download it from the link at the bottom of this post). The sample displays a list of animals, one default PropertyViewer, and one customized PropertyViewer. Select any animal to find out more about it (note that the PropertyViewer updates automatically when the data source changes).

Here's what it looks like on WPF:

WPF Sample

And on Silverlight:

Silverlight Sample

Thanks to the power of IValueConverter, it's easy to use PropertyViewer to display an object's properties in a customizable way. Additionally, PropertyViewer can help troubleshoot data bindings: just drop one in the target location and you can see the available properties along with their current values. I originally wrote this for my project - but now I hope you can use it in yours!

[PropertyViewer.zip]

Buttons in a ListBox, and More [Demonstration of some useful Silverlight techniques]

One scenario I've seen cause a bit of trouble on the Silverlight Controls Forum is that of putting a Button in a ListBox. There are two aspects of this that seem to cause difficulty and I thought it would be helpful to demonstrate the complete scenario in a runnable, self-contained sample. (Please Download the ZIP file attached to the bottom of this post for all the code/XAML in a ready-to-go Visual Studio 2008 + Silverlight Tools solution.) While I was developing the sample, I threw in a couple of other handy techniques that may not be widely known. The sample application shows a typical shopping cart experience where products are listed and their quantities can be interactively changed:

"Buttons in a ListBox" Sample Application

Details on the button scenario:

  • One problem with putting a Button in a ListBox is that by default the Button can be clicked only when the corresponding ListBoxItem is selected. This is an unfortunate consequence of a very late change to Silverlight Beta 1 that causes ListBox to receive duplicate GotFocus/LostFocus events. For most controls the duplication is harmless, but for ListBox it interferes with ListBox's attempts to preserve WPF's focus/selection behavior when a focusable control (like Button) is clicked on. Fortunately, Button (and its subclasses like CheckBox) expose the ClickMode property which can be used to work around the problem. Simply changing the Button's ClickMode enumeration from the default value of Release to Press does the trick.
    <Button ... ClickMode="Press" ... />
  • Another problem people tend to have is hooking up an event handler to Buttons in a ListBox. While this can be a little tricky to do in code (specifically when the ItemTemplate is being used), it's quite easy to do in XAML by specifying a value for the Click event. Visual Studio generates the code for the event handler automatically, so this ends up being elegant and simple.
    <Button ... Click="Add_Click" ... />

Other points of interest:

  • ListBox's ItemsSource property is used to specify the items as a collection of the custom class Product and its ItemTemplate property is used to display those objects appropriately (coloring the text, adding Buttons, etc.).
    For lots more about configuring and using ListBox, please see my ListBox/ScrollViewer FAQ.
  • The Add/Remove Buttons directly modify the Quantity property of the Product objects. While that would not normally be enough to automatically update the UI, the Product class implements the INotifyPropertyChanged interface which can be thought of as kind of a light-weight DependencyProperty for non-DependencyObjects. More simply, it's an easy way to add change notifications to simple classes that Silverlight's data binding framework can use to automatically respond to property changes. In this case, updates to the Quantity property fire the PropertyChanged event and the displayed quantity gets updated automatically.
    public int Quantity
    {
        get { return _quantity; }
        set
        {
            _quantity = value;
            // Fire PropertyChanged event to notify listeners of changed value
            var handler = PropertyChanged;
            if (null != handler)
            {
                handler.Invoke(this, new PropertyChangedEventArgs("Quantity"));
            }
        }
    }
    private int _quantity;
    
    public event PropertyChangedEventHandler PropertyChanged;
    
  • It's nice when the UI automatically prevents invalid actions - in this case it's an invalid action for the user to remove items when the quantity is already 0 (as it is for "Bananas" in the image above). While it would be possible to create a dedicated bool property of the Product class and bind the Remove Button's IsEnabled property to it, there's a more elegant way. What the sample does is bind the Remove Button's IsEnabled property to the Quantity property - using an IValueConverter to convert the int type to a bool automatically. This nicely avoids adding otherwise unused properties to the Product object and helps to isolate the relevant logic.
    // Simple IValueConverter returns true iff the value is positive
    // Used to toggle Remove button's IsEnabled when Quantity changes between 0 and 1
    public class IntIsPositive : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (0 < ((int)value));
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

At the risk of straying too far from the original scenario, I think the additional techniques I've shown here support it nicely and improve the user experience notably. I hope this sample helps people with their own projects - and maybe introduces a useful trick or two!

Enjoy!

[ListBoxWithButtons.zip]

Proof-of-concept Silverlight XPS reader gets some Beta 1 love [SimpleSilverlightXpsViewer updated for Silverlight 2 Beta 1!]

After a few customer requests to update my SimpleSilverlightXpsViewer proof-of-concept XPS reader for Silverlight 2 Beta 1, I finally found time to do so. :) I've just updated the original SimpleSilverlightXpsViewer demonstration page and also updated the original source code download - so you can try it out in your own browser and/or download the code to see how it works!

SimpleSilverlightXpsViewer Application

Click on the image above to play around with the application in your browser. More details about what it is doing and how it works are available in the original SimpleSilverlightXpsViewer post.

Notes:

  • This was a fairly straightforward port from the original 1.1 Alpha implementation to 2 Beta 1. I have not changed the manual Canvas-based layout to automatic layout via Grid/StackPanel/etc. - but I'd definitely take that approach if I were writing it from scratch today!
  • The XAML didn't change at all - and most of the necessary code changes are detailed in the Breaking Changes in Silverlight 2 document on MSDN.
  • Because of the file:// URL restrictions introduced a while ago as part of the 1.1 Refresh, the sample code includes a separate web site. I explain more in the second half of my post outlining changes for the 1.1 Refresh.
  • One notable improvement is that I was able to #define SETSOURCE (see the discussion in the original post) and therefore the images are all being accessed from the XPS document itself instead of needing to be pulled out as separate files! I removed the associated #if/#endif blocks because they are no longer relevant.
  • I still didn't see any way around the Glyphs.FontUri issue (discussed in the same section of the original post), so the fonts remain separate like before.
  • Silverlight occasionally renders the "pageGraphic" Rectangle incorrectly. The obvious consequence of this is that the white page background disappears if it extends outside the Silverlight control's edges (e.g., you've zoomed in or panned). It's possible to verify that you're in this situation because the black page border is also not drawn properly. I've found that a simple refresh of the web page typically corrects the problem.

Blogging code samples a tad more easily [Updated free ConvertClipboardRtfToHtmlText tool and source code!]

Kind readers gave some great feedback on my previous post of the ConvertClipboardRtfToHtmlText tool and source code. Accordingly, I have made three small tweaks to the tool:

  • The code to detect the start of the RTF text worked only if Visual Studio's font size was set to 8pt. That's what I use, but it's not the default, so this would cause problems for most people who tried the tool. The relevant code no longer looks for a specific font-size.
  • Tab characters in the RTF text were ignored, causing layout problems for code with tabs (vs. spaces). Tabs are now auto-expanded to the mostly-standard value of 4 spaces.
  • The use of the private Color class was unnecessary because it added nothing over System.Drawing.Color. System.Drawing.Color is now used to save a few lines of code.

The sample code and tool in the previous post have been updated with these changes, so please go there to get the latest version.

Improved access to Silverlight 2's generic.xaml resources [SilverlightDefaultStyleBrowser available via ClickOnce]

In response to my previous post announcing the SilverlightDefaultStyleBrowser tool for working with default styles for Silverlight 2 controls, I got an email from Rob Relyea suggesting that I make SilverlightDefaultStyleBrowser available via ClickOnce as well. (For those who may not be familiar with it, here's a brief ClickOnce overview.) ClickOnce is simple to configure, adds a Start Menu entry automatically, supports seamlessly updating an application, and installs/uninstalls on Vista as standard user without requiring elevation - so this was easy to do!

SilverlightDefaultStyleBrowser sample image

[Click here or on the image above to install SilverlightDefaultStyleBrowser with ClickOnce.]

The functionality of SilverlightDefaultStyleBrowser is the same whether you use the executable in the ZIP or use the ClickOnce version - so pick whatever suits you best. And have fun styling those controls! :)

Note: The source code for SilverlightDefaultStyleBrowser is available only in the ZIP file download.