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);
}
}
}
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));
}
}
}
}
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With