Console-Type Output in XAML?
March 18, 2006
Sunny NYC
I wanted to write a little standalone XAML file that displayed several static properties from the System.Environment class. Basically I needed some way — some simple way, I hoped — to intersperse text and objects and line breaks, where the objects are rendered with their ToString methods. (For example, Environment.OSVersion is an object of type OperatingSystem, which its ToString method formats it into a readable string.)
In other words, I wanted to mimic Console.WriteLine in XAML.
What seemed to make most sense was to do it as a TextBlock where each piece is a separate Inline object of some sort, and the LineBreak element terminates each line. However, sometimes arbitrary objects are converted into text in a TextBlock and sometimes they are not.
For example, this does not work:
-
<TextBlock Text="{x:Static s:Environment.OSVersion}" />
The exception message says "value not valid for property 'Text'." However, this one works:
-
<TextBlock Text="{Binding Source={x:Static s:Environment.OSVersion}}" />
I was greatly encouraged by that because I figured if I could assign the Text property of a TextBlock to a binding that references an arbitrary object, I could also assign the Text property of a Run to the same object. Alas, it just ain't so. This does not work:
-
<TextBlock>
<Run Text="{Binding Source={x:Static s:Environment.OSVersion}}" />
</TextBlock>
The exception message is "Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.String'," which doesn't make that much sense to me.
Addendum March 20, 2006: The Text property of TextBlock is backed by the dependency property TextProperty. The Text property of Run is not. Binding targets must be dependency properties. That's why it works for TextBlock and not Run.
It quickly became evident that if I didn't want to introduce a Converter, I'd need something more amenable to dealing with arbitrary objects and using their ToString methods to display them as text. I needed a ContentControl, such as Label, where the Content property is set to the x:Static markup extension expression. Fortunately, Label can be a valid member of the Inlines collection of TextBlock because it gets wrapped in an InlineUIContainer element. The Label elements could then be intermixed with LineBreak elements. The XAML file I came up with can be run or downloaded from here and is listed here:
-
<!-- ==================================================
EnvironmentInfo.xaml (c) 2006 by Charles Petzold
================================================== -->
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<TextBlock>
<Label Content="Operating System Version: " />
<Label Content="{x:Static s:Environment.OSVersion}" />
<LineBreak />
<Label Content=".NET Version: " />
<Label Content="{x:Static s:Environment.Version}" />
<LineBreak />
<Label Content="Machine Name: " />
<Label Content="{x:Static s:Environment.MachineName}" />
<LineBreak />
<Label Content="User Name: " />
<Label Content="{x:Static s:Environment.UserName}" />
<LineBreak />
<Label Content="User Domain Name: " />
<Label Content="{x:Static s:Environment.UserDomainName}" />
<LineBreak />
<Label Content="System Directory: " />
<Label Content="{x:Static s:Environment.SystemDirectory}" />
<LineBreak />
<Label Content="Current Directory: " />
<Label Content="{x:Static s:Environment.CurrentDirectory}" />
<LineBreak />
<Label Content="Command Line: " />
<Label Content="{x:Static s:Environment.CommandLine}" />
</TextBlock>
</StackPanel>
The version shown here is different from the version accessed by the link. It turns out that most of these items — all except the first two, in fact — can't be accessed with the security permissions allowed loose XAML files when run under IE, so the version stored on my site has the forbidden items commented out. You can run the director's cut (the listing above) under XAML Cruncher or XamlPad or an equivalent.
The Label controls are rendered considerably larger than their context, which puts a lot of air around the items. To get a tighter line spacing, you can alternatively use ContentControl or UserControl (which is the same thing). However, it doesn't appear possible to substitute Run element for the controls that just display literal text. The problem is aesthetic: The two pieces of side-by-side text just don't line up right.