XAML Control Design

Silverlight LogoI've been digging into some of the open source and 3rd party controls that are becoming available for Silverlight 2. While running into some odd issues with some of them it occurred to me that there are some design guidelines that haven't been well communicated. Back in the early days of WPF I learned (though exactly where is unclear) that every control should support an empty constructor and that all properties (e.g. XAML Attributes) should have a default value. I knew this to be true but I couldn't document where it came from.

So as usual when I am stuck, I contacted Chris Sells as he was my mentor in early XAML usage. He was at MSDN at the time gathering content and helped me get the Data Binding articles as well into the Software Design Review for WPF (then Avalon). If anyone could help me figure out where I learned this, he'd know. He reminded me of the language they use internally: "Create-Set-Use". Essentially this means that the design pattern for controls is that they should work without requiring any properties:

<Grid ...>
  <Rectangle />
</Grid>

You can see that the Rectangle doesn't require any properties to be valid. Of course this Rectangle has no fill brush and no stroke brush which means it will likely not be visible.  But that's OK because it is valid XAML and doesn't break.  The XAML for a control doesn't have to read the mind of the user, but should behave (e.g. not throw exceptions). One of the most egregious was a control that threw an exception if I failed to set the Width and Height. Worse yet, when it did throw these exceptions, it didn't tell me what *all* the properties I needed therefore it was painful to use.  Width and Height are particularly problematic in this way in that by not defaulting to "Auto", showing the control in a non-Canvas container meant I needed to set "Auto" to the values which is what they should *always* be defaulted to.

Create-Set-Use happens during parsing of the XAML. This comes back to thinking of XAML as a serialization format for in-memory objects. XAML is simply a way to define what the structure of a particular document should be when its de-serialized. If you keep in mind that an Element becomes an object construction and that properties become SetValue calls, it makes it fairly clear what is happening during the parsing of the XAML itself.

As you write your own Silverlight or WPF classes that you expect to be used in XAML (this includes controls but also may be types that can be created as resources), try to keep these design ideas in mind. It will make life easier for your users.

Comments:

What is the meaning of the "Use" portion of Create-Set-Use? It seems like your blog post described the Argument Accumulator pattern (the Create-Set part), but did not cover precisely what Use meant.

Does Use simply mean that the test coverage for functionality of the control should be tested with all combinations of default properties and non-default properties? If so, then it appears automated testing is a large portion of Create-Set-Use. Doing so would help ensure that the control's functionality covers "fall back mechanisms".

Could you ask Chris Sells for clarification on what the Use portion means?

I haven't been able to ask Chris Sells yet but the nature of "Create-Set-Use" (though I've been informed of another name for this that has been used out of MS but I can't find it anymore).

The pattern is meant to describe that construction and setting of properties are two different steps (unlike "Create-Use"). This implies an empty construction and setting of values where every property has a valid default value. "Use" simply implies anything that you do *after* the object has been created and initial properties have been set.

I like the contrast you point out between Create-Set and Create-Use.

Maybe link Chris Sells here so we can get a direction explanation or a trackback to an explanation on his blog?

There are a lot of "patterns" dealing with object construction. XAML attributes are analogous to the Setter Injection pattern (without the syntactic weight of a SetXXX method). However, specifying an object's initial property values using an object serialization format like XAML also avoids the Run-On Initialization bug pattern, because construction and initialization are scoped together.

I find that understanding the benefits of an object serialization format is also a great helper in comparing the quality of design among XML languages, as well as the importance of being able to serialize your objects and de-serialize them in the same fashion. A lot of object builder DSLs don't do this, and so they aren't true serialization formats. What they do instead is equivalent to the "cut" operator in Prolog to eliminate backtracking. They not only distribute an object's setup responsibility across its client classes, but they also mutate the object's internal state in-between setup operations. A good example of this is Java Swing LayoutBuilder classes that serve as a facade to a LayoutManager. Once you do that and eliminate backtracking, you're done, you're finished, you can't serialize (as in a strongly isolated audit) how you built the object.

I hope that makes sense.

I think the pertinent idea here isn't what pattern or not is being used, but instead is that default values should always be valid. When you have a XAML object that requires that certain properties be set you are breaking the idea that default property values are valid.

One of my arguments for using WPF over other technologies (e.g. JavaFX Script or Flex) is that I want to take advantage of a technically superior solution, and to do that it is necessary to compare.

The main reason I found interest in this blog post is because I was looking for a way to explain how to use XAML correctly and how to write WPF code correctly. One way to justify superiority is to compare to something else, like JGoodies Forms.

In Matthew MacDonald's book, Pro WPF (1st edition), I saw him setting an attribute on a ValueConverter explicitly to "{x:Null}". So, you could say he wasn't following this Create-Set-Use idiom. It's a carry over mentality. A truism in programming is that idioms in one language or one API do not usually make for best practices in another.


 



 
Save Cancel