c# wpf CanExecuteChanged property changed on different thread - c#

I have a problem that the CanExecuteChanged is not done when I change a property. I pretty much figured out why. However I am not really sure how to fix it. The issue apparently comes from changing the property on a different thread.
My code..
Command:
class StartTestCommand : ICommand
{
private MainViewModel viewModel;
public StartTestCommand(MainViewModel viewModel)
{
this.viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
bool canExecute = true;
if (viewModel.SerialPortNo==null)
{
canExecute = false;
}
if (viewModel.IsTestRunning)
{
canExecute = false;
}
return canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
Task.Factory.StartNew(() => { viewModel.StartTest(); });
}
}
And the thing that is called on a separate thread:
/// <summary>
/// Start the testing
/// </summary>
public void StartTest()
{
StartSerial();
ExecuteSelectedTests();
StopSerial();
}
/// <summary>
/// start serial communication
/// </summary>
private void StartSerial()
{
serialManager.Start(serialPortNo);
}
/// <summary>
/// stop serial communication
/// </summary>
private void StopSerial()
{
serialManager.Stop();
}
/// <summary>
/// runs the selected tests
/// </summary>
private void ExecuteSelectedTests()
{
this.IsTestRunning = true;
foreach (var item in testItemsToRunCollection)
{
item.Execute();
}
this.IsTestRunning = false;
}
So the IsTestRunning is set from another thread. I would like to know what would be the correct way of fixing this problem :)

You can dispatch the code to be run on the UI thread like this:
Dispatcher.Invoke(((Action)(() => ]
{
this.IsTestRunning = false;
CommandManager.InvalidateRequerySuggested();
})));

Related

CommandBinding Executed event handler not called when using custom ICommand implementation

I was trying to undestand how RoutedUICommand Works. I have worked with MVVM in Wpf, and was trying to under stand ICommand implementation actually works in wpf. However, I am unable to understand something.
public class RelayCommand : ICommand
{
private Action<Task> callBack;
private bool runOnUi;
private Action<object> whatToExecute;
private Func<object, bool> whenToExecute;
public RelayCommand(Func<object, bool> whenToExecute, Action<object> whatToExecute,
bool runOnUi = true, Action<Task> callBack = null)
{
this.whenToExecute = whenToExecute;
this.whatToExecute = whatToExecute;
this.runOnUi = runOnUi;
this.callBack = callBack;
}
/// <summary>
/// This is an event from Interface
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// Decides whether the command can execute or not.
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
if (this.whenToExecute != null)
{
try
{
return this.whenToExecute(parameter);
}
catch
{
return false;
}
}
return true;
}
/// <summary>
/// Called when CanExecute is true and command is fired.
/// </summary>
/// <param name="parameter"></param>
public void Execute(object parameter)
{
if (this.whatToExecute != null)
{
if (this.runOnUi)
{
this.whatToExecute(parameter);
}
else
{
var parallelTask = Task.Run(() =>
{
this.whatToExecute(parameter);
});
if (this.callBack != null)
parallelTask.ContinueWith(this.callBack);
}
}
}
}
This is the custom implementation of ICommand interface.
I have added two menuitems to context menu of label. First with ApplicationCommands.Close and second on with my customer RelayCommand type.
this.Label1.ContextMenu = new ContextMenu();
var menuItem1 = new MenuItem();//Source
menuItem1.Command = ApplicationCommands.Close; //Command
var commandBindingObject = new CommandBinding(menuItem1.Command);
commandBindingObject.CanExecute += this.MenuItem1Close_CanExecute;
commandBindingObject.Executed += this.MenuItem1Close_Executed;
menuItem1.CommandBindings.Add(commandBindingObject);
var menuItem2 = new MenuItem();//Source
menuItem2.Header = "Custom Command";
menuItem2.Command = new RelayCommand(o =>
{
return true;
},
o =>
{
}); //Command
var commandBindingObject2 = new CommandBinding(menuItem2.Command);
commandBindingObject2.CanExecute += this.MenuItem2Close_CanExecute;
commandBindingObject2.Executed += this.MenuItem2Close_Executed;
menuItem2.CommandBindings.Add(commandBindingObject2);
this.Label1.ContextMenu.Items.Add(menuItem1);
this.Label1.ContextMenu.Items.Add(menuItem2);
Whenever I click the first menu item handler is menuitem1 handler for executed is called, but not for the menuitem2. I am just trying to understand how command pattern is implemented in wpf, so in addition any links to such would also be of great help.
Routed Commands
The ApplicationCommands.Close command is a RoutedUICommand. Routed commands raise the PreviewExecuted and Executed routed events which traverse the element tree until they find a command binding. If one is found the corresponding ExecutedRoutedEventHandler is called. In case of menuItem1 the executed handler is MenuItem1Close_Executed. The same applies to the PreviewCanExecute and CanExecute events and the MenuItem1Close_CanExecute handler as CanExecuteRoutedEventHandler.
Relay Commands
Your menuItem2 uses a RelayCommand. This type of command uses delegates for CanExecute and Execute, instead of traversing the element tree in search for command bindings.
In your case the following lines are unnecessary.
var commandBindingObject2 = new CommandBinding(menuItem2.Command);
commandBindingObject2.CanExecute += this.MenuItem2Close_CanExecute;
commandBindingObject2.Executed += this.MenuItem2Close_Executed;
menuItem2.CommandBindings.Add(commandBindingObject2);
Instead pass methods or anonymous methods for whenToExecute and whatToExecute.
menuItem2.Command = new RelayCommand(CanExecute, Execute);
You cannot pass MenuItem1Close_CanExecute or MenuItem1Close_Executed here, because they have incompatible signatures:
void CanExecuteRoutedEventHandler(object sender, CanExecuteRoutedEventArgs e)
void ExecutedRoutedEventHandler(object sender, ExecutedRoutedEventArgs e)
CanExecute has to be a Func<object, bool> and Execute has to be an Action<object> delegate.
bool CanExecute(object obj)
void Execute(object obj)

