How to use json.net asynchronously in WinRT's app ViewModel? - c#

I have MVVM(Prism) Windows Store app and I don't understand how to do asynchronous serialization/deserialization using Json.NET library(Version 6.0.4) in ViewModel layer.
I have method which is bound to GridView's ItemClick event:
public async void GridViewClick(object sender, ItemClickEventArgs parameter)
{
if (App.IsInternet())
{
if (parameter != null)
_navigationService.Navigate("AnimeDetails",
await Task.Run(() => JsonConvert.SerializeObject(parameter.ClickedItem)));
}
else
{
new MessageDialog(ResourceController.GetTranslation("MainPage_FeatureUnavaliableOffline")).ShowAsync();
}
}
This method throws me following Exception:
The application called an interface that was marshalled for a different thread. (Исключение из HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I tried to use Dispatcher, but it didn't help me(similar wrong thread exception).
public async void GridViewClick(object sender, ItemClickEventArgs parameter)
{
if (App.IsInternet())
{
var serializedItem = string.Empty;
await
Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
await Task.Run(() => serializedItem = JsonConvert.SerializeObject(parameter.ClickedItem)));
if (parameter != null)
_navigationService.Navigate("AnimeDetails", serializedItem);
}
else
{
new MessageDialog(ResourceController.GetTranslation("MainPage_FeatureUnavaliableOffline")).ShowAsync();
}
}
Could anybody please explain me, how to do serialization/deserialization correctly?

Usually, serialization is fast enough you can just do it directly without messing with background threads at all:
if (parameter != null)
_navigationService.Navigate("AnimeDetails",
JsonConvert.SerializeObject(parameter.ClickedItem));
However, if your objects are really huge and you're sure you want to use a background thread, then you just have to do any UI access (i.e., ItemClickEventArgs.ClickedItem) on the UI thread and serialization on the background thread:
if (parameter != null)
{
var item = parameter.ClickedItem;
_navigationService.Navigate("AnimeDetails",
await Task.Run(() => JsonConvert.SerializeObject(item)));
}

Related

Process items from datagrid WPF one by one when use BackgroundWorker

I want to process items one by one, every item from a datagrid element.
I have a method which creates a background worker:
internal void Run(Action doWork, Action completed, Action loadingBeforeAction = null, Action loadingAfterAction = null)
{
using (BackgroundWorker worker = new BackgroundWorker())
{
worker.DoWork += (s, ev) =>
{
if (loadingBeforeAction != null)
{
_dispatcher.Invoke(loadingBeforeAction);
}
doWork();
};
worker.RunWorkerCompleted += (s, ev) =>
{
if (loadingAfterAction != null)
{
_dispatcher.Invoke(loadingAfterAction);
}
completed();
};
worker.RunWorkerAsync();
}
}
And now process the selected items from datagrid:
var folders = btn.Name.Equals("test")
? _model.Folders.ToArray()
: fileDataGrid.SelectedItems.Cast<FolderStatus>().ToArray();"
and
foreach (var folder in folders)
{
Run(() =>
{
Dispatcher.Invoke(()=> {_model.Message = $"This is message for {folder.Name}"});
// long operation here
}, () =>
{
// stuff
}, () =>
{
_model.IsBusy = true;
}, () =>
{
_model.IsBusy = false;
});
}
Seems that all items are processed simultaneously and process message flick from text to other depends on _model.Message text.
How to process item one by one but without blocking the UI?
BackgroundWorker is a legacy component from Windows Forms. You shouldn't use it with WPF applications. It will work, but there are better ways to implement what you want.
The easiest way is to use the TAP (Task-based Asynchronous Programming) pattern, supported by the async and await C# keywords.
If you're targeting at least the .NET Framework 4.5 or .NET Core, you have everything out-of-the box. For .NET Framework 4.0, there are NuGet packages to enable this functionality.
The implementation could look like this:
async void MyButtonClick(object sender, RoutedEventArgs e)
{
var btn = (Button)sender;
var folders = btn.Name.Equals("test")
? _model.Folders.ToArray()
: fileDataGrid.SelectedItems.Cast<FolderStatus>().ToArray();"
foreach (var folder in folders)
{
// This will be executed on the UI thread
_model.Message = $"This is message for {folder.Name}"};
_model.IsBusy = true;
// This will be executed on a worker thread due to Task.Run
await Task.Run(() => YourLongRunningOperation(folder));
await Task.Run(() => SomeOtherLongRunningOperation());
// This will be executed again on the UI thread
_model.IsBusy = false;
}
}
A couple of notes:
The method above has a signature async void (object, EventArgs). However, async methods should return a Task or a Task<T>. async void is here to make the event handler async. This is the only case when you should use async void - UI event handlers. Note that an uncaught exception in an async void UI event handler will crash your application.
When await Task.Run() returns, the thread context will be restored to the original one. So, your _model.IsBusy = false code will be executed on the UI thread again. If you don't need this, use await Task.Run().ConfigureAwait(false).
Read more about the Task-based asynchronous pattern (TAP).
Here is a short but nice FAQ and tutorial too.

