When using the ICommand interface and binding to a control that via Command and CommandParameters (or just command) it's common to realize the button / control will not automatically become enabled / disabled (call CanExecute) when the value being used changes.
-
There are _lots_ of questions on Stack Overflow already discussing various strategies for ensuring `CanExecuteChanged` is raised at appropriate times. Such as the marked duplicate. See also https://stackoverflow.com/questions/7350845/canexecute-logic-for-delegatecommand, https://stackoverflow.com/questions/6425923/canexecutechanged-event-of-icommand, https://stackoverflow.com/questions/30002300/how-to-use-the-canexecute-method-from-icommand-on-wpfhttps://stackoverflow.com/questions/14479303/icommand-canexecute-not-triggering-after-propertychanged, and... – Peter Duniho Feb 25 '17 at 05:42
-
https://stackoverflow.com/questions/31078633/wpf-icommand-canexecute-raisecanexecutechanged-or-automatic-handling-via-di. Your question is either a rehash of everything that's already been written, or entirely too broad a question for Stack Overflow (let's assume for a moment that your question is honestly more than just an excuse to post a link to your web page). If you really have a question you'd like help with, please post a new question that is more specific, which includes a good [mcve] and a detailed explanation of what problem you are trying to solve which you can't. – Peter Duniho Feb 25 '17 at 05:43
-
@PeterDuniho Comment above for you. – Michael Puckett II Feb 25 '17 at 06:11
-
@PeterDuniho Will you show me which link above that you've posted as duplicates will work with multiplex models? This is a unique situation. There are no links that you've provided that says this is a duplicate. – Michael Puckett II Aug 08 '18 at 18:15
1 Answers
Just incase anyone is interested here's a little repo that forces the ICommand to update always as needed via the RelayCommand. This requires no extra work in the views which makes it practical IMO.
The relay command is just an example of how to easily automate the ICommand interface and isn't meant to be the fix all solution.
Here is the RelayCommand
public class RelayCommand : ICommand
{
private readonly RelayCommandBindings relayCommandBindings;
public event EventHandler CanExecuteChanged;
internal RelayCommand(RelayCommandBindings relayCommandBindings)
{
this.relayCommandBindings = relayCommandBindings;
relayCommandBindings.BindingModel.PropertyChanged += (s, e) =>
{
if (relayCommandBindings.BindingProperties.Any(p => p == e.PropertyName))
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
};
}
public bool CanExecute(object parameter) => (relayCommandBindings.CanExecuteChecks?.All(p => p.Invoke(parameter))).GetValueOrDefault();
public void Execute(object parameter) => relayCommandBindings?.Execute?.Invoke(parameter);
}
Here is the RelayCommandBindings
internal class RelayCommandBindings
{
public Action<object> Execute { get; set; }
public IEnumerable<Predicate<object>> CanExecuteChecks { get; set; }
public INotifyPropertyChanged BindingModel { get; set; }
public IEnumerable<string> BindingProperties { get; set; }
}
Here is the ViewModel
public class MultiplexViewModel : INotifyPropertyChanged
{
private const string NoName = "(no name)";
private bool isActive;
private bool isActiveChanging;
private string name;
private readonly MultiPlexModel multiPlexModel;
private readonly SynchronizationContext synchronizationContext;
public MultiplexViewModel()
{
multiPlexModel = new MultiPlexModel();
synchronizationContext = SynchronizationContext.Current;
var bindingProperties = new[]
{
nameof(IsActive),
nameof(IsActiveChanging)
};
var setNewNameBindings = new RelayCommandBindings()
{
Execute = async (obj) => await multiPlexModel.StartMultiplexModelAsync(obj.ToString()),
CanExecuteChecks = new List<Predicate<object>>
{
(obj) => IsValidName(obj?.ToString()),
(obj) => IsActive == false,
(obj) => IsActiveChanging == false
},
BindingModel = this,
BindingProperties = bindingProperties
};
var stopMultiplexBindings = new RelayCommandBindings()
{
Execute = async (obj) => await multiPlexModel.StopMultiplexModelAsync(),
CanExecuteChecks = new List<Predicate<object>>
{
(obj) => IsActive == true,
(obj) => IsActiveChanging == false
},
BindingModel = this,
BindingProperties = bindingProperties
};
SetNewNameCommand = new RelayCommand(setNewNameBindings);
StopMultiplexCommand = new RelayCommand(stopMultiplexBindings);
multiPlexModel.PropertyChanged += (s, e) => GetType().GetProperties().Where(p => p.Name == e.PropertyName).FirstOrDefault()?.SetValue(this, multiPlexModel.GetType().GetProperty(e.PropertyName).GetValue(multiPlexModel));
}
public event PropertyChangedEventHandler PropertyChanged;
public void Notify([CallerMemberName] string propertyName = "") => synchronizationContext.Post((o) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)), null);
public bool IsActive
{
get { return isActive; }
private set
{
isActive = value;
Notify();
}
}
public bool IsActiveChanging
{
get { return isActiveChanging; }
private set
{
isActiveChanging = value;
Notify();
}
}
public string Name
{
get { return string.IsNullOrEmpty(name) ? NoName : name; }
private set
{
name = value;
Notify();
}
}
private bool IsValidName(string name) => (name?.StartsWith("@")).GetValueOrDefault();
public RelayCommand SetNewNameCommand { get; private set; }
public RelayCommand StopMultiplexCommand { get; private set; }
}
Here is the Model
public class MultiPlexModel : INotifyPropertyChanged
{
private bool isActive;
private bool isActiveChanging;
private string name;
public event PropertyChangedEventHandler PropertyChanged;
public void Notify([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public bool IsActive
{
get { return isActive; }
private set
{
isActive = value;
Notify();
}
}
public bool IsActiveChanging
{
get { return isActiveChanging; }
private set
{
isActiveChanging = value;
Notify();
}
}
public string Name
{
get { return name; }
private set
{
name = value;
Notify();
}
}
public async Task StartMultiplexModelAsync(string newName)
{
await Task.Run(async () =>
{
if (IsActiveChanging)
return;
IsActiveChanging = true;
await Task.Delay(2000);
Name = newName;
IsActive = true;
IsActiveChanging = false;
});
}
public async Task StopMultiplexModelAsync()
{
await Task.Run(async () =>
{
if (IsActiveChanging)
return;
IsActiveChanging = true;
await Task.Delay(2000);
Name = string.Empty;
IsActive = false;
IsActiveChanging = false;
});
}
}
And here is the View
<UserControl.DataContext>
<ViewModels:MultiplexViewModel />
</UserControl.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="Auto Relay Command Example"
Margin="12, 40, 12, 12"
FontSize="32"
TextWrapping="Wrap" />
<TextBlock Text="The purpose of this application is to demonstrate a method for automatically activating CanExecute in ICommand. This automatically triggers buttons to become enabled / disabled based on various binding properties at once."
Margin="12"
TextWrapping="Wrap" />
<TextBlock Text="The name of the multiplex can only be set once if it starts with the @ symbol and the multiplex is not active."
Margin="12"
TextWrapping="Wrap" />
<TextBlock Text="The multiplex is started when the name is being properly set. At that time the multiplex cannot be altered... once it is set it cannot be reset until it has been stopped."
Margin="12"
TextWrapping="Wrap" />
<TextBlock Text="There is no code behind, triggers, converters, or other tricks used to make this work. This is purely binding to the commands in the ViewModel. The magic is in the RelayCommand and RelayCommandBindings in the ViewModels namespace."
Margin="12"
TextWrapping="Wrap" />
<TextBox Name="TextBoxName"
Text="{Binding Name, Mode=OneWay}"
Margin="12" />
<Button Content="Set New Name"
Margin="12"
Command="{Binding SetNewNameCommand}"
CommandParameter="{Binding Text, ElementName=TextBoxName}" />
<Button Content="Stop Multiplex"
Margin="12"
Command="{Binding StopMultiplexCommand}" />
<StackPanel Margin="12">
<TextBlock Text="Multiplex Changing" />
<TextBlock Text="{Binding IsActiveChanging}" />
</StackPanel>
<StackPanel Margin="12">
<TextBlock Text="Multiplex Active" />
<TextBlock Text="{Binding IsActive}" />
</StackPanel>
<StackPanel Margin="12">
<TextBlock Text="Multiplex Name" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</StackPanel>
Images---------
Here the button to Set New Name is enabled...
Here the buttons are again disabled while activating...
Here the name is set and the Stop button is enabled...
Here, once stopped, the view is back to start.
In this example you see various properties setting the tone of the View with straight forward command binding in the View the way it should be, IMO. Just thought this would help...

- 6,586
- 5
- 26
- 46
-
@PeterDuniho I could care less if people want to use the Git Repo or not. I spent the time writing the example, so people could see the demonstration working, only to help anyone that might be caught trying to handle multiple variables modifying CanExecute in a pure MVVM architecture. The repo was just to provide insight / help and is completely useless to maintain for any other purpose. – Michael Puckett II Feb 25 '17 at 06:10