Tuesday, October 21, 2008

[Silverlight, C#] Adventures in Accessing Path Data

So I've been stumped for awhile on this, and it seems I was simply misunderstanding something about the Path Syntax used to describe path data, and as I discovered shortly after, there is a limitation in what Silverlight can parse.

Let's start at the beginning. I had a path object, that I took from an example, that looked like this:

[Path Name="shape_path"
Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000"
Width="92.5" Height="57.617"
Data="M137,122 C227,161 168.5,204.5 228.5,159.5" /]

I wanted, in my code behind, to access the Data property so that I could serialize the object into string whenever I wanted to. The problem I was having was that when I tried to access the Data by casting, I was getting an empty collection back.

Essentially PathGeometry g = (PathGeometry)path.Data was giving me an empty collection [Figures] and I had no way to get access to the data. I poked around the Silverlight forums and it seemed like other people were having this issue, and this used to work pre-Beta2, so there was some confusion.

Problem 1: My Misunderstanding of the Types of Path Markup Syntax

As it turns out though, MS never intended a PathGeometry to be accessed in that manner. And here's why [and what I had overlooked]. With Path Markup Syntax, there are actually 2 different types of paths:
StreamGeometry : You use the StreamGeometry mini-language when setting a property of type Geometry, such as the Clip property of a UIElement or the Data property of a Path element. The following example uses attribute syntax to create a StreamGeometry.
[Path Stroke="Black" Fill="Gray"
Data="M 10,100 C 10,300 300,-200 300,100" /]

PathFigureCollection : You use the PathFigureCollection mini-language when setting the Figures property of a PathGeometry. The following example uses a attribute syntax to create a PathFigureCollection for a PathGeometry.
[Path Stroke="Black" Fill="Gray"]
[Path.Data]
[PathGeometry Figures="M 10,100 C 10,300 300,-200 300,100" />]
[/Path.Data]
[/Path]
As you can see from the preceding examples, the two mini-languages are very similar. It's always possible to use a PathGeometry in any situation where you could use a StreamGeometry; so which one should you use? Use a StreamGeometry when you don't need to modify the path after creating it; use a PathGeometry if you do need to modify the path.
In my case, I actually needed to access the Figures collection, so I needed to use the longer notation. In my case, my error was that I had taken an example from somewhere, but it was not the correct syntax for what I ultimately wanted to do.

Now I'm not certain why there is a difference between these two types of paths since they seem so similar, although I imagine a little deeper reading into Geometries would tell me that.

Note: The following link is to documentation for WPF, which is where part of my confusion lay... since support in WPF is different then in Silverlight.
MSDN Documentation on Path Markup Syntax
The correct link is contained in the section "A Good Explanation" at the end of this post.


Problem 2: In Silverlight, Path.Figures Cannot be Parsed using the Mini-Lanugage Syntax

So yay right?!? This should be all fine and dandy! I change my XAML to use the correct Mini-Language as indicated in Problem 1, I go to build/run my application and BAM!

"Invalid attribute value M 10,100 C 10,300 300,-200 300,100 for property Figures. [Line: 5 Position: 27]".

With a little more searching I find a post indicating that:

The PathGeometry.Figures property does not support the path "mini-language" syntax in Silverlight. You have to use the verbose explicit markup to describe those figures in your XAML.

Now I just want to scream... after all that, Silverlight can't even PARSE the mini method of syntax and I need to make sure that the path I'm getting is using the long verbose method :(

Something like this:

[PathGeometry]
[PathGeometry.Figures]
[PathFigure]
[PathFigure.Segments]
[LineSegment Point="150,0"/]
[/PathFigure.Segments]
[/PathFigure]
[/PathGeometry.Figures]
[/PathGeometry]

Blech. How irritating, especially when many examples are given on MSDN in the mini syntax, and from what I've heard the WPF XAMLWriter also outputs the mini syntax.

Anyways, just a heads up, this seems to be the way it is.

Long story short: YOU HAVE TO BE VERBOSE WITH PATHGEOMETRY



A Good Explanation: What is actually supported in Silverlight, and how it works in WPF as well...

First, my hats off to all the people in the Silverlight community that help dummies like me out with issues on forums ^^. Big thanks to Wolf Schmidt (MSFT) who gave me this excellent explanation of what is going on:
PathGeometry.Figures does not support the Path minilanguage/syntax in Silverlight. However, Path.Data DOES support the Path minilanguage/syntax in Silverlight.

In Silverlight, if you use the minilanguage and set Path.Data, you get your intended path and its various parts rendered, but you do NOT get a run-time object tree representation of how that minilanguage was parsed such that you could walk into Figures, Segments, etc. You have to use the verbose form of markup by putting various PathFigure-type object elements under PathGeometry.Figures IF you want discrete run-time "parts" (and for best results, put some "Name" attributes on there so that you don't even need to walk, you can just use FindName).

BTW, in WPF, you also do not get object-tree representation of a path created via the mini-language. The only delta is that Silverlight only supports parsing/processing of the minilanguage on Path.Data whereas WPF supports it for either Path.Data OR PathGeometry.Figures.

PS: the MSDN article you linked two posts above is specifically the WPF version of the Path Markup Syntax information. You need to look at http://msdn.microsoft.com/en-us/library/cc189041(VS.95).aspx which is the Silverlight version. In general, you do have to keep an eye on whether you are looking at a WPF topic or a Silverlight topic when you look at MSDN, because many cases the TOC organization/titling etc. is similar between WPF and Silverlight but the information on the specific pages is often quite different because of the inherent technology differences.

My Silverlight Forum Post

.

6 comments:

Cory said...

I'm trying to do a similar thing but having no luck. I'm trying to build a minilanguage string in C# which I can then use to update a Path but I can't find any Path property that will take a string. Any ideas or am I barking up the wrong tree?

yamroll said...

Hey Cory,

I found some threads on the Silverlight forums regarding the exact same issue you are having.

* http://silverlight.net/forums/p/17762/61681.aspx#61681

* http://silverlight.net/forums/p/10326/32881.aspx#32881]

Much like I expected, I'm fairly certain that in Silverlight Beta 2 and up there is no way to directly set/get the path data with a string [Path Markup Syntax], however, from looking at those forum posts it seems that the majority of people construct the entire path object as a string and then use XAMLReader.Load to load it all up - including the path data string.

So something similar to this:

string pathXaml = "[Path xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Data=\"M 50,50 L 50,100 L 100,100 L 100,50 L 50,50 Z\"/]";

Path path = (Path)System.Windows.Markup.XamlReader.Load(pathXaml);

Note that you must add the namespacing to the Path object [xlmns and xlmns:x as XAMLReader requires it post 1.1].

This should allow you to set a path object using the path data syntax string, however, I still do not know a way that we can *get* it [which is what my problem is!].

Hope this helps!

yamroll said...

Note: XAMLReader can be found by using System.Windows.Markup :)

Cory said...

Thanks a bunch for the links. using XAMLReader is certainly an interesting approach that I hadn't even thought of. My first stab at it was going to be to just build up the PathGeometry using PathFigure objects, a less robust version of the full-on parser that hotdave2 put together. I assume there must be a good reason for Microsoft to pull out support for passing in the minilanguage string but it would have to be darn good reason to force every user who wanted the functionality to have to write their own minilanguage parser.

Richard said...

see here for a quick way to get silverlight to convert the path syntax for you! Just animate a single vertex of your path and poof!
Posting

yamroll said...

Thanks Richard, I'll copy this to my more current version of this blog as well :)