C# WPF - How to load data to datagrid in thread

I have a problem with application freezes for a few seconds.
I loading data from XML file and deserialize to MyList.
public List<My20FieldsDataRecord> MyList;
...
void ShowDataInThread()
{
MyGrid.DataContext = MyList;
}
public void ShowDane(bool inThread)
{
if (inThread)
{
Thread thr = new Thread(ShowDataInThread);
thr.Start();
}
else
{
ShowDataInThread();
}
}
if inThread = false everything work fine, but application not responding for a 2-3 seconds.
When inThread = true application crash.
I want do this in thread, but i was not able to understand that how it works from examples on internet. I'll be very grateful for your help, becouse i have no idea how to do that.
Since Microsoft introduced the async / wait approach for .NET Framework programming in .NET 4.5, the code for async methods is a lot.
You can not find any async async with as example as such as type:
private async void button1_Click(object sender, EventArgs e)
{
string result = await AnMethodAsync();
textBox1.Text += result;
}
private Task<string> AnMethodAsync()
{
//Do somethine async
}
And you think this is done, the function will run async do not have to worry about hanging thead anymore, too strong.
But the problem is not so simple.
Now try to put in the AnMethodAsync () function the following code:
Thread.Sleep(5000);
return Task.FromResult("HoanHT");
Run the code above and when you press button1, the UI will hang stiff for 5s.
What the hell, I have applied async / await properly that the UI is still hanging.
After a brief look at the problem: In the AnMethodAsync function does not create any other task on the other thread. The consequence is that asyn away, but it still runs on UI thread -> UI freeze.
Then fix it, there are two ways:
Method 1: Create a new task and executable:
return Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
return "HoanHT";
});
Method 2: Use Task.Delay () instead of Thread.Sleep ()
private async Task<string> AnMethodAsync()
{
await Task.Delay(5000);
return "HoanHT";
}
This is the way I usually do with the asynchronous problem, plus one more way is to use ConfigureAwait () for the Task but will cause minor problems.
Here is a way that I've found to load data for a datagrid in the background while not blocking your UI.
First, create a lock object, and enable collection synchronization, then actually load the data on a background thread using Task.Run():
private readonly object _myDataLock = new object();
private FastObservableCollection<My20FieldsDataRecord> MyList = new FastObservableCollection<My20FieldsDataRecord>();
private CollectionViewSource MyListCollectionView = new CollectionViewSource();
public MyViewModelConstructor() : base()
{
// Other ctor code
// ...
// assign the data source of the collection views
MyListCollectionView.Source = MyList;
// Setup synchronization
BindingOperations.EnableCollectionSynchronization(MyList, _myDataLock);
}
private async void LoadMyList()
{
// load the list
await Task.Run(async () =>
{
MyList.ReplaceAll(await MyRepository.LoadMyList());
}
);
}
Then in your repository you could write:
public virtual async Task<IList<My20FieldsDataRecord>> LoadMyList()
{
var results = await this.DataContext.TwentyFieldDataRecords
.OrderByDescending(x => x.MyDate).ToListAsync().ConfigureAwait(false);
return results;
}
Then you could bind in your associated view like this:
<controls:DataGrid Name="MyListDataGrid" Grid.Row="1"
....
ItemsSource="{Binding MyListCollectionView.View}"
... >
For details, please see:
https://blog.stephencleary.com/2014/04/a-tour-of-task-part-0-overview.html
http://blog.stephencleary.com/2012/02/async-and-await.html#avoiding-context
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.7);k(DevLang-csharp)&rd=true
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

Load large sets of data in ViewModel without blocking UI

