Udostępnij za pośrednictwem


Microcode: A Quick Trick to turn regular XML into Xaml

MSDN has tons of examples about XAML.  Nearly everything in WPF has an example in XAML, but not all of these XAML examples actually reference the namespace that is required for this Xaml to be loadable by WPF.  It's still valid XML, just not valid XAML.

Here's a quick example of what I mean.  I just copied and pasted this chunk from the MSDN topic on LinearGradientBrush:

<!-- This rectangle is painted with a diagonal linear gradient. -->
<Rectangle Width="200" Height="100">
  <Rectangle.Fill>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
      <GradientStop Color="Yellow" Offset="0.0" />
      <GradientStop Color="Red" Offset="0.25" />
      <GradientStop Color="Blue" Offset="0.75" />
      <GradientStop Color="LimeGreen" Offset="1.0" />
    </LinearGradientBrush>
  </Rectangle.Fill>
</Rectangle>

Now if I try to simply load that up as XAML, XamlReader barfs

$x  =[xml]'<!-- This rectangle is painted with a diagonal linear gradient. -->
<Rectangle Width="200" Height="100">
  <Rectangle.Fill>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
      <GradientStop Color="Yellow" Offset="0.0" />
      <GradientStop Color="Red" Offset="0.25" />
      <GradientStop Color="Blue" Offset="0.75" />
      <GradientStop Color="LimeGreen" Offset="1.0" />
    </LinearGradientBrush>
  </Rectangle.Fill>
</Rectangle>
'

[Windows.Markup.XamlReader]::Parse($x.OuterXml)

Exception calling "Parse" with "1" argument(s): "'' XML namespace prefix does not map to a namespace URI, so cannot resolve property 'Width'. Line '1' Position '79'."
At line:14 char:35
+ [Windows.Markup.XamlReader]::Parse <<<< ($x.OuterXml)
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

 

Obviously, that's not ideal.  Luckily, with CTP3, there's a cmdlet called Select-Xml, which will allow me to query a document with xPath, regardless of namespaces.

This means that I can check to see if the fist element has a reference to the WPF namespace, and add it if it's not there.

$t = $x |
    Select-Xml //* |
    Select-Object -First 1 |
    Foreach-Object {
        if (-not $_.Node.xmlns) {
            $_.Node.SetAttribute("xmlns", 'https://schemas.microsoft.com/winfx/2006/xaml/presentation')
        }
        $_.Node.OuterXml
    }

I'll stop and explain this pipeline a little.  $x is the XML, I pipe it into Select-Xml, with the xPath parameter of //*.  //* roughly translates to any node.  I pick out the first node (with Select-Object -first 1) and then I check to see if the Node contains an xmlns attribute.  If it doesn't, I add it.  Either way, I emit the first node's OuterXml, which basically means I emit the entire XML chunk as text.  That's what $t is when I am done.

Then I use this chunk to display it:

$w = New-Object Windows.Window
$w.Content = [Windows.Markup.XamlReader]::Parse($t)
$w.SizeToContent = "WidthAndHeight"
$w.ShowDialog()

Now I have the MSDN sample working, regardless of if it had an xmlns attribute in the first place.

Hope this Helps,

James Brundage [MSFT]