MVVM executes all codes at once in Execute Command

I am using MVVM solution provided in my previous question
XAML
<ProgressBar x:Name="StatusProgressBar" IsIndeterminate="{Binding IsIndeterminate, Mode=OneWay}" Height="18" Width="120" VerticalAlignment="Center" Background="White" BorderBrush="#FF05438D" />
ViewModel
Notice here in DoExecuteGetIpCommand() method if i do same thing in code behind on content rendered event works correctly but in mvvm all codes fires at same time so progress bar update after all time consuming process.
So i want to set ProgressBar IsIndeterminate Property true while time consuming method is working after done finally set IsIndeterminate to false. any idea to this and why it is happening but working fine in code behind Content rendered event.
public class MainWindowViewModel : INotifyPropertyChanged
{
public bool _isIndeterminate;
private string _ipAdrress;
private bool _errorOccured;
public event PropertyChangedEventHandler PropertyChanged;
GetPublicIP getPublicIP = new GetPublicIP();
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/*
public bool IpIndeterminate
{
get { return _isIndeterminate; }
set
{
_isIndeterminate = value;
OnPropertyChanged(nameof(IpIndeterminate));
}
}
*/
//OR
//IsIndeterminate here is problem
public bool IsIndeterminate => _isIndeterminate;
public string IpAddress => _ipAdrress;
public Brush IpForeground => _errorOccured ? new SolidColorBrush(Colors.IndianRed) : new SolidColorBrush(Colors.Black);
public FontWeight IpFontWeight => _errorOccured ? FontWeights.SemiBold : FontWeights.Normal;
public ICommand GetIpCommand
{
get { return new RelayCommand(param => DoExecuteGetIpCommand()); }
}
private async void DoExecuteGetIpCommand()
{
_isIndeterminate = true;
try
{
_errorOccured = false;
//_ipAdrress = await MyService.GetIpAddress();
_ipAdrress = await getPublicIP.GetIPAddressAsync();//time consuming method.
}
finally
{
//Commented this because progress bar immediately Is indeterminate go false.
//_isIndeterminate = false;
}
if (await getPublicIP.ExceptionOccursAsync() == true)
{
_errorOccured = true;
}
OnPropertyChanged(nameof(IsIndeterminate));
OnPropertyChanged(nameof(IpAddress));
OnPropertyChanged(nameof(IpForeground));
OnPropertyChanged(nameof(IpFontWeight));
}
}
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute"); //NOTTOTRANS
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
You have to change your code like this: (as dymanoid said)
private async void DoExecuteGetIpCommand()
{
_isIndeterminate = true;
//Tell the UI that something changed...
OnPropertyChanged(nameof(IsIndeterminate));
try
{
_errorOccured = false;
_ipAdrress = await getPublicIP.GetIPAddressAsync();//time consuming method.
}
finally
{
_isIndeterminate = false;
}
if (await getPublicIP.ExceptionOccursAsync() == true)
{
_errorOccured = true;
}
OnPropertyChanged(nameof(IsIndeterminate));
OnPropertyChanged(nameof(IpAddress));
OnPropertyChanged(nameof(IpForeground));
OnPropertyChanged(nameof(IpFontWeight));
}

Pass command parameter to method in ViewModel in WPF?

I am trying to pass CommandParameter to the method in my ViewModel.
How to do this?
private void Open(object sender)
{
if (sender==this.objMainWindow.btnHistory)
{
objMainWindow.Container.Child = objHistory;
}
if (sender == this.objMainWindow.btnNew_Item)
{
objMainWindow.Container.Child = objNewItem;
}
if (sender == this.objMainWindow.btnSide_Effects)
{
objMainWindow.Container.Child = objSideEffect;
}
}
This is my meyhod in ViewModel that I want to pass CommandParameter. I use CommandParameter for button.
"ViewModel" implies MVVM. If you're doing MVVM you shouldn't be passing views into your view models. Typically you do something like this in your XAML:
<Button Content="Edit"
Command="{Binding EditCommand}"
CommandParameter="{Binding ViewModelItem}" >
And then this in your view model:
private ViewModelItemType _ViewModelItem;
public ViewModelItemType ViewModelItem
{
get
{
return this._ViewModelItem;
}
set
{
this._ViewModelItem = value;
RaisePropertyChanged(() => this.ViewModelItem);
}
}
public ICommand EditCommand { get { return new RelayCommand<ViewModelItemType>(OnEdit); } }
private void OnEdit(ViewModelItemType itemToEdit)
{
... do something here...
}
Obviously this is just to illustrate the point, if you only had one property to edit called ViewModelItem then you wouldn't need to pass it in as a command parameter.
Just using Data Binding syntax. For example,
<Button x:Name="btn"
Content="Click"
Command="{Binding ClickCmd}"
CommandParameter="{Binding ElementName=btn,Path=Content}" />
Not only can we use Data Binding to get some data from View Models, but also pass data back to View Models. In CommandParameter, must use ElementName to declare binding source explicitly.
If you are that particular to pass elements to viewmodel You can use
CommandParameter="{Binding ElementName=ManualParcelScanScreen}"
Try this:
public class MyVmBase : INotifyPropertyChanged
{
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler( MyAction));
}
}
public void MyAction(object message)
{
if(message == null)
{
Notify($"Method {message} not defined");
return;
}
switch (message.ToString())
{
case "btnAdd":
{
btnAdd_Click();
break;
}
case "BtnEdit_Click":
{
BtnEdit_Click();
break;
}
default:
throw new Exception($"Method {message} not defined");
break;
}
}
}
public class CommandHandler : ICommand
{
private Action<object> _action;
private Func<object, bool> _canExecute;
/// <summary>
/// Creates instance of the command handler
/// </summary>
/// <param name="action">Action to be executed by the command</param>
/// <param name="canExecute">A bolean property to containing current permissions to execute the command</param>
public CommandHandler(Action<object> action, Func<object, bool> canExecute)
{
if (action == null) throw new ArgumentNullException(nameof(action));
_action = action;
_canExecute = canExecute ?? (x => true);
}
public CommandHandler(Action<object> action) : this(action, null)
{
}
/// <summary>
/// Wires CanExecuteChanged event
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Forcess checking if execute is allowed
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_action(parameter);
}
public void Refresh()
{
CommandManager.InvalidateRequerySuggested();
}
}
And in xaml:
<Button
Command="{Binding ClickCommand}"
CommandParameter="BtnEdit_Click"/>

How to update the view after the model has changed?

