I'm developing an asynchronous application using WPF and MVVM, but I can't seem to get an async method to run inside my relaycommand.
I have a button on my WPF view hooked up to a relaycommand in my viewmodel, which trys to call an async method in my model to return a list of results:
/// <summary>
/// Search results
/// </summary>
private ObservableCollection<string> _searchResults = new ObservableCollection<string>();
public IList<string> SearchResults
{
get { return _searchResults; }
}
/// <summary>
/// Search button command
/// </summary>
private ICommand _searchCommand;
public ICommand SearchCommand
{
get
{
_searchCommand = new RelayCommand(
async() =>
{
SearchResults.Clear();
var results = await DockFindModel.SearchAsync(_selectedSearchableLayer, _searchString);
foreach (var item in results)
{
SearchResults.Add(item);
}
//notify results have changed
NotifyPropertyChanged(() => SearchResults);
},
() => bAppRunning); //command will only execute if app is running
return _searchCommand;
}
}
However I get the following exception when the relaycommand tries to execute:
An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll
Additional information: A Task's exception(s) were not observed either
by Waiting on the Task or accessing its Exception property. As a
result, the unobserved exception was rethrown by the finalizer thread.
I've tried a number of things in this thread to try and resolve the issue with no luck. Does anyone know how to resolve this?
Not sure where your RelayCommand is coming from (MVVM framework or custom implementation) but consider using an async version.
public class AsyncRelayCommand : ICommand
{
private readonly Func<object, Task> execute;
private readonly Func<object, bool> canExecute;
private long isExecuting;
public AsyncRelayCommand(Func<object, Task> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute ?? (o => true);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
public bool CanExecute(object parameter)
{
if (Interlocked.Read(ref isExecuting) != 0)
return false;
return canExecute(parameter);
}
public async void Execute(object parameter)
{
Interlocked.Exchange(ref isExecuting, 1);
RaiseCanExecuteChanged();
try
{
await execute(parameter);
}
finally
{
Interlocked.Exchange(ref isExecuting, 0);
RaiseCanExecuteChanged();
}
}
}
Ok so I actually managed to resolve the issue by making a change to the way I run my async method.
I changed this:
var results = await DockFindModel.SearchAsync();
To this:
var results = await QueuedTask.Run(() => DockFindModel.SearchAsync());
Although i'm a bit confused as to why I need to await Task.Run() when relaycommand is already accepting async lambda. I'm sure it'll become clear with time however.
Thanks to those who commented.
That RelayCommand class is probably accepting a parameter of type Action. Since you are passing an async lambda, we are having an async void scenario here.
Your code will be fired and forgot. Consider using an ICommand implementation that takes Func<Task> as a parameter instead of Action.
Related
Firstly I have a user control which has a dependency property as follows. The MenuItems property is bound to some List control on the UI.
public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register(
nameof(MenuItems),
typeof(IEnumerable<MenuItem>),
typeof(MenuViewControl),
new PropertyMetadata(null));
public IEnumerable<MenuItem> MenuItems
{
get => (IEnumerable<MenuItem>)GetValue(MenuItemsProperty);
set => SetValue(MenuItemsProperty, value);
}
The MenuItem class is as follows which has 3 properties,
public class MenuItem : BindableBase
{
private string _text;
private Action _action;
private ICommand _executeCommand;
public string Text
{
get => _text;
set => Set(ref _text, value);
}
public Action Action
{
get => _action;
set => Set(ref _action, value);
}
public ICommand ExecuteCommand
{
get => _executeCommand ?? (_executeCommand = new RelayCommand(Action, _canExecute));
set
{
if (Set(ref _executeCommand, value))
{
CanExecute = () => _executeCommand?.CanExecute(null) ?? true;
_executeCommand.CanExecuteChanged += (sender, args) => RaisePropertyChanged(nameof(IsEnabled));
}
}
}
}
Now somewhere in my code I want to reuse the above user control. Along the same lines I need to call some async methods. So I have a view model class for the current UI where I will be calling the above user control as follows. My problem is the IsBorderProgressRingVisible is never being set to false and the RunMethodResult never updates the TextBlock in the current UI. Please help.
public class UserMaintenanceMethodsViewModel:BindableBase
{
//This collection is bound to the above UserControl's MenuItem property on my current UI.
private ObservableCollection<MenuItem> _userMaintenanceMenuCollection;
public ObservableCollection<MenuItem> UserMaintenanceMenuCollection
{
get => _userMaintenanceMenuCollection;
set => Set(ref _userMaintenanceMenuCollection, value);
}
//This string is bound to a textblock
private string _runMethodResult;
public string RunMethodResult
{
get => _runMethodResult;
set => Set(ref _runMethodResult, value);
}
//This property is bound to a progress ring.
private bool _isBorderProgressRingVisible;
public bool IsBorderProgressRingVisible
{
get => _isBorderProgressRingVisible;
set => Set(ref _isBorderProgressRingVisible, value);
}
//In my constructor I am calling some async methods as follows..
public UserMaintenanceMethodsViewModel()
{
_ = PopulateServiceMethods();
}
//Problem in this method is once the IsBorderProgressRingVisible is set to true, it never sets the value back to false. As a result the progress ring never collapses.
//The other problem is the RunMethodResult which is bound to a textblock never gets updated. Please help.
private async Task PopulateServiceMethods()
{
try
{
if (_atlasControlledModule != null)
{
IsBorderProgressRingVisible = true;
UserMaintenanceMenuCollection = new ObservableCollection<MenuItem>();
var Methods = await _atlasControlledModule.GetServiceMethods(AtlasMethodType.Maintenance).ConfigureAwait(true);
foreach (var method in Methods)
{
UserMaintenanceMenuCollection.Add(new MenuItem()
{
Text = method.Name,
Action = async () =>
{
var result = await ExcuteAtlasMethod(method).ConfigureAwait(true);
RunMethodResult = result.Status.ToString(); //The textblock on the UI never gets updated.
},
Warning = false
});
}
}
}
finally
{
IsBorderProgressRingVisible = false; //This code dosen't work.
}
}
private async Task<AtlasMethodRequest> ExcuteAtlasMethod(AtlasMethod method)
{
try
{
IsBorderProgressRingVisible = true;
return await _atlasControlledModule.CallMethod(method);
}
finally
{
IsBorderProgressRingVisible = false;
}
}
}
Edit: Here is the Xaml for the current view
<viewCommon:PageViewBase
x:Class="Presentation.InstrumentUI.ViewsLoggedIn.UserMaintenanceMethodsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewCommon="using:Presentation.InstrumentUI.Common"
xmlns:viewsCommon="using:Presentation.InstrumentUI.ViewsCommon"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:valueConverters="using:Presentation.Common.ValueConverters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<viewsCommon:MenuViewControl x:Name="UserMaintenanceMethodsMenuView"
Grid.Row="0"
Title="{Binding UserMaintenanceMethodsTitle, Source={StaticResource StringResources}}"
LifetimeScope="{x:Bind LifetimeScope}"
MenuItems="{x:Bind ViewModel.UserMaintenanceMenuCollection,Mode=OneWay}"
HeaderVisibility="Visible">
</viewsCommon:MenuViewControl>
</Grid>
</viewCommon:PageViewBase>
This is the xaml.cs
public sealed partial class UserMaintenanceMethodsView : PageViewBase
{
public IUserMaintenanceMethodsViewModel ViewModel { get; set; }
public UserMaintenanceMethodsView()
{
this.InitializeComponent();
ViewModel = LifetimeScope.Resolve<IUserMaintenanceMethodsViewModel>();
}
}
From what I see, you code should generally work. The problem is that all your code is executed in the constructor of UserMaintenanceMethodsViewModel. You shouldn't call long running methods from a constructor and you shouldn't call async methods from your constructor. An asynchronous method generally indicates some long running or CPU heavy operation. It should be moved outside the constructor so that you can execute it asynchronously.
Also the way you invoke the asynchronous method from the constructor is wrong:
ctor()
{
// Executes synchronously.
// Leaves object in undefined state as the constructor will return immediately.
_ = PopulateServiceMethodsAsync();
}
The previous example will execute the method PopulateServiceMethods synchronously. Furthermore, the constructor will return before the method has completed, leaving the instance in an uninitialized state.
The caller of the constructor will continue and probably use the instance, assuming that it is ready to use. This might lead to unexpected behavior.
To solve this, you should move the resource intensive initialization to a separate method:
ctor()
{
// Some instance member initialization
}
// Call later e.g. on first access of property internally or externally
public async Task InitializeAsync()
{
// Some CPU heavy or long running initialization routine
}
You can also consider to instantiate this type deferred using Lazy<T> or AsyncLazy<T>.
This property in MenuItem class has a "dangerous" setter:
public ICommand ExecuteCommand
{
get => _executeCommand ?? (_executeCommand = new RelayCommand(Action, _canExecute));
set
{
if (Set(ref _executeCommand, value))
{
CanExecute = () => _executeCommand?.CanExecute(null) ?? true;
_executeCommand.CanExecuteChanged += (sender, args) => RaisePropertyChanged(nameof(IsEnabled));
}
}
}
Calling the set method will replace the previous command without unsubscribing from the old CanExecuteChanged event. This can lead to memory leaks in certain scenarios. Always unsubscribe from the old instance before subscribing to the new instance.
Also I'm not quite sure why you are listening to this event at all. Usually controls listen to this event in. E.g., a Button subscribes to this event and when it is raised, it would invoke ICommand.CanExecute again to deactivate itself, if this method returns false. From your view model you usually want to call RaiseCanExecuteChanged on your command to trigger re-evaluation for all controls (or implementations of ICommandSource).
Using async in lambdas can also lead to unexpected behavior:
Action = async () =>
{
var result = await ExcuteAtlasMethod(method).ConfigureAwait(true);
RunMethodResult = result.Status.ToString(); // The textblock on the UI never gets updated.
}
Executing the Action won't cause the thread to wait asynchronously, because the delegate is not awaited. Execution continues. You should consider to implement the RelayCommand that it accepts a Func<object, Task>. This way the invocation of the delegate can be awaited.
{x:Bind} has a different behavior than {Binding}. x:Bind is a compiletime binding. It doesn't bind to the DataContext and requires a static binding source. You should debug your code in order to check if LifeTimeScope resolves properly. Maybe it executes on a different thread. You can try to change ViewModel to a DependencyProperty.
I also realized that you are binding to a property that is declared as interface type:
public IUserMaintenanceMethodsViewModel ViewModel { get; set; }
This won't work. Please try to replace the interface with a concrete type. I think this will solve this. E.g.,
public UserMaintenanceMethodsViewModel ViewModel { get; set; }
I am experiencing some confusion with Tasks and the async/await key words. I understand that you should NOT mix async and blocking code. Or at least what my interpretation of mixing them is:
Don't make calls to blocking API's from non- async methods. So here's my issue.
I am trying to await a method, then update the UI accordingly. The issue is that the only way to await an async method() call is from within and async method().
Here's an example:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
{
Completed = false;
var cancellationTokenSource = new CancellationTokenSource();
await RunValidation(options, cancellationTokenSource.Token);
Completed = true;
}));
}
}
This code runs the method properly and awaits. The issue is when I return. For some reason when setting the Complete flag the buttons dependent on this flag are not toggled. If I comment the await code, then the buttons are toggled correctly. So assumed I was not returning on the UI thread, so I tried using this code instead:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
{
Completed = false;
var cancellationTokenSource = new CancellationTokenSource();
var context = TaskScheduler.FromCurrentSynchronizationContext();
await RunValidation(options, cancellationTokenSource.Token).ContinueWith(t => Completed = true, context);
//Completed = true;
}));
}
}
Here is the RunValidation() method:
private async Task RunValidation(Options options, CancellationToken token)
{
await _someService.SomAsyncMethod(options, token));
}
If you notice, the ExecuteCommand has an async key word before the (options) parameter that is passed to the command. If I remove the async key word then I have to modify the call to the RunValidation() method. I still need it to await, so this is what I did:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>((options) =>
{
Completed = false;
var context = TaskScheduler.FromCurrentSynchronizationContext();
var cancellationTokenSource = new CancellationTokenSource();
Task.Run(async () => await RunValidation(options, cancellationTokenSource.Token));
Completed = true;
}));
}
}
The problem with this code is that it doesn't await. So I am at a loss.
Can anyone shed some light on this for me please. I've spend 2 plus days on this and I am still here.
Thanks,
Tim
Here are the bindings to the Command Buttons.
private readonly Independent<bool> _completed = new Independent<bool>(true);
public bool Completed
{
get { return _completed; }
set { _completed.Value = value; }
}
private ICommand _doneCommand;
public ICommand DoneCommand
{
get
{
return _doneCommand ?? (_doneCommand = MakeCommand.When(() => Completed).Do(() =>
{
DoSomething();
}));
}
}
private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
return _cancelCommand ??
(_cancelCommand = MakeCommand.When(() => !Completed).Do(() => DoSomthingElse()));
}
}
I am using the MakeCommand objects from the UpdateControls library from Michael Perry. They contain dependancy tracking that raises the CanExecuteChange events when the Complete property is changed.
Your first code is correct. Most likely you have an incorrect implementation for your Completed property. Your view model object should implement INotifyPropertyChanged. The easiest way to do this right is use a base class that provides the functionality. ReactiveUI is the nuget package I always use. Usage is as simple as
public class MyObject : ReactiveObject {
private bool _Completed;
public bool Completed {
get => _Completed;
set => this.RaiseAndSetIfChanged(ref _Completed, value);
}
}
This will make sure that notifications are raised to the UI when the property is changed.
If you want it to be more magic you can use ReactiveUI.Fody and then your code will reduce to
public class MyObject : ReactiveObject {
[Reactive]
public bool Completed { get; set;}
}
So the issue was in fact the third party library. I was using it to provide dependency tracking for the 2 buttons. So when the complete flag changed it raised the CanExecuteChange events for both buttons without me having write code to do it. Unfortunately it stopped working after introducing the async/await calls. I replaced the 2 MakeCommands with RelayCommands and raised the events myself and everything worked.
So thanks to everyone for your responses.
Tim
I'm trying create a class that has events AND can be awaitable, but keep coming across stumbling blocks.
First, I tried a TransferJob class that returns a TransferTask object which is already running when it is returned. This would be accomplished through something like this:
public abstract class TransferJob
{
public TransferTask Start()
{
return Start(CancellationToken.None);
}
public TransferTask Start(CancellationToken token)
{
TransferTask task = CreateTransferTask();
task.Start(token);
return task;
}
protected abstract TransferTask CreateTransferTask();
}
public abstract class TransferTask
{
public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;
private Task transferTask;
private TransferStatus status;
public TransferStatus Status
{
get { return this.status; }
protected set
{
TransferStatus oldStatus = this.status;
this.status = value;
OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
}
}
internal void Start(CancellationToken token)
{
this.transferTask = TransferAsync(cancellationToken);
}
protected abstract Task TransferAsync(CancellationToken cancellationToken);
protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
{
if (this.StatusChanged != null)
{
this.StatusChanged(this, txStatusArgs);
}
}
public TaskAwaiter GetAwaiter()
{
return this.transferTask.GetAwaiter();
}
}
The problem with the above is that if the TransferTask finishes very quickly, then users of TransferJob.Start() might not have time to register their event handlers on the returned TransferTask's StatusChanged event before it finishes. So I tried a different approach whereby the user has to call the TransferTask's Start() method themselves. This would give the user time to register their event handlers on the TransferTask in between the transferJob.CreateTask() call and the transferTask.Start() call:
public abstract class TransferJob
{
public abstract TransferTask CreateTask();
}
public abstract class TransferTask
{
public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;
private Task transferTask;
private TransferStatus status;
public TransferStatus Status
{
get { return this.status; }
protected set
{
TransferStatus oldStatus = this.status;
this.status = value;
OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
}
}
public void Start(CancellationToken token)
{
this.transferTask = TransferAsync(cancellationToken);
}
protected abstract Task TransferAsync(CancellationToken cancellationToken);
protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
{
if (this.StatusChanged != null)
{
this.StatusChanged(this, txStatusArgs);
}
}
public TaskAwaiter GetAwaiter()
{
return this.transferTask.GetAwaiter();
}
}
Now, I have a different problem. If a user tries await transferTask; before transferTask.Start(); has been called, then presumably they'll get a NullReferenceException thrown because the task hasn't been started (and therefore assigned to the transferTask field). I'm really struggling for a way to solve this. Is there a way? Or a better pattern to use than the above?
I'm not really convinced this is a good idea. Just expose the TAP pattern. Delete the event as well as transferTask. The caller of Start must hold onto that task and pass it to any code that wants to listen for completion. This results in a very clean API. No mutable state, very simple to understand, supports all use cases.
If you insist, you can create a proxy task that looks like it's the real thing:
public abstract class TransferTask
{
public event EventHandler<TransferStatusChangedEventArgs> StatusChanged;
private TaskCompletionSource<object> transferTask = new ...; //changed
private TransferStatus status;
public TransferStatus Status
{
get { return this.status; }
protected set
{
TransferStatus oldStatus = this.status;
this.status = value;
OnStatusChanged(new TransferStatusChangedEventArgs(oldStatus, value));
}
}
public Task Start(CancellationToken token)
{
await TransferAsync(cancellationToken);
transferTask.SetResult(null); //complete proxy task
}
protected abstract Task TransferAsync(CancellationToken cancellationToken);
protected virtual void OnStatusChanged(TransferStatusChangedEventArgs txStatusArgs)
{
if (this.StatusChanged != null)
{
this.StatusChanged(this, txStatusArgs);
}
}
public TaskAwaiter GetAwaiter()
{
return this.transferTask.Task.GetAwaiter(); //changed
}
}
Now, transferTask.Task is always not null. That task will eventually complete. I quickly hacked this together, I hope the idea is clear.
Probably, you should base the event on transferTask.Task.ContinueWith(...).
The best way I found when trying to mix events and awaitable code in C# is to use the Reactive Extension (Rx) library. From Microsoft:
Reactive Extension (Rx) is a library to compose asynchronous and event-based programs using observable collections and LINQ-style query operators.
You could do something like the following to fix your issue. (I am not sure this is exactly what you want to accomplish, but the goal is just to demonstrate how Rx can be used to combine events with asynchronous code):
public async Task TransferAndWaitStartedAsync()
{
var transferTask = new TransferTask();
// Prepare the observable before executing the transfer to make sure that the observable sequence will receive the event
// You can use Linq operators to filter only specific events. In this case, I only care about events with Status == StatusCode.Started
var whenStatusChanged = Observable.FromEventPattern<TransferStatusChangedEventArgs>(h, transferTask.StatusChanged += h, h => transferTask.StatusChanged -= h)
.Where(e => e.EventArgs.Status == StatusCode.Started)
.FirstAsync();
// Start the transfer asynchronously
await transferTask.TransferAsync();
// Continuation will complete when receiving the first event that matches the predicate in the observable sequence even if the event was triggered too quickly.
await whenStatusChanged;
}
I find that the Rx library has a steep learning curve with all its subtleties, but when you know how to use it, it is a really powerful tool.
Intro to Rx with lot of examples
Design guidelines
Assuming I have the following command
public class SignOutCommand : CommandBase, ISignOutCommand
{
private readonly UserModel _userModel;
private readonly IUserService _userService;
public SignOutCommand(IUserService userService)
{
_userService = userService;
_userModel = App.CurrentUser;
}
public bool CanExecute(object parameter)
{
var vm = parameter as EditProfileViewModel;
return vm != null;
}
public Task<bool> CanExecuteAsync(object parameter)
{
return Task.Run(() => CanExecute(parameter);
}
public void Execute(object parameter)
{
var vm = (EditProfileViewModel)parameter;
var signOutSucceeded = SignOutUser();
if (signOutSucceeded)
{
vm.AfterSuccessfulSignOut();
}
}
public Task ExecuteAsync(object parameter)
{
return Task.Run(() => Execute(parameter);
}
private bool SignOutUser()
{
// populate this so that we get the attached collections.
var user = _userService.GetByEmailAddress(_userModel.Email);
_userService.RevokeLoggedInUser(user);
return true;
}
}
And my XAML has a button
<Button Text="Sign out"
Command="{Binding SignOutCommand}"
CommandParameter="{Binding}" />
What would it take for this to execute the ExecuteAsync instead of Execute? Sorry if this is trivial, I'm quite new to XAML.
Also, I'm actually doing this in Xamarin.Forms XAML, not sure if it makes a difference here.
You could simply implement your command this way
public class SignOutCommand : ICommand
{
public bool CanExecute(object parameter)
{
var vm = parameter as EditProfileViewModel;
return vm != null;
}
public async void Execute(object parameter)
{
Task.Run(() =>
{
var vm = (EditProfileViewModel)parameter;
var signOutSucceeded = SignOutUser();
if (signOutSucceeded)
{
vm.AfterSuccessfulSignOut();
}
}
}
...
}
But then the bound button is not disabled during execution and the command can be executed even if it is already running...
If you need that the command cannot be executed during execution have a look at:
https://mytoolkit.codeplex.com/wikipage?title=AsyncRelayCommand
Source code: https://xp-dev.com/svn/mytoolkit/MyToolkit/Command/
Implementing the CanExecute asynchronously would be more tricky...
In the following method I'm sending an enumeration of actions and want an array of ICommands back that call Action<object> that wrap those actions (needed for the relayCommand).
The problem is that if I do this inside the for each (or even a for loop) I get commands that always execute the first action passed in the parameters.
public static ICommand[] CreateCommands(IEnumerable<Action> actions)
{
List<ICommand> commands = new List<ICommand>();
Action[] actionArray = actions.ToArray();
// works
//commands.Add(new RelayCommand(o => { actionArray[0](); })); // (_execute = {Method = {Void <CreateCommands>b__0(System.Object)}})
//commands.Add(new RelayCommand(o => { actionArray[1](); })); // (_execute = {Method = {Void <CreateCommands>b__1(System.Object)}})
foreach (var action in actionArray)
{
// always add the same _execute member for each RelayCommand (_execute = {Method = {Void <CreateCommands>b__0(System.Object)}})
commands.Add(new RelayCommand(o => { action(); }));
}
return commands.ToArray();
}
It seems that the lambda is always reused inside the loop thinking it does the same, but it does not.
How do I overcome this situation?
How can i force the loop to threat o => { action(); } always as a new one?
Thanks!
What I tried as per suggestions, but did not help:
foreach (var action in actionArray)
{
Action<object> executeHandler = o => { action(); };
commands.Add(new RelayCommand(executeHandler));
}
What seems to work for me is:
class RelayExecuteWrapper
{
Action _action;
public RelayExecuteWrapper(Action action)
{
_action = action;
}
public void Execute(object o)
{
_action();
}
}
/// ...
foreach (var action in actionArray)
{
RelayExecuteWrapper rxw = new RelayExecuteWrapper(action);
commands.Add(new RelayCommand(rxw.Execute));
}
Code of RelayCommand:
/// <summary>
/// A command whose sole purpose is to
/// relay its functionality to other
/// objects by invoking delegates. The
/// default return value for the CanExecute
/// method is 'true'.
/// </summary>
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");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
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
}
This problem is reported several times a week on StackOverflow. The problem is that each new lambda created inside the loop shares the same "action" variable. The lambdas do not capture the value, they capture the variable. That is, when you say
List<Action> list = new List<Action>();
foreach(int x in Range(0, 10))
list.Add( ()=>{Console.WriteLine(x);} );
list[0]();
that of course prints "10" because that's the value of x now. The action is "write the current value of x", not "write the value that x was back when the delegate was created."
To get around this problem make a new variable:
List<Action> list = new List<Action>();
foreach(int x in Range(0, 10))
{
int y = x;
list.Add( ()=>{Console.WriteLine(y);} );
}
list[0]();
Since this problem is so common we are considering changing the next version of C# so that a new variable gets created every time through the foreach loop.
See http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/ for more details.
UPDATE: From the comments:
Every ICommand has the same methodinfo:
{ Method = {Void <CreateCommands>b__0(System.Object)}}
Yes, of course it does. The method is the same every time. I think you are misunderstanding what a delegate creation is. Look at it this way. Suppose you said:
var firstList = new List<Func<int>>()
{
()=>10, ()=>20
};
OK, we have a list of functions that return ints. The first one returns 10, the second one returns 20.
This is just the same as:
static int ReturnTen() { return 10; }
static int ReturnTwenty() { return 20; }
...
var firstList = new List<Func<int>>()
{ ReturnTen, ReturnTwenty };
Make sense so far? Now we add your foreach loop:
var secondList = new List<Func<int>>();
foreach(var func in firstList)
secondList.Add(()=>func());
OK, what does that mean? That means exactly the same thing as:
class Closure
{
public Func<int> func;
public int DoTheThing() { return this.func(); }
}
...
var secondList = new List<Func<int>>();
Closure closure = new Closure();
foreach(var func in firstList)
{
closure.func = func;
secondList.Add(closure.DoTheThing);
}
Now is it clear what is going on here? Every time through the loop you do not create a new closure and you certainly do not create a new method. The delegate you create always points to the same method, and always to the same closure.
Now, if instead you wrote
foreach(var loopFunc in firstList)
{
var func = loopFunc;
secondList.Add(func);
}
then the code we would generate would be
foreach(var loopFunc in firstList)
{
var closure = new Closure();
closure.func = loopFunc;
secondList.Add(closure.DoTheThing);
}
Now each new function in the list has the same methodinfo -- it is still DoTheThing -- but a different closure.
Now does it make sense why you are seeing your result?
You might want to also read:
What is the lifetime of a delegate created by a lambda in C#?
ANOTHER UPDATE: From the edited question:
What I tried as per suggestions, but did not help:
foreach (var action in actionArray)
{
Action<object> executeHandler = o => { action(); };
commands.Add(new RelayCommand(executeHandler)); }
}
Of course that did not help. That has exactly the same problem as before. The problem is that the lambda is closed over the single variable 'action' and not over each value of action. Moving around where the lambda is created obviously does not solve that problem. What you want to do is create a new variable. Your second solution does so by allocating a new variable by making a field of reference type. You don't need to do that explicitly; as I mentioned above the compiler will do so for you if you make a new variable interior to the loop body.
The correct and short way to fix the problem is
foreach (var action in actionArray)
{
Action<object> copy = action;
commands.Add(new RelayCommand(x=>{copy();}));
}
That way you make a new variable every time through the loop and each lambda in the loop therefore closes over a different variable.
Each delegate will have the same methodinfo but a different closure.
I'm not really sure about these closure and lambdas
You're doing higher-order functional programming in your program. You'd better learn about "these closures and lambdas" if you want to have any chance of doing so correctly. No time like the present.
I just made a working example of exactly what you're trying to do: http://ideone.com/hNcGx
interface ICommand
{
void Print();
}
class CommandA : ICommand
{
public void Print() { Console.WriteLine("A"); }
}
class CommandB : ICommand
{
public void Print() { Console.WriteLine("B"); }
}
public static void Main()
{
var actions = new List<Action>();
foreach (var command in new ICommand[]{
new CommandA(), new CommandB(), new CommandB()})
{
var commandcopy = command;
actions.Add(() => commandcopy.Print());
}
foreach (var action in actions)
action();
}
Output:
A
B
B
Does this help any?
Make a local reference to action in the scope of the loop.
foreach (var action in actionArray)
{
var myAction = action;
// always add the same _execute member for each RelayCommand (_execute = {Method = {Void <CreateCommands>b__0(System.Object)}})
commands.Add(new RelayCommand(o => { action(); }));
}
You're only ever using the first item in the actionArray array.
ie
commands.Add(new RelayCommand(o => { actionArray[0](); }));
You need to iterate through the collection of actions.
eg
public static ICommand[] CreateCommands(IEnumerable<Action> actions)
{
commands = actions.Select(o => new RelayCommand(o)).ToArray();
}
Code is freehand so might be some typos but should point you in the right idea.