views:

127

answers:

1

Ok, I didn't want a bunch of ICommands in my MVVM ViewModels so I decided to create a MarkupExtension for WPF that you feed it a string(the name of the method), and it gives you back an ICommand that executes the method.

here's a snippet:

public class MethodCall : MarkupExtension
{
  public MethodCall(string methodName)
  {
     MethodName = methodName;
     CanExecute = "Can" + methodName;
  }

  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    Binding bin= new Binding { Converter = new MethodConverter(MethodName,CanExecute) };

    return bin.ProvideValue(serviceProvider);
  }
}

public class MethodConverter : IValueConverter
{
  string MethodName;
  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    //Convert to ICommand
    ICommand cmd = ConvertToICommand();
    if (cmd == null)
      Debug.WriteLine(string.Format("Could not bind to method 'MyMethod' on object",MethodName));
    return cmd;      
  }
}

It works great, except when the binding fails(e.g. you mistype).

When you do this in xaml: {Binding MyPropertyName} you see in the output window whenever the binding fails. and it tells you the propertyName the Type name etc.

The MethodConverter Class can tell you the name of the method that failed, but it can't tell you the source object type. Because the value will be null.

I can't figure out how to store the source object type so for the following class

public class MyClass
{
  public void MyMethod()
  {
  }
}

and the following xaml:

<Button Command={d:MethodCall MyMethod}>My Method</Button>

It currently says:

"Could not bind to method 'MyMethod' on object

but I would like it to say:

"Could not bind to method 'MyMethod' on object MyClass

Any ideas?

A: 

Hmm, well I can think of a roundabout way. There may be something easier/cleaner, but try this:

  1. Get the IProvideValueTarget implementation via the IServiceProvider.
  2. Use the target information to resolve a BindingExpression for the bound property.
  3. Use the DataItem property on the BindingExpression to get the source object.

Something like:

var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var bindingExpression = BindingOperations.GetBindingExpression(provideValueTarget.TargetObject, (DependencyProperty)provideValueTarget.TargetProperty);
var source = bindingExpression.DataItem;

Awful, but it should work. There may well be a service that does this for you, but I couldn't find one after a quick look in MSDN.

HTH,
Kent

Kent Boogaart