I met Simon Ferquel this week at the 2010 MVP Summit. I gave a talk about XAML on Wednesday, and later that day blogged one of my samples from the talk: “XamlSchemaContext/XamlType/XamlMember – a command line example”.
He commented on that post with:
Hi Rob,
As I understand, creating a custom XamlSchemaContext could be a way to make Xaml content integrate with an IoC, isn't it?
I can imagine a scenario where you can then write something like :
<Foo>
<Foo.Bar><IBar /></Foo.Bar>
</Foo>
With IBar being an interface that is resolved by the IoC. Same thing could apply with types without default constructor whose dependencies could be injected by the IoC-enabled schema context.
If you confirm that it is doable, I'll make a sample of a Unity-enabled Xaml Schema Context.
by Simon Ferquel
Turns out that subclassing XamlSchemaContext and XamlType seems to have worked quite nicely. See Simon’s “[Xaml] IoC-enabled Xaml parser” The basic idea is the custom schema context returns a XamlType for IBar that says it knows how to be created. When the XamlObjectWriter calls it to create the IBar, it can find an appropriate IBar to return.
Other Approaches
Other people have accomplished Invesrion of Control other ways…
- John “Z-Bo” Zabroski uses a custom markup extension. (via comment)
- I’ll add more links as I find other approaches or comments mention them.
Today at MVP Summit 2010, one of the things I discussed with the MVPs was the concept of XamlSchema. To leverage my already done work, and to share with all of you, my XAML friends, I’m following up my talk with a post that goes into that area.
All XAML systems have a way that they figure out XamlSchema. [MS-XAML]’s section 3 is “XAML Schema Information set” which defines Schema, XamlType, and XamlMember. .NET 4’s XAML stack exposes a set of apis that closely mirror that information about types and members.
XamlSchemaContext
A XamlSchemaContext is the object that retrieves (and usually caches) type/member information for the purposes of understanding XAML behavior for Load or Save or other.
There are 3 interesting flavors of XamlSchemaContexts today:
- WPF optimized context: XamlReader.GetWpfSchemaContext() – These XamlTypes/XamlMembers are optimized to avoid reflection in many cases for type/member information and for actions like “New”, “Set”, “Get”, “Add”, etc…
- General purpose .NET context: new XamlSchemaContext() – These XamlTypes/XamlMembers are built on top of Reflection APIs for type/member information and for actions like “New”, “Set”, “Get”, “Add”, etc…
- Silverlight context: new SilverlightSchemaContext() //from XamlToolkit - code.msdn.com/xaml - These XamlTypes/XamlMembers are built on top of Reflection APIs for type/member information and for actions like “New”, “Set”, “Get”, “Add”, etc… (but they know that the Silverlight has a subset of “XamlTypeInformation attributes and interfaces” that .NET Framework has – and adapt. For example ContentPropertyAttribute in .NET 4 is in System.Xaml.dll, but it is in System.Windows.dll in Silverlight 2-4.)
Goal for XamlSchemaContext
Have one way for XAML systems to understand XamlSchema for types. Have one way for XAML systems to provide XamlSchema in other type systems (CLR, Silverlight, NativeCode, JavaScript, Java, etc…)
Some Scenarios for XamlSchemaContexts
Given a XamlSchemaContext, there are a few interesting things that you can do with it:
Code Sample
I’ve posted this project at http://robrelyea.com/demos/xamlSchemaExplorer
1: using System;
2: using System.Collections.Generic;
3: using System.ComponentModel;
4: using System.Windows.Markup;
5: using System.Xaml;
6: using System.Text;
7:
8: namespace XamlSchemaExplorer
9: {
10:
11: public class Program
12: {
13: static void Main(string[] args)
14: {
15: XamlSchemaContext schemaContext = new XamlSchemaContext();
16: XamlType xamlType = schemaContext.GetXamlType(typeof(SampleClass));
17:
18: StringBuilder sb = XamlUtilities.GetStringRepresentationOfXamlType(xamlType);
19: string output = sb.ToString();
20: Console.WriteLine(output);
21: }
22: }
23:
24: #region SampleClass class definition
25: [ContentProperty("Bar")]
26: public class SampleClass
27: {
28: [TypeConverter(typeof(BarConverter))]
29: public bool Bar { get; set; }
30:
31: public int Foo { get; set; }
32:
33: public string Baz { get; set; }
34:
35: private List<int> _integers;
36: public List<int> Integers
37: {
38: get
39: {
40: if (_integers == null)
41: {
42: _integers = new List<int>();
43: }
44: return _integers;
45: }
46: }
47: }
48: #endregion
49:
50: #region TypeConverters
51: public class BarConverter : TypeConverter
52: {
53:
54: }
55: #endregion
56:
57: public static class XamlUtilities
58: {
59: public static StringBuilder GetStringRepresentationOfXamlType(XamlType xamlType)
60: {
61: StringBuilder sb = new StringBuilder();
62:
63: sb.AppendLine(xamlType.Name);
64: XamlMember contentProperty = xamlType.ContentProperty;
65:
66: foreach (var xamlMember in xamlType.GetAllMembers())
67: {
68: sb.AppendFormat(" {0} {1} {2}\n",
69: xamlMember.Type.IsGeneric ? XamlUtilities.GetGenericName(xamlMember.Type) : xamlMember.Type.Name,
70: xamlMember.Name,
71: xamlMember.IsReadOnly ?
72: " { get; } " : " { get; set; }");
73: if (xamlMember.TypeConverter != xamlMember.Type.TypeConverter)
74: {
75: sb.AppendLine(" ** Converter is " + xamlMember.TypeConverter.Name);
76: }
77: if (xamlMember == contentProperty)
78: {
79: sb.AppendLine(" ** This is the ContentProperty");
80: }
81: }
82: return sb;
83: }
84:
85: public static string GetGenericName(XamlType xamlType)
86: {
87: string typeName = xamlType.Name;
88: typeName += "<";
89: int typeArgCount = 0;
90: foreach (var typeArg in xamlType.TypeArguments)
91: {
92: if (typeArgCount > 0)
93: {
94: typeName += ",";
95: }
96:
97: if (typeArg.IsGeneric)
98: {
99: typeName += GetGenericName(typeArg);
100: }
101: else
102: {
103: typeName += typeArg.Name;
104: }
105: }
106: typeName += ">";
107: return typeName;
108: }
109: }
110: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
This outputs the following to the command line:
1: SampleClass
2: Boolean Bar { get; set; }
3: ** Converter is BarConverter
4: ** This is the ContentProperty
5: Int32 Foo { get; set; }
6: String Baz { get; set; }
7: List<Int32> Integers { get; }
8:
9: Press any key to continue . . .
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Would love to understand your questions/issues
Please let us know what you think…
Interesting to see “From NetReflector to XAML” from Craig Sutherland detailing an exploration of using XAML for Config for CruiseControl.NET.
WPF’s XamlReader.Load vs System.Xaml’s XamlServices.Load()
CS: “although from what I understand WPF will still have its own implementation”
RR: WPF 4’s API XamlReader.Load’s goal is to be compatible, yet better, than the WPF 3 version of XamlReader.Load. This call does some special things for WPF (see Mike Shim’s “Use XamlReader.Load for WPF XAML (not XamlServices.Load)” for details. For Config scenarios, XamlServices.Load/Save would be ideal…that is what I do wth XamlPadSample as detailed in “Persistable View Models & XAML for Config”
Representing Time
Craig shows the use of a markupextension for Time: {Time 5} or {Time 30, Unit=Minutes}, etc…
A few options to consider (for Interval.Period, SubVersion.Timeout, Project.ModificationDelay, etc…) would be:
- Keep using the {Time} markupextension. Decent readability…
- Make those properties of type TimeSpan, which has a type converter that will convert to/from a string – see DoubleAnimation.Duration, for example:
<DoubleAnimation Storyboard.TargetName="MyAnimatedRectangle" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever" />
- Keep those property of type Int32, but use a “A Type Converter Declared on a Property” (see “Strings to Things (or How XAML interprets Attribute Values”) to introduce a TypeConverter than knows how to convert a string representing a time to integers. Similar approach allows UIElement.Height/.Width use “1in”, “96px”, etc…
I always enjoy understanding Don’s perspectives. This InfoQ interview is interesting as always. Touches on SOAP, XML, XML Schema, M, etc…
InfoQ: Don Box Discusses SOAP, XML, REST and M
Is XML as bad as Don is saying to you?
We can explore other ways to write down the XAML data model (O-M-V + TypeInfo)… John Gossman prototyped JAML (John’s Application Markup Language) the other week with .NET 4’s XAML stack.
if XML isn’t the right thing, we are not stuck with XML…
XAML has a major problem in v3 that we’ve addressed with a hotfix last year and .NET 4 includes the fix as well.
From KB 968227 article:
Consider the following scenario:
- You have a computer that uses the English (United States) regional settings.
- In the Regional and Language Options item in Control Panel, you customize the List separator character in Customize Regional Options dialog box .
- You create a Windows Presentation Foundation (WPF) application.
- The application creates a CultureInfo object for the English (United States) culture before WPF starts rendering.
When you start the application, an exception is thrown by the Extensible Application Markup Language (XAML) parser, and then the application crashes.
…