I'd like to load my viewmodel after a page has loaded but I can't seem to figure out how to do this.
Here's the viewModel methods where both methods inside LoadViewModel are long running async methods:
public async Task LoadViewModel()
{
await GetAllTiles();
await UpdateAllScenarioCardsAsync();
}
In the view I'm trying something like this:
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
var viewModel = DataContext as StatsViewModel;
if (viewModel != null)
{
var statsViewModel = viewModel;
await statsViewModel.LoadViewModel();
}
}
For some reason the LoadViewModel() method blocks the entire UI even if I remove the awaits for GetAllTiles() and UpdateAllScenarioCardsAsync()... The NavigationHelper_LoadState method is run before the page is loaded so I've tried registering LoadViewModel() to the Loaded event of the page but I can't seem to get it to work.
EDIT
Here is my UpdateAllScenarioCardsAsync() class. UpdateTotalTilesAsync() and UpdataTodayTilesAsync() have await statements inside the code as well but it still blocks the UI. I used closedScenario because I thought the issue could be closing over a variable over the wrong scope like answered in this question, but still now luck. I'm tempted to think it has something to do with the foreach loop because I have successfully done this elsewhere in my solution and it doesn't block the UI thread, but that code had no foreach loop.
private async Task UpdateAllScenarioCardsAsync()
{
IsPending = true;
try
{
// Load all of the scenario cards
foreach (var scenario in _scenariosList)
{
var closedScenario = scenario;
var data = new ScenarioDataCard(closedScenario);
await UpdateTotalTiles(data);
await UpdateTodayTestedTiles(data);
ScenarioDataCards.Add(data);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
IsPending = false;
}
}
You need to add an await inside the LoadState event handler. Otherwise it will simply block whilst waiting for the LoadViewModel() to return. Like this...
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
var viewModel = DataContext as StatsViewModel;
if (viewModel != null)
{
await viewModel.LoadViewModel();
}
}
I also assume that the GetAllTiles and UpdateAllScenarioCardsAsync methods are implemented so that they really do perform work on another thread so that they are not blocking the main user interface thread.

How do I update the GUI on the parent form when I retrieve the value from a Task?

I think I'm missing something obvious here, but how do I update the GUI when using a task and retrieving the value? (I'm trying to use await/async instead of BackgroundWorker)
On my control the user has clicked a button that will do something that takes time. I want to alert the parent form so it can show some progress:
private void ButtonClicked()
{
var task = Task<bool>.Factory.StartNew(() =>
{
WorkStarted(this, new EventArgs());
Thread.Sleep(5000);
WorkComplete(this, null);
return true;
});
if (task.Result) MessageBox.Show("Success!");//this line causes app to block
}
In my parent form I'm listening to WorkStarted and WorkComplete to update the status bar:
myControl.WorkStarting += (o, args) =>
{
Invoke((MethodInvoker) delegate
{
toolStripProgressBar1.Visible = true;
toolStripStatusLabel1.Text = "Busy";
});
};
Correct me if I'm wrong, but the app is hanging because "Invoke" is waiting for the GUI thread to become available which it won't until my "ButtonClicked()" call is complete. So we have a deadlock.
What's the correct way to approach this?
You're blocking the UI thread Task.Result blocks until the task is completed.
Try this.
private async void ButtonClicked()
{
var task = Task<bool>.Factory.StartNew(() =>
{
WorkStarted(this, new EventArgs());
Thread.Sleep(5000);
WorkComplete(this, null);
return true;
});
await task;//Wait Asynchronously
if (task.Result) MessageBox.Show("Success!");//this line causes app to block
}
You can use Task.Run to execute code on a background thread. The Task-based Asynchronous Pattern specifies a pattern for progress updates, which looks like this:
private async void ButtonClicked()
{
var progress = new Progress<int>(update =>
{
// Apply "update" to the UI
});
var result = await Task.Run(() => DoWork(progress));
if (result) MessageBox.Show("Success!");
}
private static bool DoWork(IProgress<int> progress)
{
for (int i = 0; i != 5; ++i)
{
if (progress != null)
progress.Report(i);
Thread.Sleep(1000);
}
return true;
}
If you are targeting .NET 4.0, then you can use Microsoft.Bcl.Async; in that case, you would have to use TaskEx.Run instead of Task.Run. I explain on my blog why you shouldn't use Task.Factory.StartNew.

How do I force a task to run on the UI thread?

