简体   繁体   中英

Bind event in custom WPF control to command in ViewModel

I have a custom control that has an event. I have a window using that custom control. The window is bound to a viewmodel. I would like to have the event from the custom control direct to an ICommand on my viewmodel. I am obviously being dense here as I can't figure out how to do this. Any assistance is most welcome.

Thanks

If it's a one-off, you can use a simple event handler:

<some:CustomControl SuperDuper="OnSuperDuper" />

with code behind

private void OnSuperDuper(object sender, EventArgs e)
{
  _theCommand.Execute(null, (IInputElement)sender);
}

If you want to do this multiple times for a specific event I would use an attached property.

<some:CustomControl my:AttachedEvents.SuperDuperCommand="{Binding TheCommand}" />

where the attached property is simply:

// use propa snippet to build this quickly
public static ICommand GetSuperDuperCommand(DependencyObject obj) { return (ICommand)obj.GetValue(SuperDuperCommandProperty); }
public static void SetSuperDuperCommand(DependencyObject obj, ICommand value) { obj.SetValue(SuperDuperCommandProperty, value); }
public static readonly DependencyProperty SuperDuperCommandProperty = DependencyProperty.RegisterAttached("SuperDuperCommand", typeof(ICommand), typeof(AttachedEvents), new PropertyMetadata
{
  PropertyChangedCallback = (obj, e) =>
  {
    if(oldCommand==null) ((CustomControl)obj).SuperDuper += OnSuperDuper;
    if(newCommand==null) ((CustomControl)obj).SuperDuper -= OnSuperDuper;
  }
});

private void OnSuperDuper(object sender, EventArgs e)
{
  var control = (CustomControl)sender;
  GetSuperDuperCommand(sender).Execute(null, sender));
}

You may be able to further generalize this to map any event to any command using a MarkupExtension. Here's the idea:

<some:CustomControl SuperDuper="{lib:CommandWrapper {Binding TheCommand}}" />

The code is like this:

public class CommandWrapper : MarkupExtension
{
  public BindingBase CommandBinding { get; set; }

  public CommandWrapper() {}
  public CommandWrapper(BindingBase commandBinding) { CommandBinding = commandBinding; }

  public object ProvideValue(IServiceProvider serviceProvider)
  {
    return new EventHandler((obj, e) =>
    {
      // Evaluate CommandBinding against obj, fire command
    });
  }
}

You can flesh out the details. Note that instead of simply saying "new EventHandler" you may want to pass the actual event handler type into CommandWrapper and use reflection to construct the appropriate delegate.

I'm not completely sure the XAML parser will let you set an event using a MarkupExtension, so this last solution may not actually work this simply. But if not, it can be combined with an attached property like so:

<some:CustomControl lib:CommandWrapper.Add="{lib:CommandWrapper SuperDuper,{Binding TheCommand}}" />

This will definitely work: CommandWrapper.Add will receive the event name from the CommandWrapper created by the markup extension and can create the appropriate mapping.

If you want to route an event to a command you can use an attached property. I used this example to add command support to a ComboBox SelectionChanged event:

http://blog.fossmo.net/post/How-to-create-an-attached-property-in-WPF-using-a-ComboBox.aspx

Cheers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM