2

I am working through MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF and have hit a snag in the Command section. Specifically, it creates a command based on an Action<object> and Func<object, bool>. At a point where I'm supposed to 'build and run the application', I am instead getting compile errors.

The command stuff:

public class Command : ICommand
{
  private readonly Action<object> _execute;
  private readonly Func<object, bool> _canExecute;
  public Command(Action<object> execute, Func<object, bool> canExecute)
  {
    _execute = execute;
    _canExecute = canExecute;
  }
 public void Execute(object parameter)
  {
    _execute(parameter);
  }
  public bool CanExecute(object parameter)
  {
    return (_canExecute == null) || _canExecute(parameter);
  }
  ...
}

The method call stuff:

private Command _showDetailsCommand;
public Command ShowDetailsCommand
{
  get 
  { 
      return _showDetailsCommand 
             ?? (_showDetailsCommand 
                = new Command(ShowCustomerDetails, IsCustomerSelected));
   }
}
public void ShowCustomerDetails()
{
  if (!IsCustomerSelected()){
    throw new InvalidOperationException("Unable to display customer details. "
                + "No customer selected.");
  }
  CustomerDetailsTabViewModel customerDetailsViewModel = 
            GetCustomerDetailsTab(SelectedCustomerID);
  if (customerDetailsViewModel == null)
  {
    customerDetailsViewModel 
         = new CustomerDetailsTabViewModel
               (_dataProvider, 
                SelectedCustomerID);
    Tabs.Add(customerDetailsViewModel);
  }
  SetCurrentTab(customerDetailsViewModel);
}
private bool IsCustomerSelected()
{
  return !string.IsNullOrEmpty(SelectedCustomerID);
}

I get wavy blue lines under the new Command(ShowCustomerDetails, IsCustomerSelected)) bit with a 'The best overloaded match for Northwind.Application.Command.Command(System.Action<object>, System.Func<object, bool>) has some invalid arguments'.

When I try to compile, I get the above error, plus two messages:

Argument 1: Cannot convert from method group to System.Action<object>
Argument 2: Cannot convert from method group to System.Func<object, bool>

Now, I know a lot more about Actions and Funcs than I did yesterday, and can almost bypass the errrors by changing the command declaration to:

private readonly Action _execute;
private readonly Func<bool> _canExecute;

and doing similar throughout the code, but then I get an error saying I haven't implemented ICommand properly.

To save my forehead/the nearest wall, can somebody either tell me what I haven't done right so I can fix it, or that the given (book) code is leading me awry, so I can move on.

mcalex
  • 6,628
  • 5
  • 50
  • 80

3 Answers3

3

Well that's because

private bool IsCustomerSelected()
{
    return !string.IsNullOrEmpty(SelectedCustomerID);
}

is not of type Func<object, bool> but of Func<bool>. It should look like

private bool IsCustomerSelected(object param)
{
    return !string.IsNullOrEmpty(SelectedCustomerID);
}

You could use Predicate<object> instead , which is, at least for me, more clear, how the signature should look like.

