Using a Design Time Adorner to Setup Bindings Between Controls

[One warning -- the code to set the binding via the Editing Object Model will not work in VS 2008 beta 2 but will work in RTM] 

Recently I wrote a post about writing Design Time Adorners. I had a good question come up on our Cider forum and I thought I would answer that question in the form of a post and sample which will illustrate how to accomplish much of what the person who made the post was trying to do.

The question was essentially: How can I use design time adorners to make it easy to setup bindings between controls?

I've updated my sample from my post on writing Design Time Adorners to not only set the Background of my SimpleCustomControl but also to set the Background of all of the siblings of my control to bind to the Background of my control -- as long as my SimpleCustomControl has a name and the Background property of the sibling control is not set.

I piggy backed on the adorner that is already there so this post is really about using the Editing Object Model than adorners per se as the Editing Object Model is used in many situations, not just with adorners.

How the design time works

1) I start with my SimpleCustomControl in a Grid with 3 Buttons and 1 label as siblings.  Notice that one of the Buttons has it's Background set.

2) I click on the Adorner for the SimpleCustomControl (the paint can that appears when the SimpleCustomControl is selected) and select a color.

3) Notice how the Background for the siblings that didn't have the Background property set is now set to a binding to the Background of my SimpleCustomControl.

So how did I get this to work?

Implementation

As mentioned above, I added all of the functionality to the existing handler for when the adorner gets clicked -- inside of _ColorsListControl_ClosePopup():

void _ColorsListControl_ClosePopup(object sender, RoutedEventArgs e)
{
     (. . .)
}

The first step is to get at the ModelItems for the siblings of the SimpleCustomControl.  I get at them by going to the parent of the SimpleCustomControl (the Grid) and then asking for all of the Grids children. 

            ModelItem parentModelItem = adornedElementModelItem.Parent;
            ModelProperty siblingsProperty = parentModelItem.Properties["Children"];
            ModelItemCollection siblings = siblingsProperty.Collection;

The next step is to enumerate through those ModelItems and if the Background property is not set, set it to a binding:

                foreach (ModelItem mi in siblings)
                {
                    if (mi != adornedElementModelItem && !mi.Properties[Control.BackgroundProperty].IsSet)
                    {
                            (. . .) // set to a Binding
                    }
                }

Setting the Background to a Binding is done by creating a Binding instance, and setting the Background property of the sibling to it.  Then we setup the Binding by setting both the ElementName and Path properties.

                        using (var scope = mi.BeginEdit())
                        {
                            ModelItem binding = ModelFactory.CreateItem(Context, typeof(Binding));
                            mi.Properties[Control.BackgroundProperty].SetValue(binding);
                            binding.Properties["ElementName"].SetValue(adornedElementModelItem.Properties["Name"].Value);
                            binding.Properties["Path"].SetValue(new PropertyPath("Background"));

                            scope.Complete();
                        }

And that's it -- one caveat is that as all my posts, this is quick and dirty code to illustrate a concept and this code may have to be tweaked to handle all of the various situations that can arise in the usage of the SimpleCustomControl.

The code for this sample is attached to this blog post.

DesignTimeAdornerSample.zip