WPF, Silverlight and C# 3.0 object initializers

XAML is definitely the way to go whenever possible when you're writing WPF and Silverlight apps, due to its amenability to tooling, analyzability, side-effect-free-ness, etc.  However, there are occasionally (or often, depending on what you're doing) times when you need to construct WPF and Silverlight objects via code, and pure XAML expression doesn't do the trick.  A common example is when you're in a programmatic loop, and you need to generate an object on each iteration of the loop; or when you have something dynamically varying at runtime and you create based on that.  Some of those situations may still work with XAML and databinding, but there are many times where you just want to back off to writing it straight in code.

Since XAML is really an object-initialization-and-property-setting language, consider how we write the following XAML in code:

      <Canvas>

            <Rectangle StrokeThickness="30" RadiusX="97.5" RadiusY="97.5" Width="396" Height="312" >

                  <Rectangle.Fill>

                        <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">

                              <GradientStop Color="White" Offset="0"/>

                              <GradientStop Color="Blue" Offset="0.5"/>

                              <GradientStop Color="Black" Offset="1"/>

                        </LinearGradientBrush>

                  </Rectangle.Fill>

                  <Rectangle.Stroke>

                        <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">

                              <GradientStop Color="Black" Offset="0"/>

<GradientStop Color="Red" Offset="0.5"/>

                  <GradientStop Color="White" Offset="1"/>

                        </LinearGradientBrush>

                  </Rectangle.Stroke>

            </Rectangle>

            <TextBlock Width="172" Height="80" Canvas.Left="109" Canvas.Top="109" TextWrapping="Wrap"

            FontFamily="Baskerville Old Face" FontSize="72" Foreground="Orange">Hello

            </TextBlock>

      </Canvas>

The canonical means for writing something like this in code is to go from the inside out, creating objects and store them in local variables, and then assigning them into the containing objects.  So, the above would be constructed like this in C#:

        public UIElement CreateMyObject(double cornerRadius, double strokeThickness,

                                        string textString)

        {

            LinearGradientBrush fillBrush = new LinearGradientBrush();

            fillBrush.StartPoint = new Point(0, 0.5);

            fillBrush.EndPoint = new Point(1, 0.5);

            fillBrush.GradientStops.Add(new GradientStop(Colors.White, 0.0));

            fillBrush.GradientStops.Add(new GradientStop(Colors.Blue, 0.5));

            fillBrush.GradientStops.Add(new GradientStop(Colors.Black, 1.0));

            LinearGradientBrush strokeBrush = new LinearGradientBrush();

            strokeBrush.StartPoint = new Point(0, 0.5);

            strokeBrush.EndPoint = new Point(1, 0.5);

            strokeBrush.GradientStops.Add(new GradientStop(Colors.Black, 0.0));

            strokeBrush.GradientStops.Add(new GradientStop(Colors.Red, 0.5));

            strokeBrush.GradientStops.Add(new GradientStop(Colors.White, 1.0));

            Rectangle r = new Rectangle();

            r.Height = 312;

            r.Width = 396;

            r.Fill = fillBrush;

            r.Stroke = strokeBrush;

            r.StrokeThickness = strokeThickness;

            r.RadiusX = cornerRadius;

            r.RadiusY = cornerRadius;

            TextBlock t = new TextBlock();

            t.TextWrapping = TextWrapping.Wrap;

            t.FontFamily = new FontFamily("Baskerville Old Face");

            t.FontSize = 72;

            t.Foreground = Brushes.Orange;

            t.Text = textString;

            t.SetValue(Canvas.LeftProperty, 109.0);

            t.SetValue(Canvas.TopProperty, 109.0);

            Canvas c = new Canvas();

            c.Children.Add(r);

            c.Children.Add(t);

            return c;

        }

This certainly works, but it has the downside of taking something that's fundamentally declarative, and adding assignment to it solely because we need to refer to the objects we just created as we assign its properties.  This approach also encourages the inside-out construction of objects, making it harder to see the forest through the trees.

Enter C# 3.0 (coming in the "Orcas" release), with its "object initializer" feature.  Object initializers are particularly good for the sorts of objects that get built up in WPF and Silverlight -- namely objects that are created and then have properties assigned.  Not coincidentally, this is precisely the pattern used for XAML-exposed objects as well.

Here's a canonical example of construction of a Point without and with object initializers (not using any parameterized constructors):

Without:

        Point pt = new Point();

        pt.X = 3.0;

        pt.Y = 4.0;

With:

        Point pt = new Point() { X = 3.0, Y = 4.0 };

Now we can take our C# and convert it to use object initializers, resulting in this:

        public UIElement CreateMyObject(double cornerRadius, double strokeThickness,

                                        string textString)

        {

            return new Canvas()

               {

                   Children =

                   {

                       new Rectangle()

    {

                           Height = 312,

                           Width = 396,

                           Fill = new LinearGradientBrush()

                                  {

                                      StartPoint = new Point(0,0.5),

                                      EndPoint = new Point(1, 0.5),

                                      GradientStops =

                                      {

                                          new GradientStop(Colors.White, 0),

  new GradientStop(Colors.Blue, 0.5),

                                          new GradientStop(Colors.Black, 1),

                                      }

                                  },

                           Stroke = new LinearGradientBrush()

                                  {

                                      StartPoint = new Point(0,0.5),

                                      EndPoint = new Point(1, 0.5),

                     GradientStops =

                                      {

                                          new GradientStop(Colors.Black, 0),

                                          new GradientStop(Colors.Red, 0.5),

                            new GradientStop(Colors.White, 1),

                                      }

                                  },

                           StrokeThickness = strokeThickness,

                           RadiusX = cornerRadius,

                RadiusY = cornerRadius

                       },

                       new TextBlock()

                       {

                           TextWrapping = TextWrapping.Wrap,

                           FontFamily = new FontFamily("Baskerville Old Face"),

                           FontSize = 72,

                           Foreground = Brushes.Orange,

                           Text = textString,

                       }

                   }

               };

        }

Here, objects can be created outside-in, there's no possibility of creating unwanted "side-effects" here by needing to deal with temporary local variables.  Finally, C# Intellisense in Visual Studio helps out even more in object initializer expressions -- by only displaying properties that can be set (no methods), and by not displaying properties that have already been set in the expression.  So for instance when I'm typing FontSize in the above, Intellisense won't prompt with FontFamily, since this has already been set the line before.

There is one major problem with the object-initializer version I show above: it doesn't deal with the attached properties (Canvas.Top and Canvas.Left) that both the XAML and the first C# example have.  C# doesn't have direct language support for attached properties, so this is a situation where we do need to have a compromise approach -- using object initializers to create as large a chunk as possible, then setting attached properties imperatively, then using the results in another object initializer expression.  The following takes that approach as it creates a TextBlock with object initialization syntax, sets some attached properties, and then uses the result in another object initializer expression.

        public UIElement CreateMyObject(double cornerRadius, double strokeThickness,

                                        string textString)

        {

            TextBlock textBlock = new TextBlock()

                       {

                           TextWrapping = TextWrapping.Wrap,

                           FontFamily = new FontFamily("Baskerville Old Face"),

                           FontSize = 72,

                           Foreground = Brushes.Orange,

                           Text = textString,

                       };

    textBlock.SetValue(Canvas.LeftProperty, 109.0);

            textBlock.SetValue(Canvas.TopProperty, 109.0);

            return new Canvas()

               {

                   Children =

                   {

                       new Rectangle()

                       {

                           Height = 312,

                           Width = 396,

                           Fill = new LinearGradientBrush()

                                  {

                                      StartPoint = new Point(0,0.5),

                                      EndPoint = new Point(1, 0.5),

                                      GradientStops =

                                      {

                                          new GradientStop(Colors.White, 0),

  new GradientStop(Colors.Blue, 0.5),

                                          new GradientStop(Colors.Black, 1),

                                      }

                                  },

                         Stroke = new LinearGradientBrush()

                                  {

                                      StartPoint = new Point(0,0.5),

                                      EndPoint = new Point(1, 0.5),

                                      GradientStops =

                                      {

                                          new GradientStop(Colors.Black, 0),

                                          new GradientStop(Colors.Red, 0.5),

                                          new GradientStop(Colors.White, 1),

                                      }

                                  },

                           StrokeThickness = strokeThickness,

                           RadiusX = cornerRadius,

                           RadiusY = cornerRadius

                       },

                       textBlock

                   }

               };

All told, C# 3.0 object initializers make programmatic construction of WPF and Silverlight objects much more pleasant.