Say I have a Logger class, a LoggerViewModel class and a MainWindow with a TextBox. The Logger class is a thread-safe singleton, so I have only an instance of it in the application domain.
public sealed class Logger : INotifyPropertyChanged
{
private static readonly Logger _Instance = new Logger();
private static readonly object _SyncLock = new object();
private static List<LogEntry> _Data = new List<LogEntry>();
/// <summary>
///
/// </summary>
private Logger() { ; }
/// <summary>
///
/// </summary>
public static Logger Instance
{
get { return _Instance; }
}
/// <summary>
///
/// </summary>
/// <param name="entry"></param>
public void Write(LogEntry entry)
{
lock (_SyncLock)
{
_Data.Add(entry);
}
this.RaiseNotifyPropertyChanged("Entries");
}
/// <summary>
///
/// </summary>
/// <param name="component"></param>
/// <param name="message"></param>
public void Write(string component, string message)
{
LogEntry entry = LogEntry.Create(component, message);
Write(entry);
}
/// <summary>
///
/// </summary>
public IList<LogEntry> Entries
{
get
{
lock (_SyncLock)
{
return new ReadOnlyCollection<LogEntry>(_Data);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="property"></param>
private void RaiseNotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
/// <summary>
///
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
}
The Logger unique instance is updated by more thread while the application is running, so I would update the TextBox on the MainWindow whenever the model (that is the Logger singleton class) changes.
How to connect the Model and the ViewModel between them? I emphasize that the Model is changed by only a few application thread, so it is read-only from the point of view of UI.
I provided the LoggerText property within the LoggerViewModel class, since I thought the following working mechanism.
1. When the Model (the Logger instance) changes, it notifies the ViewModel.
2. The ViewModel receives the notify by the Model and create a new string containing all the messages from the logger.
3. The ViewModel notifies the View.
public class LoggerViewModel : INotifyPropertyChanged
{
Logger _LoggerModel;
/// <summary>
///
/// </summary>
public LoggerViewModel()
{
_LoggerModel = Logger.Instance;
}
/// <summary>
///
/// </summary>
public string LoggerText
{
get
{
string text = "";
List<LogEntry> entries = new List<LogEntry>(_LoggerModel.Entries);
foreach (LogEntry entry in entries)
{
text += entry.ToString();
text += "\n";
}
return text;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
How the ViewModel can intercept the notifications sent by the Model?
First of all I do not like that you are using a singleton. When using the singleton pattern you are making it hard on yourself when testing or reusing your view controllers. I would instead inject the Logger dependency into your LoggerViewModel class.
Aside from that one way to solve your problem is to register a handler for the PropertyChanged event on your Logger and build the text when the event fires for the Entries property.
In LoggerViewModel you would then add a property handler and update the LoggerText property as needed.
public LoggerViewModel(Logger loggerModel /* Dependency injection*/)
{
_LoggerModel = loggerModel;
_LoggerModel.PropertyChanged += this.LoggerModel_PropertyChanged;
}
private void LoggerModel_PropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Entries")
{
StringBuilder text = new StringBuilder(); // Use StringBuilder for performance
List<LogEntry> entries = new List<LogEntry>(_LoggerModel.Entries);
foreach (LogEntry entry in entries)
{
text.AppendLine(entry.ToString());
}
this.LoggerText = text.ToString();
}
}
private string _loggerText;
public string LoggerText
{
set
{
_loggerText = value;
RaisePropertyChanged("LoggerText");
}
get
{
return _loggerText;
}
}
Disclaimer: The above code is written without a compiler.

How to stop Execution of Thread from other thread?

I have designed MMVM pattern in C#. My GUI has different button. Each button is for particular command. These commands are derived from CommandsBase Class. Each command runs on seperate thread by calling CommandExecute. There are several commands like CommandRunMode1, CommandRunMode2, commandDiagnosys etc. Now new requirement has been arised to abort command. I am trying to write CommandAbort Class. And the problem is how to abort already executing command when ABORT button is pressed on GUI (i.e. stop other thread in the halfway from CommandAbort class thread).
enter code here
#region COMMAND_Base
public abstract class CommandsBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected delegate void NoArgsDelegate();
protected delegate void OneArgDelegate(string arg1);
protected delegate void TwoArgDelegate(string arg1, bool arg2);
protected ViewModelBase ParentViewModel;
private StatusIndicator _isEnableState;
string _uiText;
bool _uiEnable;
RelayCommand _command;
protected Dispatcher _dispatcher;
private readonly int _responseDelay = 2000; // milliseconds
#region PROPERTIES
public CommandText CommandText { get; set; } // Ui Text
public CommandStatusIndicator CommandStatus { get; set; } // background color
public StatusIndicator IsEnableState
{
get { return _isEnableState; }
set
{
_isEnableState = value;
OnChanged("Status");
}
}
public string UiText
{
get { return _uiText; }
set
{
_uiText = value;
OnChanged("UiText");
}
}
public bool UiEnabled
{
get
{
return _uiEnable;
}
set
{
_uiEnable = value;
OnChanged("UiEnabled");
}
}
public ICommand Command
{
get
{
if (_command == null)
{
_command = new RelayCommand(param => this.CommandExecute(), param => this.CommandCanExecute);
}
return _command;
}
}
public int NumberOfAttempts;
public int ResponseDelay
{
get
{
return _responseDelay;
}
}
#endregion
protected CommandsBase()
{
}
protected void UpdateUi(string text)
{
UiText = text;
}
protected void UpdateUi(bool enabled)
{
UiEnabled = enabled;
}
protected void UpdateUi(string text, bool enabled)
{
UiText = text;
UiEnabled = enabled;
}
#region COMMAND_EXECUTION
public virtual void CommandExecute()
{
NoArgsDelegate commandExecution = new NoArgsDelegate(CommandExecuteInAThread);
commandExecution.BeginInvoke(null, null);
}
protected abstract void CommandExecuteInAThread();
public abstract bool CommandCanExecute { get; }
#endregion
public virtual void OnChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion
region COMMAND_RunMode1
public class CommandRunMode1 : CommandsBase
{
public CommandRunMode1(string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Mode1
ApplicationLayer.TransmitString("START1");
while (Display.CurrentScreent != DisplayController.CurrentScreen.ST1SCREEN);
ApplicationLayer.TransmitString("MODE1ENTER");
while (Display.CurrentScreent != DisplayController.CurrentScreen.MD1SCREEN);
ApplicationLayer.TransmitString("PROCESSCREEN");
while (Display.CurrentScreent != DisplayController.CurrentScreen.PROCESSCREEN);
}
#endregion
}
endregion
region COMMAND_RunMode2
public class CommandRunMode2 : CommandsBase
{
public CommandRunMode2(string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Mode2
ApplicationLayer.TransmitString("START2");
while (Display.CurrentScreent != DisplayController.CurrentScreen.ST2SCREEN);
ApplicationLayer.TransmitString("MODE2ENTER");
while (Display.CurrentScreent != DisplayController.CurrentScreen.MD2SCREEN);
ApplicationLayer.TransmitString("PROCESSCREEN");
while (Display.CurrentScreent != DisplayController.CurrentScreen.PROCESSCREEN);
}
#endregion
}
endregion
region COMMAND_Abort
public class CommandAbort : CommandsBase
{
public CommandAbort (string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Abort currently running command
ApplicationLayer.TransmitString("ABRT");
}
#endregion
}
endregion
Cancellation of a thread should always be cooperative.
What do i mean by that?
The code running in your thread should periodically check to see if it should continue. This may be a boolean somewhere. If cancellation is required, simply cleanup the resources you are using and return.
volatile bool IsCancelled = false;
void DoWork()
{
while(!IsCancelled)
{
//do work
}
}
When is this flag set?
Perhaps you have a Cancel button. Pushing this would trigger an event handler. This event handler sets the flag. The next time your threaded code checks the flag, it will cancel the operation. This is why it is called cooperative cancellation. 2 threads work together to make it happen.
Im afraid you have an architectural challenge to overcome
In MVVM, commands are the pipeline in which user interaction is communicated to the view model. The View model should react to this command by 'doing to the work' by calling the appropriate methods on other classes ( your model/foundation layer/business objects...use whichever words you prefer). You will need an implementation of DelegateCommand or RelayCommand.
User clicks Button
Button executes command
Command invokes method on view model
View model calls your domain classes to do the work
Why structure it this way? Now you can have your cancellation flag in the view model. This is one of the things a view model is for - storing state!
If you are able to, I recommend using .Net's Task Parallel Library. It supports cooperative cancellation for free!
To sum up. I strongly recommend you move the serial code out of the command classes. It is possible to make it work with your design but this is not good practice :(

Categories

Resources