Action.SetTargetCore, odd behaviour?

Sep 17, 2010 at 12:49 PM


I think there something odd is going on when Caliburn.Micro is using a convention to bind a label to a string property on a ViewModel.


1. Create a view model and create a readonly, public, string property.
2. Write some code in the property getter that will return a valid string value.
3. Create a view and a label and give it the same name as the property on the view model.

Run the test.

If you trace through the code you'll eventually see that CM calls IoC.GetInstance in Action.SetTargetCore with the value of the string property (Action.cs, line 96). Is this correct for labels? If so, why?

 87 private static void SetTargetCore(DependencyPropertyChangedEventArgs e, DependencyObject d, bool setContext)
 88 {
 89     if (Bootstrapper.IsInDesignMode || e.NewValue == e.OldValue || e.NewValue == null)
 90         return;
 92     var target = e.NewValue;
 93     var containerKey = e.NewValue as string;
 95     if (containerKey != null)
 96         target = IoC.GetInstance(null, containerKey);
 98     if (setContext && d is FrameworkElement)
 99     {
100         Log.Info("Setting DC of {0} to {1}.", d, target);
101         ((FrameworkElement)d).DataContext = target;
102     }
104     Log.Info("Attaching message handler {0} to {1}.", target, d);
105     d.SetValue(Message.HandlerProperty, target);
106 }

In my case, the correct value for the property is in the target variable.  From what I can see, any e.NewValue that is a string is always going to trigger the call to IoC.GetInstance which will update target. For me, it fails; I'm using Unity 2.0 and unity won't accept a null type argument. I'm pretty new to all this so, its very probable that I'm not understanding something here. However, I've played around with my override of BootStrapper.GetInstance and I don't seem to be able to return anything that satisfies SetTargetCore. Here is my GetInstance Method:

protected override object GetInstance(Type serviceType, string key) {
    var result  = unity.Resolve(serviceType, key);

    if (result == null)
        throw new Exception(string.Format("Could not locate any instances of contract {0}", serviceType));

    return result;

What do I need to do get labels to bind using Convention over configuration?

I should add that if I use a data binding to my string property on the label content then, everything works as expected. I'm also using the latest check out from Mercurial: cb1494c16246


Barry Carr

Sep 17, 2010 at 2:17 PM

Barry, are you using the "latest" version of Caliburn.Micro? If this is WPF, then I think this issue has been fixed. If it is Silverlight, the reason is that there is no convention for the Label control (because its not an OOTB control), so it is falling back to using the convention for ContentControl, which has some special behavior related to hosting View Models. So, if Silverlight, add this line in your Boostrapper.Configure to fix the problem

ConventionManager.AddElementConvention<Label>(Label.ContentProperty, "Content", "DataContextChanged");

Sep 18, 2010 at 11:47 AM

Hi Rob,

Thanks for replying.

I am using WPF and I was using the latest build, well it was the latest build for me on Friday morning, British Summer Time. It was: cb1494c16246

I looked through the Mercurial history for Action.cs before I posted my original message and I couldn't see any recent changes to this file around SetTargetCore. However, IIRC, there were some changes to the XML documentation in this file.



Sep 18, 2010 at 1:15 PM

The big change was in ConventionManager. Can you send me a small sample solution that demonstrates your problem? That would help me get it fixed. Please send it to robertheisenberg at hotmail dot com