Hi everyone i am using a WCF Soap Webservice and consuming it inside a Xamarin.Forms MVVM Client. (Mvvm light with MvvmLightNavigationExtension.
The Problem is when i use the Async Methods from the Webservice and corresponding Completed Event in my ViewModel the Navigation stucks / the screen is not changing.
When i debug the current Navigation Page says to be my second page but the first Page is still showing.
I Also tried it with Messengers but that wont work eather.
Down Below is some example Code.
Fun fact: When i make a second Button/Command and navigate to my second page after i called the GetLoginResponseCommand i can navigate Back two times to the first page.
Its Probably some weird Threading/UI Thread thing but i don't get it.
public LoginViewModel(INavigationService navigationService, MyWebService service)
{
_navigationService = navigationService;
_myWebService = service;
_myWebService.GetLoginInfoCompleted += MyWebServiceOnGetLoginInfoCompleted;
}
public RelayCommand GetLoginResponseCommand
{
get
{
return _getLoginResponseCommand ?? (_getLoginResponseCommand = new RelayCommand(
() =>
{
_orkaWebService.GetLoginInfoAsync(request);
}));
}
}
private void MyWebServiceOnGetLoginInfoCompleted(object sender, GetLoginInfoCompletedEventArgs e)
{
_navigationService.NavigateTo(VmKeys.ArtikelBestandKey);
}
Try to use async/ await in your async methods.
return _getLoginResponseCommand ?? (_getLoginResponseCommand = new RelayCommand(
async () =>
{
await _orkaWebService.GetLoginInfoAsync(request);
}));
I Got it myself.
The MyWebServiceOnGetLoginInfoCompleted runs in an own Thread. But the _navigationService needs to be run on the UIThread.
(Same for ContentPage.DisplayAlert or ContentPage.DisplayActionSheet)
When you run it like Device.BeginInvokeOnMainThread(() => _navigationService.NavigateTo(VmKeys.ArtikelBestandKey)); it works like a charm!
private void MyWebServiceOnGetLoginInfoCompleted(object sender, GetLoginInfoCompletedEventArgs e)
{
Device.BeginInvokeOnMainThread(() => _navigationService.NavigateTo(VmKeys.ArtikelBestandKey));
}
Thanks to Rohit for leading me to the right solution.
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 would like to use activity indicator to show that my function is loading. It goes so fast that I can see my activity inndicator but the function have not finished loading
Question : How to use make my activity indicator to false when the functions have really finished to run
Here my code :
public MainPage()
{
InitializeComponent();
On<iOS>().SetUseSafeArea(true);
activityIndicator.IsRunning = true;
MyPageName = "home";
var tasks = new List<Task>();
tasks.Add(Task.Run(async () => {
GetMyLeafLanguage(); //p
}));
tasks.Add(Task.Run(async () => {
if (App.CheckConnection() == true)
{
MyAds(); // doit ĂȘtre apres l'initialisation //
LoadtestPage();
}
}));
Task.WhenAll(tasks);
#endregion My Initial Tasks
OnGoTutorial();
MyNotificationActivate();
activityIndicator.IsRunning = false;
}
The way you wrote it, the tasks had to finish their work, before the page could display. So the page (finally) displayed, but since the work was already done, the activity indicator immediately disappeared.
These functions should run after the page is displayed.
Otherwise, while the tasks run, user will be looking at a blank screen (or at whatever page was showing before you starting creating this page).
Unfortunately, there is no cross-platform event that runs after a page displays for the first time.
Here is one way to let the page display while the background work is being done:
public partial class MyPage : ...
{
protected override void OnAppearing()
{
base.OnAppearing();
BeginWorkWithIndicator();
}
private void BeginWorkWithIndicator()
{
activityIndicator.IsRunning = true;
// The delay gives Xamarin UI a little time, before your background work begins.
// Without this delay, under some circumstances, the page might not show up as quickly.
Device.StartTimer(TimeSpan.FromMilliseconds(100), () => {
OneTimeBackgroundWork();
// Run timer only once.
return false;
});
}
private void OneTimeBackgroundWork()
{
... Long-running work - MUST NOT TOUCH UI ...
// When done.
Device.BeginInvokeOnMainThread(() => {
// NOW MAKE UI CHANGES, based on variables set by work above.
...
activityIndicator.IsRunning = false;
});
}
}
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
I have implemented a hybrid web view in my Xamarin PCL app.
I am calling a C# function from an Html page using JavaScript in the aforementioned, hybrid web view.
The problem is that although my function is being called, an exception is thrown when I try to redirect from it.
Android.Util.AndroidRuntimeException:
Only the original thread that created a view hierarchy can touch its views.
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.
My code is as follows:
var isValid = AreCredentialsCorrect(user);
if (isValid)
{
try
{
await Navigation.PushAsync(new UserDashboard("local.html?auth_admin=true"));
}
catch { }
}
public UserDashboard(string uriname)
{
InitializeComponent();
hybridWebView.Uri = uriname;
hybridWebView.RegisterAction(data => userLogin(data));
}
Sounds like you're trying to update UI from a background thread. Try doing the navigation from the main thread:
Device.BeginInvokeOnMainThread(async () => await Navigation.PushAsync(new UserDashboard("local.html?auth_admin=true")));
I am building a Windows phone application with lots of animations. What I need is that when the application starts, it should be done finishing the first animation, say myS() is the name of the storyboard. Only after it is done with the animation, a textblock should appear after 3 seconds. What methods should I use to make the display of textbox wait for the Storyboard to finish?
What I tried is using Thread.Sleeps(10000) but that doesn't work. This is the code -
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
c1.Visibility = Visibility.Collapsed; //c1 is the name of the textbox
myS.Begin();
canDisp();
}
private void e1_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
myS1.Begin();
}
private void e1_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
myS2.Begin();
}
void canDisp()
{
c1.Visibility = Visibility.Visible;
}}
After the myS.Begin() is done executing, I want the program to wait for 3 seconds & then execute the canDisp() method, how can I achieve that?
If your animation is a Storyboard then it has a Completed event (MSDN link) for just this purpose.
Your call to Thread.Sleep() was probably being run on the same thread as the animation and so was stopping the animation from running while it was sleeping.
If you really want to go down this route then you'll need to move your sleep call to a different thread.
If you really want to use threads, a simple way to do it is:
System.Threading.ThreadPool.QueueUserWorkItem(obj =>
{
System.Threading.Thread.Sleep(5000);
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("after delay");
});
});
I am using a windows store app where System.Threading.ThreadPool.QueueUserWorkItem
is not available.So I did like this and not working as expected
await Windows.System.Threading.ThreadPool.RunAsync(async (s) =>
{
await Task.Delay(1);
await MainViewModel.Instance.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
MessageBox.Show("after delay");
});
}, Windows.System.Threading.WorkItemPriority.High);