I have a strange behavior of the ObservableCollection in my MVVM app, more exactly in the part of the code responsible for receiving the messages from NServiceBus:
public class MyViewModel: ViewModelBase, IHandleMessages<CarMoved>
{
public ObservableCollection<CarData> Cars= new ObservableCollection<CarData>();
public Task Handle(CarMoved message, IMessageHandlerContext context)
{
...
Cars.Add(new Car());
return Task.CompletedTask;
}
}
So I expect the Handle method to add a new object into my ObservableCollection but the number of the objects remains the same.
I created the test button to check whether I can add a new object using a button and this testing button works fine.
I also debugged the Handle method and I can see that the number of the objects gets increased in the Observable collection while I am in the Handle method, but all that changes once I leave the method- the number of the objects returns to the old number.
I tried to add the object using Task.Run(..); Task.Wait(); presuming that maybe it needs some time. It did not help.
Please advise how I could resolve this issue and why it happens?
Ok, so the problem here is that NServiceBus will create a new instance of your ViewModel to handle the message. This is obviously not what you want.
Instead the ViewModel and message Handler should be separate objects. Then the handlers can tell the ViewModel about the message.
I'm not sure what MVVM framework you're using, but usually there's some sort of event aggregator for passing messages from system components like NServiceBus handlers and ViewModels.
Make sure that you access the ObservableCollection on the UI thread using the dispatcher:
public Task Handle(CarMoved message, IMessageHandlerContext context)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() => { Cars.Add(new Car()); }));
return Task.CompletedTask;
}
Alternatively you could try to use the BindingOperations.EnableCollectionSynchronization method to enable the collection to be accessed across multiple threads:
public class MyViewModel : ViewModelBase, IHandleMessages<CarMoved>
{
private readonly object _lock = new object();
public ObservableCollection<CarData> Cars = new ObservableCollection<CarData>();
public MyViewModel()
{
Application.Current.Dispatcher.Invoke(() => BindingOperations.EnableCollectionSynchronization(Cars, _lock));
}
public Task Handle(CarMoved message, IMessageHandlerContext context)
{
Cars.Add(new Car());
return Task.CompletedTask;
}
}
If you still cannot make you should read this: https://stackoverflow.com/help/mcve
Related
In my XAML UI I have a listview which contains a list of complex objects. These complex objects have an async initialization method which loads in the data (downloads an image, formats the text, etc).
Here's the setup (pseudo code):
public class PageViewModel
{
public ObservableCollection<ItemViewModel> Items;
public async Task InitializeAsync()
{
var models = await GetModelsAsync();
List<Task> initTasks = new List<Task>();
foreach(var model in models)
{
var vm = new ItemViewModel(model)
initTasks.Add(vm.InitializeAsync());
Items.Add(vm);
}
await Task.WhenAll(initTasks);
}
}
The issue i'm seeing is that it seems like the UI thread is being blocked and unresponsive until all the tasks have completed which is confusing me. All my async complex logic is in an awaitable task.
This lead me to experiment with the only other logic here, the creation of the view model. The issue seems to dissapear when I wrap the following code in a Task.Run:
var vm = Task.Run(() => new ItemViewModel(item))
This surprised me because there's very little to no logic in the ViewModel constructor which is why I was fine initially putting it on the UI thread.
Does anyone have thoughts on why I would see the UI thread block here? Do you have any code suggestions?
I can't be sure where you are calling your init() but you should be (unless you use an intelligent framework that automatically calls it for you) callingit from your Page OnNavTo override, like this:
public sealed partial class MainPage : Page
{
public MainPage() { InitializeComponent(); }
MainPageViewModel ViewModel => DataContext as MainPage;
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
DataContext = new MainPageViewModel();
await DataContext.InitAsync();
}
}
public class MainPageViewModel
{
public Task InitAsync()
{
// TODO
}
}
As an aside, I don't recommend you load your models in parallel, but instead in series because of performance on low-powered devices. Make sense? I should not make a noticeable difference to your user, bur your app will not lock if the generation is costly.
Best of luck!
I've got an ICommand that needs to set data to a property on the UI Thread.
public override async void Execute(object parameter)
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
}
Now I want to wrap my call in an event logger (I originally wanted to do AOP and decorate the method with a logging attribute, but I couldn't figure it out in a PCL). So I moved onto wrapping my call like this.
public override void Execute(object parameter)
{
EventLogger.LogEvent(this,
EventLogEntryType.Command,
EventLogErrorSeverity.Warning,
Errors.GetServiceAreaCommand_ErrorMessage,
async () =>
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
});
}
Here's the LogEvent method.
public static void LogEvent(object sender,
EventLogEntryType entryType,
EventLogErrorSeverity eventLogErrorSeverity,
string friendlyErrorMessage,
Action action)
{
var name = sender.GetType().Name.SplitCamelCase();
var startEntry = new EventLogEntry(entryType);
LogEvent(string.Format("Start: {0}", name), startEntry);
try
{
action.Invoke();
}
catch (Exception ex)
{
var exEntry = new EventLogEntry(EventLogEntryType.Error, friendlyErrorMessage, false, ex)
{
ErrorSeverity = eventLogErrorSeverity
};
LogEvent(string.Format("Error: {0}", name), exEntry);
if (eventLogErrorSeverity == EventLogErrorSeverity.Critical)
{
throw;
}
}
var endEntry = new EventLogEntry(entryType);
LogEvent(string.Format("Finish: {0}", name), endEntry);
}
The problem is that it appears as though I'm STILL setting the property on a background thread instead of the Main thread (IllegalStateException in Android).
What is the cleanest way to set the data as is being done in the first example, while still wrapping the Action in a logging method?
I also had success creating a base class for ICommand, but it A) changed the method signatures for CanExecute and Execute, and B) it also (obviously) doesn't extend it's capabilities beyond Commands.
I'm looking for a clean way to log methods (BeforeExecute, AfterExecute, OnError) no matter what they do.
As an aside, the ideal logging mechanism would be to use an Interceptor, but I'm just not strong enough in my C# chops to implement it.
[Log(EventLogEntryType.Command, EventLogErrorSeverity.Warning, "Some Friendly Message")]
public override async void Execute(object parameter)
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
}
If you have (caveat below) access to the Activity object in your code then you can probably do;
Activity.RunOnUiThread(() => {
//Execute my code on UIThread here
});
But it's an if, because I note you're using a PCL, or have referenced using one, so I suspect that a shared library is not going to know anything about the Activity (unless you pass that too). Very much depends on your app structure and where this code is, but within the main Xamarin.Android project where your views are the above should work
After hours of searching I am still without answer to this question. I have read this nice writing about async MVVM and made my viewmodel to use factory method.
public class MainViewModel
{
// sic - public, contrary to the pattern in the article I cite
// so I can create it in the Xaml as below
public MainViewModel()
{
}
private async Task InitializeAsync()
{
await DoSomethingAsync();
}
public static async Task<MainViewModel> CreateAsync()
{
var ret = new MainViewModel();
await ret.InitializeAsync();
return ret;
}
}
This is clear for me, but I can't understand how to make instance of MainViewModel and set it to datacontext in MainPage. I can't simply write
<Page.DataContext>
<viewModel:MainViewModel/>
</Page.DataContext>
because I should use MainViewModel.CreateAsync()-method. And I can't do it on code-behind, which I even want to do, because code-behind -constructor is normal method, not an async-method. So which is proper way to continue?
made my viewmodel to use factory method
I'm normally a fan of that approach - it's my favorite way to work around the "no async constructors" limitation. However, it doesn't work well in the MVVM pattern.
This is because VMs are your UI, logically speaking. And when a user navigates to a screen in an app, the app needs to respond immediately (synchronously). It doesn't necessarily have to display anything useful, but it does need to display something. For this reason, VM construction must be synchronous.
So, instead of trying to asynchronously construct your VM, first decide what you want your "loading" or "incomplete" UI to look like. Your (synchronous) VM constructor should initialize to that state, and it can kick off some asynchronous work that updates the VM when it completes.
This is not too hard to do by hand, or you can use the NotifyTaskCompletion approach that I described in an MSDN article on async MVVM data binding to drive the state transition using data bindings.
You have to initalize the viewmodel before the window is open. Go to your App.xaml file and remove the part: StartupUri="MainWindow.xaml". Then you go to the App.xaml.cs and add this:
protected async override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainWindow = new MainWindow { DataContext = await CreateAsync() };
mainWindow.Show();
}
I would re-factor. Make the MainViewModel construction / instantiation lightweight. Then create a Load or Initialize method on your VM. From the code-behind create an instance, set it to the DataContext, then invoke the init method and let it run.
E.g.
/// <summary>Interaction logic for MainWindow.xaml</summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
var dc = new MainViewModel();
dc.Initialize("Hello", " ", "world");
this.DataContext = dc;
}
}
public class MainViewModel
{
/// <summary>Simple constructor</summary>
public MainViewModel() { }
public void Initialize(params object[] arguments)
{
//use the task to properly start a new thread as per:
//http://stackoverflow.com/a/14904107/1144090 and
//https://msdn.microsoft.com/en-us/library/hh965065.aspx
//(what would happen if we simply invoke init async here?)
this.InitializeAsync(arguments)
.ContinueWith(result =>
{
if (!result.IsFaulted)
return;
MessageBox.Show("Unexpected error: " + Environment.NewLine + result.Exception.ToString());
});
}
private async Task InitializeAsync(params object[] arguments)
{
await Task.Delay(2333);
MessageBox.Show(String.Concat(arguments));
}
}
Note that this is the quick-and-dirty solution, the other two answers (paired with a dependency injection framework) will give you proper high-level structure for your solution.
Firstly, you should make default constructor as private to avoid misusing your class (the article you cite does this - the constructor is private).
The approach you are using to set DataContext is not suitable for MVVM pattern (the View shouldn't create its ViewModel itself).
You should create your View and ViewModel in the higher level layer and have that layer bind them. Says if the Page is your main View you should create them in App.xaml.cs by overriding OnStartup, something like this:
var page = new Page();
var dataService = new YourDataService(); // iff Create or the ctor require arguments
var viewModel = await MainViewModel.CreateAsync(dataService);
page.DataContext = viewModel;
page.Show();
I have an application that involves a database. Previously, upon opening a window, I would query the database and use this to populate aspects of my view model. This worked reasonably well, but could create noticeable pauses when the data access took longer than expected.
The natural solution, of course, is to run the database query asynchronously and then populate the view model when that query completes. This isn't too hard, but it raises some interesting questions regarding error handling.
Previously, if something went wrong with the database query (a pretty big problem, granted), I would propagate the exception through the view model constructor, ultimately making it back up to the caller that wanted to open the window. It could then display an appropriate error and not actually open the window.
Now, however, the window opens right away, then populates later as the query completes. The question, now, is at what point should I check for an error in the background task? The window is already open, so the behavior needs to be different somehow, but what is a clean way to indicate the failure to the user and allow for graceful recovery/shutdown?
For reference, here is a snippet demonstrating the basic pattern:
public ViewModel()
{
_initTask = InitAsync();
//Now where do I check on the status of the init task?
}
private async Task InitAsync()
{
//Do stuff...
}
//....
public void ShowWindow()
{
var vm = new ViewModel(); //Previously this could throw an exception that would prevent window from being shown
_windowServices.Show(vm);
}
One option I've considered is use an asynchronous factory method for constructing the ViewModel, allowing the entire thing to be constructed and initialized before attempting to display the window. This preserves the old approach of reporting errors before the window is ever opened. However, it gives up some of the UI responsiveness gained by this approach, which allows initial loading of the window to occur in parallel with the query and also allows me (in some cases) to update the UI in increments as each query completes, rather than having the UI compose itself all at once. It avoids locking up the UI thread, but it doesn't reduce the time before the user actually sees the window and can start interacting with it.
Maybe use some kind of messaging/mediator between your viewmodel and underlying service?
Semi-pseudo code using MVVMLight
public ViewModel()
{
Messenger.Default.Register<NotificationMessage<Exception>>(this, message =>
{
// Handle here
});
Task.Factory.StartNew(() => FetchData());
}
public async Task FetchData()
{
// Some magic happens here
try
{
Thread.Sleep(2000);
throw new ArgumentException();
}
catch (Exception e)
{
Messenger.Default.Send(new NotificationMessage<Exception>(this, e, "Aw snap!"));
}
}
I dealt with a similar problem here. I found it'd be best for me to raise an error event from inside the task, like this:
// ViewModel
public class TaskFailedEventArgs: EventArgs
{
public Exception Exception { get; private set; }
public bool Handled { get; set; }
public TaskFailedEventArgs(Exception ex) { this.Exception = ex; }
}
public event EventHandler<TaskFailedEventArgs> TaskFailed = delegate { };
public ViewModel()
{
this.TaskFailed += (s, e) =>
{
// handle it, e.g.: retry, report or set a property
MessageBox.Show(e.Exception.Message);
e.Handled = true;
};
_initTask = InitAsync();
//Now where do I check on the status of the init task?
}
private async Task InitAsync()
{
try
{
// do the async work
}
catch (Exception ex)
{
var args = new TaskFailedEventArgs(ex);
this.TaskFailed(this, args);
if (!args.Handled)
throw;
}
}
// application
public void ShowWindow()
{
var vm = new ViewModel(); //Previously this could throw an exception that would prevent window from being shown
_windowServices.Show(vm);
}
The window still shows up, but it should be displaying some kind of progress notifications (e.g. using IProgress<T> pattern), until the end of the operation (and the error info in case it failed).
Inside the error event handler, you may give the user an option to retry or exit the app gracefully, depending on your business logic.
Stephen Cleary has a series of posts on his blog about Async OOP. In particular, about constructors.
I have a WPF application that initializes the state of the UI via methods in the constructor. However, it's never returning from the Wait(); in the constructor.
Here's what I am currently doing via a fairly-contrived sample:
public class SomeViewModel
{
public ICommand AddDataCommand { get { return RelayCommand(AddDataExecute); } }
public ObservableCollection<int> UIData { /* Property with INotifyPropertyChanged */}
public SomeViewModel()
{
//Load synch. here
LoadData().Wait();
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
public async void AddDataExecute()
{
//Add new data item to database on separate thread to keep UI responsive
await Task.Run(() => SomeService.AddNewData(0));
//Reload data from database and update UI, has to happen after AddNewData completes
await LoadData();
}
}
I believe it's hanging because I am never actually returning a Task. However, I don't know of a different way to assign UIData asynchronously that works both in the constructor and the Commands that call it.
You're seeing the classic deadlock situation that I describe on my blog.
To solve it, use async all the way, as I describe in my MSDN article on async best practices.
This can be difficult in some scenarios. I have a blog post describing a few approaches for async constructors. However, since you're talking about a ViewModel and data for the UI, you'll probably find my blog post on async properties more helpful - in particular, the section on data binding.
Don't construct the object through a constructor, if you require construction to be asynchronous. Use a static factory method:
public class SomeViewModel
{
private SomeViewModel()
{ }
public static async Task<SomeViewModel> Create()
{
SomeViewModel model = new SomeViewModel();
await model.LoadData();
return model;
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
//...
}
As for why your code isn't working, you're getting the standard await deadlock in which you're blocking on an asynchronous operation, that blocking is preventing continuations from being called on the UI thread, and with two different operations each waiting on the other to continue, you get a deadlock. This is why it's important to "async all the way up" rather than synchronously blocking on an asynchronous operation.