I'm working on an app and I'm restructuring my code.
On my MainPage.xaml.cs I have a TextBlock and a ListBox. I have separate file (Info.cs) that handles the HttpRequest to get the information that I need to load.
The HttpRequest in Info.cs gets information from a weather API. When it gets all the information it puts the info in a ObservableCollection.. This ObservableCollection is bind to the ListBox.
Now, I'd like to update the TextBlock when the HttpRequest is finished, to show the user that all the information has been loaded.
How can I achieve this?
MainPage.xaml.cs:
WeatherInfo weatherInfo = new WeatherInfo();
weatherInfo.getTheWeatherData();
DataContext = weatherInfo;
WeatherListBox.ItemsSource = weatherInfo.ForecastList;
StatusTextBlock.Text = "Done.";
In the Info.cs I have a Dispatcher to fill the ForecastList:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
ForecastList.Clear();
ForecastList = outputList;
}
What happens now is that the TextBlock instantly changes to "Done!" (doh, its Async!) but how can I change this? So it 'waits' on the ListBox to be updated? Unfortunatly there is no 'ItemsSourceChanged' event in Windows Phone.
I suggest to use the new async+await power from C# 5.0, This is actually a good practice to use async programming in WP8.
assuming you have control of getTheWeatherData() method, and that you can mark it as async method that returns Task, you will be able to call it using await modifier.
await will not block the UI, and will cause the next code lines to be executed only after the task is done.
WeatherInfo weatherInfo = new WeatherInfo();
await weatherInfo.getTheWeatherData();
DataContext = weatherInfo;
WeatherListBox.ItemsSource = weatherInfo.ForecastList;
StatusTextBlock.Text = "Done.";
Edit:
It is supported on WP 8, and on WP 7.5 through Microsoft.Bcl.Async Nuget Package
If async programming is not an option,
you can always create a callback event in WeatherInfo class that will be signaled inside getTheWeatherData(), and register to it on the UI.
One option looks as follows:
public static void DoWork(Action processAction)
{
// do work
if (processAction != null)
processAction();
}
public static void Main()
{
// using anonymous delegate
DoWork(delegate() { Console.WriteLine("Completed"); });
// using Lambda
DoWork(() => Console.WriteLine("Completed"));
}
Both DoWork() calls will end with calling the callback that is passed as a parameter.
Related
I have a project that needs notifications about upcoming events to show when the user opens the main page, and I'm having problems with async and await (I think). I am using the LocalNotifications Nuget plugin, because that's the only one I was allowed to use for this project. I have some methods that are intended to check for and display notifications. I have tried loading them from my App file, My MainPage.Xaml.cs's OnAppearing method, and from my MainPageViewModel.cs, and the closest that I've gotten to something working is with a combination of creating the Notifications as a class, adding them from the ViewModel and displaying them in the OnAppearing Method of the code behind. The code works right up until the if statement's body in the OnAppearing Method tries to execute and then it just stops and continues on with displaying the main page, but never showing the notifications. I am really new to using asynchronous programming, as well as Xamarin, so I'm pretty confused. This is my relevant code.
//Code starts in MainPageViewModel.cs
public MainPageViewModel()
{
Task.Run(async() => await CheckNotifications());
}
private async Task CheckNotifications()
{
await DatabaseService.Init();
var eventList = await DatabaseService.db.Table<Event>().ToListAsync();
if (eventList.Count > 0)
{
foreach (Event event in eventList)
{
if (event.NotifyStartDate && (event.StartDate.Date == DateTime.Today))
{
Notifications notification = new Notifications
{
Name = $"{event.Name}",
NotifyDate = event.StartDate,
Occurrence = "Starting"
};
await DatabaseService.AddNotification(notification);
}
if (event.NotifyEndDate && (event.EndDate.Date == DateTime.Today))
{
Notifications notification = new Notifications
{
Name = $"{event.Name}",
NotifyDate = event.EndDate,
Occurrence = "Ending"
};
await DatabaseService.AddNotification(notification);
}
}
}
}
This all seems to work fine, it creates a Notification with the event that starts today and adds it to the Notifications Table in the database. Next it moves to my MainPage Code Behind's OnAppearing method--> which is an async void method and I can't figure out any other way to do this, because I get an error that "MainPage.OnAppearing() return type must be void" if I try to make it a Task.
protected override async void OnAppearing()
{
//List fills fine here and holds my Test Event starting today information with a count of 1
var notifications = await DatabaseService.db.Table<Notifications>().ToListAsync();
if (notifications.Count > 0)
{
foreach(Notifications notification in notifications)
{
CrossLocalNotifications.Current.Show("Reminder", $"Event {notification.Name} is {notification.Occurrence} Today" +
$"on {notification.NotifyDate}");
//code stops at this line and continues the rest of the program without executing notification
}
}
base.OnAppearing();
}
My Init, FillSampleData, and AddNotifications methods in the database are all async tasks, and they all await other methods, but the problem seems to occur at OnAppearing. To add to that I am Navigating to the Main Page from App.Xaml.cs Synchrounously, because when I tried to make it an asynchronous method it got ignored there.
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
Everywhere else in my app the navigation awaits. I don't know if I'm just doing the notifications all wrong, if I'm just doing Asynchronous programming all wrong, or a little bit of both. Any help would be much appreciated. Thanks guys.
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
Although this has been posted before on StackOverflow but i think none of those reflect my issue and none of those solutions work for me either. So i'm developing a Windows Phone app and my workflow is a bit like this:
App starts
ContactPicker opens up
User selects one or multiple contacts
Based on how many contacts he selected, that many PivotItems are added into the Pivot.
My code is as follows:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// TODO: Prepare page for display here.
// TODO: If your application contains multiple pages, ensure that you are
// handling the hardware Back button by registering for the
// Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
// If you are using the NavigationHelper provided by some templates,
// this event is handled for you.
SelectContacts();
}
private async Task SelectContacts()
{
var picker = new ContactPicker();
picker.DesiredFieldsWithContactFieldType.Add(ContactFieldType.PhoneNumber);
ContactsList = (List<Contact>)await picker.PickContactsAsync();
DisplayContacts();
}
private void DisplayContacts()
{
if (ContactsList != null)
{
foreach (var item in ContactsList)
{
PivotItem pivotItem = new PivotItem();
pivotItem.Header = item.FirstName.ToString();
ContentRoot.Items.Add(pivotItem);
}
}
}
According to me, in SelectContacts() method, the app should wait at the await call and once it gets back the list of contacts, than it should execute the DisplayContacts() method but its not working. I've tried multiple other variations of this code and they aren't working either.
await the SelectContacts() method and add the DisplayContacts() method beneath it. Remove the DisplayContacts() method from SelectContacts()
await SelectContacts();
DisplayContacts();
I don't know the complete reason why but i figured it out that since i was making the PickContactsAsync() call in the OnNavigatedTo() event, that is why it wasn't working as expected. Once i moved the PickContactsAsync() call into the PageLoaded() event handler, it started working as usual.
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.
I want to update my a DataGrid from multiple thread in WPF(c#). I use dataGrid.Dispatcher.BeginInvoke() and dataGrid.Dispatcher.Invoke() but they freeze program (main thread). How can update dataGrid from multiple threads with a timeout ( because I use web service that may be unreachable ).
Use a Task kick off the web service request asynchronously. To do this you will probably need to convert the EAP (event-based asynchronous pattern) style into a TAP (task-based asynchronous pattern) style. Here is how you do that.
private Task<IEnumerable<YourDataItem>> CallWebServiceAsync()
{
var tcs = new TaskCompletionSource();
var service = new YourServiceClient();
service.SomeOperationCompleted +=
(sender, args) =>
{
if (args.Error == null)
{
tcs.SetResult(args.Result);
}
else
{
tcs.SetException(args.Error);
}
};
service.SomeOperationAsync();
return tcs.Task;
}
After you have that in place then you can use the new async and await keywords to make the call and wait for it to return using continuation style semantics. It would look like this.
private async void Page_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
IEnumerable<YourDataItem> data = await CallWebServiceAsync();
YourDataGrid.DataSource = data;
}
That is it! It does not get a whole lot more elegant than that. This will perform the operation asynchronously on a background thread and then bind the results to the DataGrid on the UI thread.
If the WCF service is unreachable then it will throw an exception and will be attached to the Task so that it propagates up to the await call. At that point it will be injected into the execution and can be wrapped with a try-catch if necessary.
If you don't need the DataGrid editing to be done in the threads, you can run them in the main thread like this:
this.Invoke((Action)delegate
{
//Edit the DataGrid however you like in here
});
Make sure to only put things you need to be run in the main thread inside it (otherwise that would defeat the purpose of multithreading).