The name "CommandManager" does not exist in the current context (Visual Studio 2015)
Asked Answered
R

5

15

Trying to use the RelayCommand class below I received the error message: "The name "CommandManager" does not exist in the current context". According to this post Class library does not recognize CommandManager class I tried to solve the problem by adding an assembly called PresentationCore on "Add Reference". Unfortunately "No framework assemblies were found on the machine". I uninstalled Microsoft.NETCore.UniversalWindowsPlatform and reinstalled it as recommended in some posts and I repaired Visual Studio, but this doesn't change anything. Is this my failure or is it a bug? As a newbie I always tend to think I overlooked something;-)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MyApplication.Viewmodels
{
public class RelayCommand : ICommand
{
    private readonly Action<object> _Execute;
    private readonly Func<object, bool> _CanExecute;

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {

    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute", "Execute cannot be null.");
        }

        _Execute = execute;
        _CanExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _Execute(parameter);
    }

    public bool CanExecute(object parameter)
    {
        if (_CanExecute == null)
        {
            return true;
        }

        return _CanExecute(parameter);
    }
}
}
Randellrandene answered 25/1, 2016 at 15:26 Comment(0)
S
16

In the .NETCore exists no CommandManager. I have written my own interpretation because i was in the exact same situation as you are now. (Developing an UWP app with the MVVM design pattern)

RelayCommand.cs:

using System;
using System.Windows.Input;

namespace MVVMBoostUniversalWindowsApp
{
    public class RelayCommand : ICommand
    {
        private readonly Action execute;
        private readonly Func<bool> canExecute;

        public RelayCommand(Action execute)
            : this(execute, null)
        { }

        public RelayCommand(Action execute, Func<bool> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute is null.");

            this.execute = execute;
            this.canExecute = canExecute;
            this.RaiseCanExecuteChangedAction = RaiseCanExecuteChanged;
            SimpleCommandManager.AddRaiseCanExecuteChangedAction(ref RaiseCanExecuteChangedAction);
        }

        ~RelayCommand()
        {
            RemoveCommand();
        }

        public void RemoveCommand()
        {
            SimpleCommandManager.RemoveRaiseCanExecuteChangedAction(RaiseCanExecuteChangedAction);
        }

        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute;
        }

        public void Execute(object parameter)
        {
            execute();
            SimpleCommandManager.RefreshCommandStates();
        }

        public bool CanExecute
        {
            get { return canExecute == null || canExecute(); }
        }

        public void RaiseCanExecuteChanged()
        {
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        private readonly Action RaiseCanExecuteChangedAction;

        public event EventHandler CanExecuteChanged;
    }
}

and here my SimpleCommandManager.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace MVVMBoostUniversalWindowsApp
{
    public static class SimpleCommandManager
    {
        private static List<Action> _raiseCanExecuteChangedActions = new List<Action>();

        public static void AddRaiseCanExecuteChangedAction(ref Action raiseCanExecuteChangedAction)
        {
            _raiseCanExecuteChangedActions.Add(raiseCanExecuteChangedAction);
        }

        public static void RemoveRaiseCanExecuteChangedAction(Action raiseCanExecuteChangedAction)
        {
            _raiseCanExecuteChangedActions.Remove(raiseCanExecuteChangedAction);
        }

        public static void AssignOnPropertyChanged(ref PropertyChangedEventHandler propertyEventHandler)
        {
            propertyEventHandler += OnPropertyChanged;
        }

        private static void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // this if clause is to prevent an infinity loop
            if (e.PropertyName != "CanExecute")
            {
                RefreshCommandStates();
            }
        }

        public static void RefreshCommandStates()
        {
            for (var i = 0; i < _raiseCanExecuteChangedActions.Count; i++)
            {
                var raiseCanExecuteChangedAction = _raiseCanExecuteChangedActions[i];
                if (raiseCanExecuteChangedAction != null)
                {
                    raiseCanExecuteChangedAction.Invoke();
                }
            }
        }
    }
}

Do not ask my why there is no CommandManager in the .NETCore i do not know / unsterstand it as well. If someone could provide some information regarding to this it would be great to know.

