Reputation: 1736
I have a RoutedUI Command that is bound as a Command property for a button and an OnClick event. Whenever I evaluate some condition from the OnClick I want to prevent the command from executing. I referred to this post but dosen't help much Prevent command execution. One quick fix is to get the sender of button on click and set its command to null. But I want to know if there is an other way. Please help.
<Button DockPanel.Dock="Right"
Name="StartRunButtonZ"
VerticalAlignment="Top"
Style="{StaticResource GreenGreyButtonStyle}"
Content="{StaticResource StartARun}"
Width="{StaticResource NormalEmbeddedButtonWidth}"
Click="StartRunButton_Click"
Command="{Binding StartRunCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl},AncestorLevel=2}}"
/>
Here is the code behind
private void StartRunButton_Click(object sender, RoutedEventArgs e)
{
if(SomeCondition){
//Prevent the Command from executing.
}
}
Upvotes: 0
Views: 943
Reputation: 1
This simple button extension could be useful for someone.
On button click at first it is invoked ConfirmationClick event. If you set in its callback e.IsConfirmed to true, then classic click event is invoked and command is executed.
Because of command binding you have button.IsEnabled property tied up to command.CanExecute.
public class ConfirmationButton : Button
{
public event EventHandler<ConfirmationEventArgs> ConfirmationClick;
protected override void OnClick()
{
ConfirmationEventArgs e = new ConfirmationEventArgs();
ConfirmationClick?.Invoke(this, e);
CommandParameter = e.CommandParameter;
if (e.IsConfirmed == true)
{
base.OnClick();
}
}
// Allow virtual click from code-behind
public void ForceClick(object sender = null)
{
ConfirmationEventArgs e = new ConfirmationEventArgs();
ConfirmationClick?.Invoke(sender ?? this, e);
CommandParameter = e.CommandParameter;
if (e.IsConfirmed == true)
{
base.OnClick();
}
}
}
public class ConfirmationEventArgs : EventArgs
{
public bool? IsConfirmed = false;
public object CommandParameter = null;
}
<model:ConfirmationButton x:Name="DeleteButton"
ConfirmationClick="DeleteButton_ConfirmationClick"
Command="{Binding DeleteCommand}"/>
private void DeleteButton_ConfirmationClick(object sender, ConfirmationEventArgs e)
{
var dialogWindow = new MyDialogWindow("Title..","Message.."); //example
e.IsConfirmed = dialogWindow.ShowDialog();
}
Upvotes: 0
Reputation: 4546
Assuming your StartRun() method follows the async / await pattern, then replace your ICommand implementation with the following. It will set CanExecute to false while the task is running, which will automatically disable the button. You don't need to mix commands and click event handlers.
public class perRelayCommandAsync : ViewModelBase, ICommand
{
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
public perRelayCommandAsync(Func<Task> execute) : this(execute, () => true) { }
public perRelayCommandAsync(Func<Task> execute, Func<bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
private bool _isExecuting;
public bool IsExecuting
{
get => _isExecuting;
set
{
if(Set(nameof(IsExecuting), ref _isExecuting, value))
RaiseCanExecuteChanged();
}
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => !IsExecuting
&& (_canExecute == null || _canExecute());
public async void Execute(object parameter)
{
if (!CanExecute(parameter))
return;
IsExecuting = true;
try
{
await _execute().ConfigureAwait(true);
}
finally
{
IsExecuting = false;
}
}
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
More details at my blog post.
Upvotes: 0