DHN
  • 4,807
  • 3
  • 31
  • 45
  • This makes sense at first blush, and looks like the other way out compared to my attempts. So can I create two new methods that take objects to satisfy the Action<object> and Func<object, bool> and simply call the no-arg version of the method to get everything running? To answer my own comment. Yep. Working now. Many thanks. – mcalex Apr 24 '13 at 08:41
  • This change does not permit to run the code from the book and brings more problems than solutions – Gennady Vanin Геннадий Ванин Apr 24 '13 at 10:02
  • 1
    @ГеннадийВанинНовосибирск Well critizing without bringing arguments is as useful as sticking a hand into running fan. So give me some arguments and let's discuss. I'm aware that my solution may not be the finest, but I wanted to show him, why he has the problems not how he has to implement it correctly. As usual there are various ways to do it the right way. I'm leaving it to him, what's he is going to do with this knowledge. – DHN Apr 24 '13 at 11:26
  • @DHN, have you checked my answer (with "fan") before writing your prev comment? Anyway, check my [response to you](http://stackoverflow.com/a/16188383/200449) there – Gennady Vanin Геннадий Ванин Apr 24 '13 at 11:58
  • umm, @ГеннадийВанинНовосибирск. If you do as I suggested in comment #1, it works with the rest of the book's code. You **add** a method IsCustomerSelected(object), and in that method, return the call to the no-arg method. This allows the rest of the usages of IsCustomerSelected to work. As it stands, the book's own code doesn't permit it to run. – mcalex Apr 24 '13 at 13:21
2

It should be tip top after you substitute your Command class to the code below

public class Command : ICommand
{   //private readonly Action<object> _execute;
    private readonly Action _execute;

    //private readonly Func<object, bool> _canExecute;
    private readonly Func<bool> _canExecute;//instead of prev line 

    //public Command(Action<object> execute) 
    public Command(Action execute)//instead of prev line
      : this(execute, null)
    { }

    //public Command(Action<object> execute,
    public Command(Action execute,//instead of prev line 
    Func<bool> canExecute)//added instead of next line 
    //Func<object, bool> canExecute)
    {   _execute = execute;
        _canExecute = canExecute;
    }

    public void Execute(object parameter)
    {
        //_execute(parameter);
        _execute();//added instead of prev line 
    }
    public bool CanExecute(object parameter)
    {   return (_canExecute == null)
        //|| _canExecute(parameter);
        || _canExecute();//added instead of prev line 
    }
    public event EventHandler CanExecuteChanged = delegate { };
    public void RaiseCanExecuteChanged()
    {   CanExecuteChanged(this, new EventArgs());     }
}

Checked this on the code from the mentioned book (p.205, Chapter 5)

  • Vice R., Shujaat Siddiqi M. - MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF, PACKT Publishing, ISBN 978-1-84968-342-5, 2012

This is the error/typo in text of book

Really, in the accompanying code to this book which anyone can get from Download the support files for this book, the correct RelayCommand class is used instead of Command class in the text of the book, according to which I made changes above to Command class

BTW, it is absent in online errata to the book


Response to @DHN comment

Changing to IsCustomerSelected() method to IsCustomerSelected(object param) will bring

on ShowCustomerDetails in following snippet (it is after "The method call stuff:" in question):

private Command _showDetailsCommand;

public Command ShowDetailsCommand
{
    get
    {
        return _showDetailsCommand ??
                (_showDetailsCommand =
                    new Command
                        (
                           ShowCustomerDetails,//error
                           IsCustomerSelected
                         )
                );
    }
}

the error:

Expected a method with 'void ShowCustomerDetails(object)' signature

since ShowCustomerDetails() is:

public void ShowCustomerDetails()
{
  if (!IsCustomerSelected())
     throw new InvalidOperationException("Unable to display customer details. "
     + "No customer selected.");
}

and if to change the latter ShowCustomerDetails(object param) this brings more changes and more necessities to change the code with each sequential change.

Just run the code and try incorporate your change to see what it would invoke

Community
  • 1
  • 1
  • Ok you got me. I didn't see, that `ShowCustomerDetails()` doesn't have the right signature too. But perhaps the TO is a clever programmer and can adapt my partial solution, so that everything is correct. ;o) Well that I didn't refer to the book is IMHO not a problem, because I don't have it. But I don't see, why I should have. – DHN Apr 24 '13 at 12:11
  • I know that the next portion of the book deals with the `RelayCommand` and I am using the d/l code to help where necessary. But, I wanted to understand *why* it wasn't working. I also know that not every written piece of code works out-of-the-page, so I did ask if I should just ignore the issue and move on. Your code came up after I had already tried out DHN's solution and got it working - though I ain't no clever programmer - and due to the next book instruction being *delete Command.cs*, I haven't implemented it yet. I will try (& will upvote) if yours works, but I've got my solution. – mcalex Apr 24 '13 at 13:36
0

Action, Func is the way C# design for implementation of closure. Action means that you have closure with parameter object and return System.Void type. Func is the way to implement closure with parameter object and return System.Bool type.

For more detail about closure in C#, see The beauty of closure by Jon Skeet

cat916
  • 1,363
  • 10
  • 18