The blog of dlaa.me

Posts from June 2008

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