Today's blog entry is brought to you by the letters W, and X ...
For those of you that have been developing in the release versions of Microsoft Visual Studio, along with the Visual Studio 2005 Extensions for .NET Framework 3.0 (WCF & WPF), you have probably noticed that sometimes compiling XAML can result in quite a profusion of warnings. Sometimes these warnings are quite verbose.
Warning: The element 'StackPanel' in namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'
has invalid child element 'StaticResource' in namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'.
List of possible elements expected: 'StackPanel.CanHorizontallyScroll,
StackPanel.CanVerticallyScroll, StackPanel.ScrollOwner, StackPanel.Background,
StackPanel.IsItemsHost, StackPanel.Style, StackPanel.OverridesDefaultStyle,
Yet the XAML compiles and loads just fine.
Or, you might see "green squiggle" come up as you are editing your XAML, trying to helpfully suggest that you shouldn't put <StaticResource .../> into your <StackPanel> in the first place, wouldn't one of these other fine choices be more appropriate?
Say it with me now:
Generally, we are not in the business of telling developers to ignore Microsoft's own compiler warnings, particularly those that are not or practically cannot be excluded through the CSC command line, or where a behavior is built-in to an existing Microsoft design tool surface. So some eXtensive eXplanation is in order here.
Although XAML is an XML-based language, XAML is really pushing the envelope for quite a number of XML language features that XML developers have traditionally relied upon. One such language feature is using a schema for validation of elements, attributes, and element relationships in the xmlns namespace. WPF XAML provides the XAML language exposure for a fairly complex set of managed types, with a lot of successive base classes in the mix, some of which are abstract. Each derived class of any base class usually has a different list of child elements that its representation as a XAML element can contain, and abstract base types themselves can't be declared in XAML, yet their members surface on derived types. In an XSD schema format, there would have to be great numbers of complex types, sequences, and choices to account for all the possibilities. Similar issues apply for generating an accurate list of supported attributes. Finally, when you get to attribute values, all bets are off. It's all a string to XML, but XAML has elaborate processing rules for how strings are interpreted on a per-attribute basis, because strings often resolve to objects when parsed.
The WPF team made a valiant effort to tackle the XAML schema issues, using a combination of tools and iterative tweaking. The resultant schema drives the XAML Intellisense for WPF in the .NET3.0 RTM that you will currently see in Visual Studio. This is applicable when editing XAML through the VS XML editor with the WPF extensions installed. You can view this schema directly if you want, under [Program Files]\[Visual Studio]\XML\Schemas\XAMLPresentation2006.xsd. That single schema is over 3600K in size. Wow. Ironically, that means that the schema file is larger than the assembly (PresentationFramework) that contains most of the code supporting the XAML that the schema describes.
Most of the time, that schema and/or certain compiling behaviors for XAML are correct. But occasionally, you might see spurious compiler errors, or a not very helpful suggestion for fixing what isn't really a problem. What's happening is that you have quite likely reached the edge of what the combination of XML, the VS XML editor, the XSD format, and a lot of hard work, are capable of describing about XAML.
Sometimes, Intellisense might suggest something that flat out won't work, yet it will pass compilation. For instance, try the following markup:
In that blank line within <StackPanel>, Intellisense will give you a number of possible choices, including: <Window>. So, you stick <Window> in there for the heck of it, and compile, and wait for it to error. Except, it won't. This will compile fine. However, you'll get a runtime error when the XAML page is loaded.
"Whatever" might not be the appropriate word here. Perhaps a plaintive "waaaaaaa!"?
The explanation for this behavior requires talking about how XAML is processed as a page in an application. "Compiled" XAML really is not being compiled fully into a CLR object set when you build your application. Certain aspects of a page get object representations at that time, such that partial classes get joined and various other WPF trickery that's part of the build targets and the specific build components that WPF added to the general CSC and MSBUILD sets. But much of the XAML page content is just optimized to a binary XAML format, included in the application, and then loaded by the runtime execution engine when the page is accessed. In absence of a schema or something else heading it off, only the runtime execution environment really knows for certain that the Window object can't exist at that position in the content model and runtime object representation. This Window example is a blatant one, but there are plenty of more subtle object-relationship issues in WPF that a design-time XSD schema enforcement would not be very helpful for preventing. This is especially true once you involve deliberately late-binding concepts such as data binding and dynamic resource references. WPF and its XAML implementation have a design-time and run-time XAML behavior split because it is necessary for a variety of important architectural issues, such as referencing pages through URIs, using system resources, theming, and so on. It's just part of how the WPF application model works, but it can occasionally be frustrating to debug. By the way, improving the debugging experience around XAML is definitely on the table as a feature for upcoming WPF versions.
Even though you will occasionally have to wade through spurious warnings, or even runtime issues when debugging, having the Intellisense for XAML working is still totally worthwhile. It may not be 100% right, but it's probably 98%, or 99%. So don't even think of not installing the extensions. And if you haven't already: here ...
Just bear in mind that maybe, just maybe, you might occasionally know more about XAML than your computer does.
Let's talk some more about just how hard this problem really is to solve at the XML schema level. Perhaps that will give you some appreciation of just how hard it is to get from 99% right to 100% right when it comes to validating XAML.
There are a lot of language features in XAML that make schema-based Intellisense for the XML editing environment a headache to produce.
For starters, there is XAML property element syntax. To XML, an element such as <Window.Resources> is a discrete element entity. The dot in the middle is not a significant character to how XML thinks of that element. Sure, you can write a schema to enforce that any Window.* element is always a child of <Window>. But even once the schema enforces the relationship, XML doesn't really have a handle on what a property element represents. XML-based Intellisense doesn't know that the dot means you've just crossed the divide between a type, and a member of the type. There's no object that backs a property element, it is just an alternative syntax for expressing a property value. Typically property element syntax is used in places where attribute syntax won't work, but even more maddeningly for the schema there are plenty of cases where you really could choose either attribute syntax OR property element syntax (but of course you can't use both for the same property). And, just like lists of child elements and attributes, the class inheritance that underlies WPF XAML generates a load of extra work for the XSD to account for what could be a valid property element. (For this example, .Resources isn't even a Window property, it was defined by FrameworkElement.)
Another issue is that XAML has several deliberate streamlining conventions that are intended to encourage better readability in XAML, but must now be accounted for as schema special cases. Any given XAML element may have a single content property, and for that the property the otherwise necessary property element may be omitted. The XAML schemas also omit property element intermediates for content properties, and you'll see "green squiggle" if you happen to include property element tagging for a content property. The schema could have gone either way with this, but went with the "best practice" decision of encouraging the content syntax whenever it's possible. But, such a property element isn't illegal, and it's sometimes a matter of coding style whether you want the optional elements in your markup or not. Collections also have a streamlining whereby child elements of a collection are interpreted as items of a collection without an intervening "<*.Items>", and the object element of the collection type itself may be omitted. (For more information about the wonders of XAML syntax and how it is processed by readers/loaders/compilers/etc., see Xaml Syntax Terminology in the SDK.)
Then, there are attached properties (and attached events). Syntactically, attached properties CAN be placed on any element. But that does not mean that they SHOULD be placed on any given element. The usual scenarios for attached properties is that either other elements on the page/tree are actually the ones that process the value and thus also define the property in the strictest sense, or that something even beyond the current page (such as a system, service, or editor) interprets the attached property eventually. The schema can't anticipate an entire application or programming environment or really catalog what any given attached property really does; there are a lot of them. Thus, the schema pretty much says you can put any attached property anyplace. But practically speaking, only an element that is an immediate child of a <Canvas> has any business setting a Canvas.Left attached property. As you may have noticed, the attached property scoping issue alone clutters the schema quite a bit.
Are we done listing all the inadequacies of traditional XML schemas for representing XAML language features and allowable tagging? Sadly, no. There's still the big one. Let's remember today's sponsor: the letter X. What does the letter X in XAML stand for, kids? That's right, it stands for eXtensible. Do schemas enjoy having to account for the fact that any given derived custom class of <Button> such as <my:BigRedShinyButton> (when appropriately mapped) is a perfectly legitimate variant for <Button> when inserted into some other control's content model? No, schemas don't enjoy that, no sir.
Resultingly, you will see higher numbers of Whatever Warnings if your project includes XAML references to types you have defined, or to any other mapped namespace that is outside of the WPF XAML xmlns.
The WPF team and other groups around Microsoft that deal with XAML have been well aware of these problems. The good news is that there is a lot of eXciting stuff coming that will improve the experience of working with XAML in editors/designers. This stuff hinges on the basic assumption that XSD as a schema format just won't cut it, and something better needs to be created to help tools process XAML. Instead, Microsoft is working on a XAML language service that editors, designers, reflection tools and others can plug into. The XAML language service is akin to the language services provided for CLR supported languages such as C#, but will of course be much more complex because of having to report not only language features per se but also all the syntax rules, as well as provide support for custom types that are compatible with XAML rules. Some of the precursors to this work are part of "Cider", which is the codename for the part of the Visual Studio IDE specifically for working with WPF and related .NET 3.0 technologies. "Cider" is currently a part of the very latest WPF Extensions for Visual Studio, but upcoming versions are probably going to be packaged as part of the larger "Orcas" previews for Visual Studio. For more information about "Cider", see http://channel9.msdn.com/wiki/default.aspx/Cider.HomePage.
On behalf of the letters W and X, I wish you well. Feel free to Xpost :-)