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