Samuel
Samuel

Reputation: 6490

Is it possible to bind a command to a resource?

I have a view model that provides a RelayCommand LoadImage.

Typically I would use a button and bind the command to this button.

However I would like to call the LoadImage command from view's codebehind (I need to do some view related stuff that must not be put into view model)

The one way I am aware is to create an event handler for the button, e.g. Button_Click. In Button_Click I would cast DataContext to the corresponding ViewModel and use this instance to call (DataContext as MyViewModel).LoadImage.Execute(...)

This is odd as I need to know the view model.

What I am trying, is to bind LoadImage not to a button but to a resource in the view, so the Button_Click event just need to call FindResource with a given name and cast it to ICommand without the necessity to know the specific ViewModel.

Is this possible? The command itself is not static as it needs to know the context in what it is called.

Upvotes: 1

Views: 120

Answers (2)

Samuel
Samuel

Reputation: 6490

Based on Bill Zhangs idea of behaviours I've created a generic version which is quite control agnostic and which allows to be reused. The required assembly is

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

I've created a Trigger action that passes the execution along to an event handler:

using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
using System;

namespace Misc
{
   public class CommandWithEventAction : TriggerAction<UIElement>
   {
      public event Func<object, object> Execute;

      public static DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandWithEventAction), null);
      public ICommand Command
      {
         get
         {
            return (ICommand)GetValue(CommandProperty);
         }
         set
         {
            SetValue(CommandProperty, value);
         }
      }


      public static DependencyProperty ParameterProperty = DependencyProperty.Register("Parameter", typeof(object), typeof(CommandWithEventAction), null);
      public object Parameter
      {
         get
         {
            return GetValue(ParameterProperty);
         }
         set
         {
            SetValue(ParameterProperty, value);

         }
      }

      protected override void Invoke(object parameter)
      {
         var result = Execute(Parameter);
         Command.Execute(result);
      }

   }
}

To avoid any logic in a custom behaviour this allows to hook up any event to an event callback followed by a command call.

XAML:

<Button>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <misc:CommandWithEventAction Command="{Binding LoadImageCommand}" Parameter="Custom data"  Execute="CommandWithEventAction_OnExecute"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    Execute
</Button>

This will pass the "Custom data" string boxed as object to a function called

CommandWithEventAction_OnExecute

its signature of Func<object,object> may use the parameter and need to return something that will then be boxed into object and passed to the LoadImageCommand

Upvotes: 0

Bill Zhang
Bill Zhang

Reputation: 1939

You can make it by creating a behavior, which requires Prism referred in your project:

public class LoadImageBehavior : Behavior<Button>
{
    public public static static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof (ICommand), typeof (LoadImageBehavior));

    public ICommand Command
    {
        get { return (ICommand) GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.Click += AssociatedObject_Click;
    }

    private void AssociatedObject_Click(object sender, RoutedEventArgs e)
    {
        //Logic...

        if(Command != null && Command.CanExecute(null))
            Command.Execute(null);

        //Logic...
    }
}

On Xaml:

    <Button>
        <i:Interaction.Behaviors>
            <Behaviors:LoadImageBehavior Command="{Binding LoadImageCommand}"/>
        </i:Interaction.Behaviors>
    </Button>

Upvotes: 1

Related Questions