Welcome to WindowsClient.net | Sign in | Join

Rob Relyea - XAMLified

WPF, Silverlight and XAML

XAML node loop example: finding all named object instances

[Beta 1 of .NET 4 has released, and we’ve been doing a series of blog posts describing new XAML functionality.]

One of the things that most excites me about System.Xaml.dll is the flexibility that our programming model gives to people who need something extra from XAML.  You may already know that we've componentized XamlReader.Load() into a component that reads XML and produces XAML nodes (XamlXmlReader) and a component which takes XAML nodes and creates an object graph (XamlObjectWriter).

These readers and writers have base classes (System.Xaml.XamlReader and System.Xaml.XamlWriter) which define a model of XamlReaders which produce XAML nodes and XamlWriters which consume XAML nodes and do something interesting.  The model is similar to XmlReader and XmlWriter from System.Xml.dll.

So what is a XAML node?

We have 7 XamlNodeTypes:

  • StartObject - Signifies the start of an object instance in XAML.  This is an object element start tag or a markup extension (inside an attribute value).
  • EndObject - Signifies the end of an object instance in XAML.  This is an object element end tag or a markup extension close (inside an attribute value).
  • StartMember - Signifies the start of a member (property, event, attached property, attached event, or directive). These are represented in markup as attributes or property elements.
  • EndMember - Signifies the end of a member.
  • Value - Attribute values or text nodes in the XamlXmlReader's case.
  • Namespace - Signifies the definition of a namespace and prefix pair.  These come before the StartObject that they are specified on in XAML's XML syntax.
  • GetObject - Signifies the start of an object instance in XAML.  This occurs for r/o collections or r/w collections without an explicit collection element.

XamlReader and these nodes define the Data Model for XAML - which I like to call “Objects - Members - Values.”

Things you can do in a XAML node loop

