I have a component with the following event:
// MyComponent :
[Parameter] public EventCallback<DataChangedEventArgs> OnTextChanged { get; set; }
protected async void SomeMethod(ChangeEventArgs args)
{
DataChangedEventArgs evArg =new DataChangedEventArgs(args.value);
Console.Writeline("1");
await OnTextChanged.InvokeAsync(evArg);
Items = evArg.Data; // <=== problem here: Data is always null.
Console.Writeline("4");
}
When I use and handle the OnTextChanged event on another component, I set the Data property of DataChangedEventArgs:
// AnotherComponent that uses MyComponent:
public async void theTextChanged(DataChangedEventArgs args)
{
//...
Console.Writeline("2");
args.Data = await GetAPersonAsync(); // or some other object
Console.Writeline("3");
}
Now I expect Items to be set to the Person (the line Items = evArg.Data). But Data is always null, which means the changes that have been made to args.Data(which is a reference type) in event handler are not accessible in the invoking method the SomeMethod.
Can anyone help me with that ?
UPDATE: I think it has something to do with async method, as the console result is not as I expect (1,2,3,4):
console:
1
2
4
3
You should use async Task instead of async void.
Theodor provided a link to the documentation
To add some meat to #Mister Magoo's answer, I believe the Blazor code for the event handlers looks something like this (simplified for clarity):
var task = InvokeAsync(EventMethod);
StateHasChanged();
if (task.Status != TaskStatus.RanToCompletion && task.Status != TaskStatus.Canceled)
{
await task;
StateHasChanged();
}
If your EventMethod returns a void, InvokeAsync returns a completed Task as soon as EventMethod yields. Only one StateHasChanged gets called - the if block is skipped. Anything that happens in EventMethod after the yield is not reflected in the UI until another component UI update occurs.
Related
I'm developing WinForms App in VS2013, .NET FW 4.5.1. Here is my reduced code with inline comments about structure:
// Progress object implementing IProgress<MyProgressData>
var progressCallback = new Progress<MyProgressData>();
// listOfMyList is actually List<List<MyObject>>, which contains list of
// list of MyObject's which will be executed as tasks at once.
// For example, this would be sample structure for list of lists:
// List1
// MyObject1
// MyObject2
// MyObject3
// List2
// MyObject4
// MyObject5
// MyObject6
// List1's and List2's objects would be executed as all tasks at once, but List1 and List2 respectively
// would be executed one after another (because of resources usage inside TASK CODE)
foreach (var myItem in listOfMyList)
{
var myList = myItem.ToList();
// Create a list of tasks to be executed (20 by default; each taking from 30-60 seconds)
// Here cs is actually MyObject
var myTasks = myList.Select(cs => Task.Run(async () =>
{
// TASK CODE (using cs as an input object and using "cancellationToken.ThrowIfCancellationRequested();" inside execution to cancel executing if requested)
}, cancellationToken));
await Task.WhenAll(myTasks); // Wait for all tasks to finish
// Report progress to main form (this actually calls an event on my form)
await Task.Run(() => progressCallback.Report(new MyProgressData() { props }), CancellationToken.None);
}
As you can see, I construct progress object and then I have list of lists. Each item within top-level list should execute in serialized fashion (one after another). Each item's list elements, should execute all at once in a form of a tasks.
So far so good, all tasks start and even WhenAll waits for them. Or at least I thought so. I have put logging inside relevant methods, to show me code execution. It turns out that while progress logic (at the bottom) is executing, foreach loop starts executing another batch of tasks, which it shouldn't.
Am I missing something here? Does code for progress not block or wait for Report method to finish executing. Maybe I'm missing sth about async/await. With await we make sure that code won't continue until after method is finished? It won't block current thread, but it also won't continue executing?
Is it even possible (since its happening, it probably is), for my foreach loop to continue executing while progress reporting is still on the go?
This code resides inside an async method. It's actually called like this (lets assume this method is async MyProblematicMethod()):
while (true)
{
var result = await MyProblematicMethod();
if (result.HasToExitWhile)
break;
}
Every method up from MyProblematicMethod uses await to wait for async methods, and is not called many times.
Based on Glorin's suggestion that IProgress.Report returns immediately after firing an event handler, I've created exact copy of Progress class, which uses synchronizationContext.Send instead of Post:
public sealed class ProgressEx<T> : IProgress<T>
{
private readonly SynchronizationContext _synchronizationContext;
private readonly Action<T> _handler;
private readonly SendOrPostCallback _invokeHandlers;
public event EventHandler<T> ProgressChanged;
public ProgressEx(SynchronizationContext syncContext)
{
// From Progress.cs
//_synchronizationContext = SynchronizationContext.CurrentNoFlow ?? ProgressStatics.DefaultContext;
_synchronizationContext = syncContext;
_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
}
public ProgressEx(SynchronizationContext syncContext, Action<T> handler)
: this(syncContext)
{
if (handler == null)
throw new ArgumentNullException("handler");
_handler = handler;
}
private void OnReport(T value)
{
// ISSUE: reference to a compiler-generated field
if (_handler == null && ProgressChanged == null)
return;
_synchronizationContext.Send(_invokeHandlers, (object)value);
}
void IProgress<T>.Report(T value)
{
OnReport(value);
}
private void InvokeHandlers(object state)
{
T e = (T)state;
Action<T> action = _handler;
// ISSUE: reference to a compiler-generated field
EventHandler<T> eventHandler = ProgressChanged;
if (action != null)
action(e);
if (eventHandler == null)
return;
eventHandler((object)this, e);
}
}
This means that ProgressEx.Report will wait for method to finish, before returning. Maybe not the best solution in all situations, but it worked for me in this case.
To call it, just create ProgressEx with SynchronizationContext.Current as a parameter to constructor. However, it must be created in UI thread, so the right SynchronizationContext gets passed in. E.g. new ProgressEx<MyDataObject>(SynchronizationContext.Current)
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.
So here's deleagte and event
public delegate Task SomeEventHandler(SomeEventArgs e);
...
public event SomeEventHandler OnSomething;
Subscribers (multiple)
some.OnSomething += DoSomething;
...
public async Task DoSomething(SomeEventArgs e) {
await SomethingElse();
e.A = true;
}
Event call
if (this.OnSomething != null)
await this.OnSomething(args);
// Here args.A is false
// It should be true
The problem is that last part continues even when DoSomething isn't finished. What would be the problem?
The problem here is that multiple instances of SomeEventHandler are running hence there are multiple Task values being created. The await call is only running on one of them hence it's somewhat up to chance as to whether or not it's theDoSomething method that ends up being awaited.
To fix this you will need to await on every Task value that is created
if (this.OnSomething != null) {
foreach (var d in this.OnSomething.GetInvocationList().Cast<SomeEventHandler>()) {
await d(args);
}
]
I'm new in C# async/await and facing some issues while trying to work with async method.
I have a collection:
private IList<IContactInfo> _contactInfoList
And an async method:
public async Task<IList<IContactInfo>> SelectContacts()
{
_contactInfoList = new List<IContactInfo>();
ContactsSelector selector = new ContactsSelector();
selector.ShowPicker();
selector.ContactsSelected += (object sender, ContactsSelectorEventArgs e) =>
{
this._contactInfoList = e.Contacts;
};
return _contactInfoList;
}
Contact selector is a popup user control which allows to select some contacts from phone and after the "OK" button tapped it fires ContactsSelected event. I need to get the selected contacts list from the event arguments e.Contacts and return that list in above mentioned SelectContacts() async method. And here is the issue: My method is already returning empty list _contactInfoList before the ContactsSelected event has finished his job. I know that async/await even doesn't matter in this case and this issue will be exist in usual method, but I just need to make that method to wait event handling result.
What you need to do here is convert an event style of asynchronous programming to a task style of asynchronous programming. The use of a TaskCompletionSource make this fairly straightforward.
public static Task<IList<IContactInfo>> WhenContactsSelected(
this ContactsSelector selector)
{
var tcs = new TaskCompletionSource<IList<IContactInfo>>();
selector.ContactsSelected += (object sender, ContactsSelectorEventArgs e) =>
{
tcs.TrySetResult(e.Contacts);
};
return tcs.Task;
}
Now that we have a method that returns a task with the result that we need, the method that uses it is quite straightforward:
public Task<IList<IContactInfo>> SelectContacts()
{
ContactsSelector selector = new ContactsSelector();
selector.ShowPicker();
return selector.WhenContactsSelected();
}
There are a few things to note here. First, I removed the instance field; that seems like a bad idea here. If SelectContacts is called several times it would result in the two fighting over that field. Logically if you do need to store the list it should be a local variable. Next, there are no await uses here, so the method shouldn't be marked as async. If you wanted to await the call to WhenContactsSelected then feel free to add async back in, but as of now I see no real need for it.
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;
}