Original message below. Let me try and explain with more details why I am asking for this.
I have a page that listens to the Share charm request:
void Page_Loaded(object sender, RoutedEventArgs e)
{
m_transferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.GetForCurrentView();
m_transferManager.DataRequested += TransferManager_DataRequested;
}
When the event fires (TransferManager_DataRequested) it does not fire on the UI thread:
void TransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var data = args.Request.Data;
// More related stuff omitted - not important.
data.SetDataProvider(StandardDataFormats.Bitmap, GetImage_DelayRenderer);
}
Now, when GetImage_DelayRender is called, it also does not get called on the UI thread. However, in it, I need to do a bunch of UI related things. Specifically, I need to call a method that only works on the UI (it's a method I use elsewhere and I want to reuse it's logic). The method is called GetImageAsync and it needs to run on the UI because it does a bunch of interactions with WriteableBitmap. It also does a bunch of async operations (such as writing to stream etc) which is why it's async. I block the UI on GetImageAsync() for as short a time as I can.
Here's what GetImage_DelayRender looks like:
private async void GetImage_DelayRenderer(DataProviderRequest request)
{
var deferral = request.GetDeferral();
await Dispatcher.RunTask(async () => // RunTask() is an extension method - described in the original question below.
{
try
{
var bitmapStream = await GetImageAsync();
request.SetData(RandomAccessStreamReference.CreateFromStream(bitmapStream));
}
catch
{
}
});
deferral.Complete();
}
What I want to know is, what is the most correct way to achieve the call to Dispatcher.RunTask() above (which is my hack extension method).
----- START ORIGINAL MESSAGE -------
Say I have the following task:
private async Task SomeTask()
{
await Task.Delay(1000);
// Do some UI and other stuff that may also be async
}
Edit (Clarification): I do not want to block the UI. The task I want to execute (even in the example, if you read it) WILL NOT block the UI. I just want the task to run in the context of the UI for it's synchronous portions.
I want to run this on code on the UI thread as an Async operation. Dispatcher.RunXXX() methods take an action, which means they will run the action and notify you when they are done. That's not good enough. I need the entire task to run on the UI thread (as it would have executed had I run it from the UI thread) and then, when done, to notify me back.
The only way I could think of, is to use the Dispatcher.RunXXX() methods to execute an anon delegate that sets a local variable in my method to the task and then awaits that...
public async static Task RunTask(this CoreDispatcher dispatcher, Func<Task> taskGiver)
{
Task task = null;
await dispatcher.RunAsync(() => task = taskGiver());
await task;
}
This looks pretty damn ugly. Is there a better way of doing it?
Edit2: Guys - read this code - if I execute the first code block above using the RunTask() hack I have, IT WILL NOT BLOCK THE UI on the Task.Delay()...
I want to run this on code on the UI thread as an Async operation.
Then just run it:
async void MyEventHandler(object sender, ...)
{
await SomeTask();
}
Update:
I'm not sure this is a "legal" operation, but you can schedule that method to run on the UI thread by capturing the CoreDispatcher while the UI is active and later calling RunAsync:
private async void GetImage_DelayRenderer(DataProviderRequest request)
{
var deferral = request.GetDeferral();
Task task = null;
await coreDispatcher.RunAsync(() => { task = SomeTask(); });
await task;
deferral.Complete();
}
I don't have time to do a complete solution, so hopefully you will still find this useful...
First, as others have pointed out, you cannot run something on the UI thread and not have it block the UI thread. End of discussion. What you are saying you need is something to run on a non-UI thread and periodically notify the UI thread that there are updates that need to be processed.
To accomplish this, you need something like this...
public class LongTask
{
public event EventHandler MyEvent;
public void Execute()
{
var task = Task.Factory.StartNew(() =>
{
while (true)
{
// condition met to notify UI
if (MyEvent != null)
MyEvent(this, null);
}
});
}
}
In your UI then, do something like...
private void button_Click(object sender, RoutedEventArgs e)
{
var test = new LongTask();
test.MyEvent += test_MyEvent;
test.Execute();
}
void test_MyEvent(object sender, EventArgs e)
{
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
test.Text += " bang ";
});
You could obviously implement this in a much cleaner fashion using something like MVVM, but this is the basic idea.
}
I've done it like this:
public static Task<string> GetResultAsync()
{
return Task<string>.Factory.StartNew(() => GetResultSync());
}
In UI:
private async void test()
{
string result = await GetResultAsync();
// update UI no problem
textbox.Text = result;
}

Categories