Writing a XAML node loop can walk through the set of nodes that come from a XamlReader.  It can then choose to do one of many things:

  • evaluate the data, report results.  (for example: a validator)
  • pass the XAML nodes to a writer (like XamlObjectWriter).  (you could build XamlPad like this...perhaps you'd want to strip out x:Class and any events...better than XamlPads of today)
  • pass the XAML nodes to a writer and adding additional nodes.  The Workflow designer adds linenumber and lineoffset information as attached properties to enable better debugging at runtime.
  • more...

Example of finding all named object instances

I built an example that uses a XAML node loop to help answer a WPF forum question "How to get x:Name from 3DElements using XamlReader.Load".

By using XamlXmlReader and XamlObjectWriter, and keeping track of names and instances of objects, we’re able to solve the scenario.

[Will upload this sample to http://robrelyea.com soon…]

   1: //This .NET 4 Beta 1 example was inspired by the WPF forum question at:
   2: //  http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5c226430-c54d-45b8-a8a2-7e4a79e3692a
   3: //
   4:  
   5: /*
   6:  Console output of this example is:
   7:  XAML Contents:
   8: <Window
   9:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  10:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  11:     xmlns:local="clr-namespace:NodeLoopExample;assembly=NodeLoopExample"
  12:     >
  13:     <StackPanel>
  14:         <Button Name="button1">Ok</Button>
  15:         <Button x:Name="button2">No, it is not ok!</Button>
  16:         <Button>
  17:             <Button.Background>
  18:                 <LinearGradientBrush x:Name="brush">
  19:                     <GradientStop Color="AliceBlue" />
  20:                     <GradientStop Color="Red" Offset="1" />
  21:                 </LinearGradientBrush>
  22:             </Button.Background>
  23:             Button with color
  24:         </Button>
  25:     </StackPanel>
  26: </Window>
  27: 
  28: NamedObjects:
  29: Name: button1, Type: Button
  30: Name: button2, Type: Button
  31: Name: brush, Type: LinearGradientBrush
  32: --close the window to close the app--
  33: */
  34:  
  35: using System;
  36: using System.Collections.Generic;
  37: using System.Windows;
  38: using System.Xaml;
  39: using System.Xml;
  40: using System.IO;
  41:  
  42: namespace NodeLoopExample
  43: {
  44:     class Program
  45:     {
  46:         [STAThread]
  47:         static void Main(string[] args)
  48:         {
  49:             Console.WriteLine("XAML Contents:");
  50:             string xamlContents = File.ReadAllText("WindowWithNames.xaml");
  51:             Console.WriteLine(xamlContents);
  52:             Console.WriteLine();
  53:             
  54:             List<Frame> namedObjects = ShowObjectGraphAndHarvestNames("WindowWithNames.xaml");
  55:             Console.WriteLine("NamedObjects:");
  56:             foreach (Frame frame in namedObjects)
  57:             {
  58:                 Console.WriteLine("Name: {0}, Type: {1}", frame.Name, frame.Type.UnderlyingType.Name);
  59:             }
  60:  
  61:             Console.WriteLine("--close the window to close the app--");
  62:             Application app = new Application();
  63:             app.Run();
  64:         }
  65:  
  66:         //Let's loop through all the XAML nodes in this XAML file.
  67:         //We'll keep track of the tag name (Type), attributes or property elements (Member)
  68:         // names (Name) and instances (Instance) on a Frame class.
  69:         //We'll return all Frames with a name from this method.
  70:         //We also want to show the objectGraph created.
  71:         private static List<Frame> ShowObjectGraphAndHarvestNames(string fileName)
  72:         {
  73:             //we use a stack to keep track of names/instances
  74:             Stack<Frame> stack = new Stack<Frame>();
  75:  
  76:             XmlReader xr = XmlReader.Create(fileName);
  77:             XamlXmlReader reader = new XamlXmlReader(xr);
  78:             XamlObjectWriter writer = new XamlObjectWriter(reader.SchemaContext);
  79:  
  80:             //List of all frames which had names
  81:             List<Frame> namedObjects = new List<Frame>();
  82:  
  83:             while (reader.Read())
  84:             {
  85:                 writer.WriteNode(reader);
  86:  
  87:                 switch (reader.NodeType)
  88:                 {
  89:                     case XamlNodeType.StartObject:
  90:                         Frame newFrame = new Frame();
  91:                         newFrame.Type = reader.Type;
  92:                         stack.Push(newFrame);
  93:                         break;
  94:                     case XamlNodeType.GetObject:
  95:                         Frame newFrameForGet = new Frame();
  96:                         newFrameForGet.Type = stack.Peek().Member.Type;
  97:                         stack.Push(newFrameForGet);
  98:                         break;
  99:                     case XamlNodeType.StartMember:
 100:                         stack.Peek().Member = reader.Member;
 101:                         break;
 102:                     case XamlNodeType.EndMember:
 103:                         stack.Peek().Member = null;
 104:                         break;
 105:                     case XamlNodeType.EndObject:
 106:                         stack.Peek().Instance = writer.Result;
 107:                         if (stack.Peek().Name != null)
 108:                         {
 109:                             namedObjects.Add(stack.Peek());
 110:                         }
 111:                         stack.Pop();
 112:                         break;
 113:                     case XamlNodeType.Value:
 114:                         XamlMemberBase contentProperty = stack.Peek().Type.GetAliasedProperty(XamlLanguage.Name);
 115:                         if (stack.Peek().Member == contentProperty
 116:                             || stack.Peek().Member == XamlLanguage.Name)
 117:                         {
 118:                             stack.Peek().Name = reader.Value as string;
 119:                         }
 120:                         break;
 121:                 }
 122:             }
 123:  
 124:             object rootObject = writer.Result;
 125:             ShowInWindow(rootObject);
 126:             return namedObjects;
 127:         }
 128:  
 129:         private static void ShowInWindow(object root)
 130:         {
 131:             Window window = root as Window;
 132:             if (window == null)
 133:             {
 134:                 window = new Window();
 135:                 window.Content = root;
 136:             }
 137:             if (window.Title == "")
 138:             {
 139:                 window.Title = "NodeLoopExample";
 140:             }
 141:             window.Height = 400;
 142:             window.Width = 300;
 143:             window.Top = 400;
 144:             window.Left = 400;
 145:             window.Show();
 146:         }
 147:     }
 148:     class Frame
 149:     {
 150:         public XamlType Type { get; set; }
 151:         public XamlMemberBase Member { get; set; }
 152:         public string Name { get; set; }
 153:         public object Instance { get; set; }
 154:     }
 155: }
Published Monday, June 22, 2009 1:23 PM by Rob_Relyea

Comments

# Dew Drop &#8211; June 23, 2009 | Alvin Ashcraft's Morning Dew@ Tuesday, June 23, 2009 8:35 AM

Pingback from  Dew Drop &#8211; June 23, 2009 | Alvin Ashcraft's Morning Dew

# re: XAML node loop example: finding all named object instances@ Wednesday, June 24, 2009 5:16 AM

Very helpfull code!

Thank you!

# re: XAML node loop example: finding all named object instances@ Saturday, July 11, 2009 8:20 PM

I tried adapting your code here to workflow xaml, but I can't get it to simply read the xaml then write the xaml node by node.  The question on the forums is here if you want to take a look at it.

social.msdn.microsoft.com/.../a5053d11-5371-4883-91bc-11090a1b2692

Thanks in advance,

Bob

by Robert

# re: XAML node loop example: finding all named object instances@ Thursday, October 15, 2009 4:08 PM

Hi Rob, and thanks, as always for your insight and help.  In regards to the original post "How to get x:Name from 3DElements using XamlReader.Load", I like the .Net 4.0 solution, but it is not really something I can use at this point in time.  

So, in order for me to build a TreeView of my XAML 3D model, do I really have to resort to reading the XAML a second time, searching for x:Names using XML SelectNode?  Does not sound like the right way to go - some of my XAML model files are 300+MB...

You mentioned "One solution for this is to make your root element implement INameScope.  You can then iterate through the list, since you own it."  I'm not sure I understand how this would work.  My root element is the Window which contains the Viewport3D.  Are you suggesting to implement INameScope in the Window, and call INameScope.RegisterName to add each Visual3D elements to the Window's namescope?  And that would somehow allow me to get the Visual3D element's name?

How does Expression Blend display the tree (including the element names) when a XAML 3D file is loaded?  Does it compile it?  Is there some way for me to compile my XAML file as I'm importing it, in order to get the field names??

Thanks for any insight.

E.

by Eeee Efff

# re: XAML node loop example: finding all named object instances@ Friday, October 16, 2009 12:50 PM

E-

I've replied on the forum thread with answers to your questions about INameScope...

Hope that solves it for you.

Thanks, Rob

# re: XAML node loop example: finding all named object instances@ Monday, October 19, 2009 8:30 PM

Thanks very much, Rob.  Always helpful.  I tried the suggestion you mentioned on the forum thread, and I have a couple of additional questions, if you can spare a few minutes.

Thanks again,

E.

by Eeee Efff

# XamlPadSample – Step 1@ Wednesday, January 20, 2010 12:15 PM

XamlPadSample step 1 sample code is now available for download from robrelyea.com/.../XamlPadSample

Leave a Comment

(required) 
(required) 
(optional)
(required) 
Page view counter