But you should care about the usage: If you leave a Page you should destroy the leftover Commands in the SimpleCommandManager which are no more used but referenced. I also have a solution for this if one needs it i could edit my post.

It's really only a very simple / primitve solution and nothing special. But it works.

EDIT:

To provide a better usage sample and base, here is my base ViewModel as well. As mentioned above one should call the "RemoveCommands" when leaving the current page. Otherwise these would never set free by the GC.

ViewModel.cs

using System.ComponentModel;
using System;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml.Controls;
using System.Collections.Generic;
using Windows.UI.Core;

namespace MVVMBoostUniversalWindowsApp
{
    public abstract class ViewModel : INotifyPropertyChanged
    {
        protected ViewModel()
        {
            DispatcherObject = CoreWindow.GetForCurrentThread().Dispatcher;
            SimpleCommandManager.AssignOnPropertyChanged(ref this.PropertyChanged);
            _commandsList = new List<RelayCommand>();
        }

        private List<RelayCommand> _commandsList;

        protected RelayCommand CreateCommand(Action execute)
        {
            return CreateCommand(execute, null);
        }

        protected RelayCommand CreateCommand(Action execute, Func<bool> canExecute)
        {
            var tempCmd = new RelayCommand(execute, canExecute);
            if (_commandsList.Contains(tempCmd))
            {
                return _commandsList[_commandsList.IndexOf(tempCmd)];
            }
            else
            {
                _commandsList.Add(tempCmd);
                return tempCmd;
            }
        }

        public void RemoveCommands()
        {
            for (var i = 0; i < _commandsList.Count; i++)
            {
                _commandsList[i].RemoveCommand();
            }
        }

        public virtual CoreDispatcher DispatcherObject { get; protected set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void ChangeProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(property, value))
            {
                return;
            }
            else
            {
                property = value;

                OnPropertyChanged(propertyName);
            }
        }

        protected void OnPropertyChangedEmpty()
        {
            OnPropertyChanged(String.Empty);
        }

        protected void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
Stalinsk answered 26/1, 2016 at 8:42 Comment(0)
P
3

You are mixing standard .NET Framework WPF (CommandManager) and the smaller .NET Core Framework which is used for UWP development.

Unfortunately, they are not 100% compatible in all areas. You cannot use the RelayCommand class as it is written there - you will need to rewrite the parts which rely on CommandManager.

Puddle answered 25/1, 2016 at 15:29 Comment(1)
Thank you for your answer. So, does something like RelayCommand exists for UWP? I could imagine that it would be a great help for newbies to have a "base" class for commands (and even one for viewmodels). Not a big framework - just a class from experienced developers to work with and to learn from.Randellrandene
S
1

When you creat a Class whith a base (inherited) class try to open the Instance afterwards:

   public class RelayCommand : ICommand
{

public RelayCommand()
{}

private readonly Action<object> _Execute;
private readonly Func<object, bool> _CanExecute;

public RelayCommand(Action<object> execute)
    : this(execute, null)
{

}
Sec answered 25/1, 2016 at 15:34 Comment(0)
W
1

For future readers, Change your .csproj file to this:

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
</Project>

Clean solution and rebuild.

Wintergreen answered 15/9, 2021 at 11:4 Comment(2)
For future readers targeting .NET 5.0, this also works; however, the TargetFramework needs to be changed to net5.0-windows.Metope
This should be the accepted answer! Works also for .NET 6.0 make sure to change the TargetFramework = net6.0-windowsWilli
H
-1

This wont work on netstandard

I copied your code and I get

   public RelayCommand(Action<object> execute)
        : this(execute, null)
    {

    }

the "this" does not take 2 parameters

and I also get this

public class RelayCommand : ICommand
{

    public RelayCommand()
    { }

Does not implement ICommand -> CanExecuteChanged

So this won't work

Histidine answered 5/4, 2018 at 18:27 Comment(2)
This does not answer the OP's question. Plus, your points are completely incorrect. 1) The constructor is overloaded in the original code 2) CanExecuteChanged is defined in the original code 3) Nowhere was .NET Standard mentioned in the question (although it is irrelevant anyway)Tremain
I think i saw NET CORE in the original question - Is NET Core providing what he is looking for??Histidine

© 2022 - 2024 — McMap. All rights reserved.