0

I'm new to WPF and especially Commands, and I have task right now to build a RelayCommand for a button. I'm supposed to learn that I need to separate the logic from the UI. I just have 2 textboxes and a textBlock, the user writes the names in the boxes and clicks on a button to display them in the textblock. My task is to read about the RelayCommand and implement it, but I really don't understand how it works. I have an UpdateName method in my Logic.cs class, how do I use it in a RelayCommand? All I have is the RelayCommand.cs with the implemented ICommand Interface. This is the code I found online, but I really don't know what to put where.

public event EventHandler CanExecuteChanged
{
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
}
private Action methodToExecute;
private Func<bool> canExecuteEvaluator;
public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
{
    this.methodToExecute = methodToExecute;
    this.canExecuteEvaluator = canExecuteEvaluator;
}
public RelayCommand(Action methodToExecute)
    : this(methodToExecute, null)
{
}
public bool CanExecute(object parameter)
{
    if (this.canExecuteEvaluator == null)
    {
        return true;
    }
    else
    {
        bool result = this.canExecuteEvaluator.Invoke();
        return result;
    }
}
public void Execute(object parameter)
{
    this.methodToExecute.Invoke();
}
tweedledum11
  • 121
  • 9

2 Answers2

2

You don't put any logic in the RelayCommand itself.

I assume the view where the Button is, has itsDataContext set to the class in Logic.cs, so I assume Logic.cs contains the viewmodel. So in the viewmodel you add a new property:

public ICommand UpdateTextCommand { get; private set; }

In the viewmodel's constructor you initialize this command:

UpdateTextCommand = new RelayCommand(() => this.UpdateName(), null);

And in the view (the XAML) you bind the Button's Command property:

<Button Content="Click me to change the TextBlock" Command="{Binding UpdateTextCommand}" />

Of course I'm not familiar with the structure of your application, this binding might fail. But this is the general idea with commanding.

Update: The constructor is the method without a return type (not even void). Whenever you instantiate (new) a class that method runs.

For Logic it should be (if the class name is Logic):

public Logic()
{
    // Statements here
} 

For RelayCommand this is the constructor:

public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
Szabolcs Dézsi
  • 8,743
  • 21
  • 29
  • Yes, the DataContext is set to the Logic class. I added the property, but where exactly do I initialize the command? Where is the viewmodel's constructur, in the Logic.cs? Sorry, I'm really new to this. The Binding part I understand. – tweedledum11 Feb 03 '16 at 09:50
  • Ok got it, I initialized the command in the Logic contructor, by UpdateText() you mean my custom method UpdateName right? My Visual Studio made me change it to this: UpdateTextCommand = new RelayCommand(delegate(object obj) { UpdateName(); }, null); Why is that? The one before didn't work. – tweedledum11 Feb 03 '16 at 09:57
  • I like the way you call the constructor of the RelayCommand, but for what purpose do you need the the second parameter null? I've never seen it like that. – kaliba Feb 03 '16 at 10:03
  • 1
    Lambda expressions (the one with `=>`) were introduced in C# 3.0, it's highly unlikely you use older version than that. These three should be equivalent: 1) `var actionWithLambda = new Action(() => Console.WriteLine("Hello World"));` 2) `var actionWithDelegate = new Action(delegate { Console.WriteLine("Hello World"); });` 3) `var actionMethodRef = new Action(OnAction);` where `OnAction` is `private static void OnAction() { Console.WriteLine("Hello World"); }` – Szabolcs Dézsi Feb 03 '16 at 10:04
  • I've used Lambda expressions before, but I've never seen it being used like that. Thanks! Always nice to learn something new. – kaliba Feb 03 '16 at 10:06
  • I only used `null` because I saw the asker's `RelayCommand` implementation. I usually see `CanExecute` parameters as default parameters: `Func canExecuteEvaluator = null`. So the user of the `RelayCommand` doesn't have to explicitly pass `null` in. – Szabolcs Dézsi Feb 03 '16 at 10:07
  • It says that Delegate Action does not take 0 arguments. But it seems to work with the delegate, didn't know these would all be equivalent! Thank you very much for you help, I think to really understand why it works now I'll have to look at it some more, but it definitely works now, thanks! – tweedledum11 Feb 03 '16 at 10:08
  • If you want to encapsulate a method with 0 arguments, you would have to make an "Action" instead of an "Action". – kaliba Feb 03 '16 at 10:11
1

You need to implement your method you want to call in your ViewModel just as you did it with the CodeBehind-File before you started with MVVM.

Then you need to create an ICommand as property in your Viewmodel (for the Binding afterwards):

private RelayCommand relUpdateText;
public ICommand CUpdateTextCommand { get { return relUpdateText; } }

In your constructor (of your viewmodel) you need to create the RelayCommand object:

relUpdateText = new RelayCommand(OnUpdateText);

With OnUpdateText being your method you want to call.

Next you have to make a constructor with the right parameters. If your OnUpdateText looks like this:

private void OnUpdateText(string text){...}

Your RelayCommand constructor should look like that:

private Action<String> exec;
public RelayCommand(Action<String> exec)
{
   this.exec = exec;
}

As you see the Action need the same parameters as the method it encapsulates. At the end you should also check if the event isn't null:

public void Execute(object parameter)
{
    if(exec != null) exec(paramters as string);
}

If you got more parameters, you will have to use a Converter.

kaliba
  • 230
  • 2
  • 12