Why is ContentDialog and Prism Navigation blowing up my UWP app? - c#

I am showing a content dialog on one of my sessionstateaware pages, I close the dialog fine and can reopen another on the same page as many times as I want. When I navigate away and come back and try to reopen my dialog, it blows up with this error:
WinRT information: Only a single ContentDialog can be open at any time.
Additional information: An async operation was not properly started.
I have tried numerous things, but it seems whenever I navigate away and come back, somehow the reference to the control is lost and another gets created?
Here is a little snippet of the problem area:
if(asyncCommand != null)
asyncCommand.Cancel();
var result = new ContentDialogResult();
if (CanOpenNewDialog)
{
CanOpenNewDialog = false;
MyContentDialog.Title = "Homebuyer Options - " + apt.Customer1FullName;
asyncCommand = MyContentDialog.ShowAsync();
result = await asyncCommand.AsTask();
}

Facing the same issue with ContentDialouge when navigating more than one time. App is just crashing by saying "An async operation was not properly started. Only a single ContentDialog can be open at any time."

I got a solution for this. For example the method Show Dialouge, where the ContentDialog is calling, should be awaited. So make the Show Dialouge method as a task.
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
await ShowDialouge();
}
private async Task ShowDialouge()
{
ContentDialog dialouge = new ContentDialog();
await dialouge.ShowAsync();
}

The problem was when I was navigating using prism, every time I came back to my page I resubscribed to my event that was creating a dialog. To fix this I simply call unsubscribe OnNavigatedFrom.

Related

Xamarin Forms Navigation PopAsync isn't working

I'm working on an app in Xamarin.Forms, and things have been going pretty steadily, until I ran into a navigation error. The thing that mystifies me about it is that I've already been successfully using the same code calls on other pages, but suddenly with this page, it isn't working.
I have designed a bit of a unique navigation flow because of the visual result that I'm trying to accomplish, using a combination of master-detail that has two tiers of navigation pages using the normal push / pop code. I was following suggestions from this article on medium.com.
The app initializes a main page called "Root Navigation" that initializes the master and detail pages.
public App()
{
InitializeComponent();
MainPage = new RootNavigation();
}
Root Navigation page:
public partial class RootNavigation : MasterDetailPage
{
private MenuPage menuPage;
private NavigationPage OuterPage;
private NavigationPage InnerPage;
public RootNavigation()
{
this.Master = this.menuPage = new MenuPage();
this.menuPage.MenuItemsListView.ItemSelected += Menu_ItemSelected;
var viewModel = new SelectEmployeeViewModel();
var page = new SelectEmployeePage(viewModel);
SetAsDetailPage(page);
}
To navigate forward in the app, I'm using a method called "set as detail page," that bridges the gap between master-detail behavior and navigation push / pop behavior.
private void SetAsDetailPage(ContentPage page)
{
NavigationPage.SetHasNavigationBar(newPage, false);
if (newPage.GetType() == typeof(JobDetailPage))
{
newPage.ToolbarItems.Add(
new ToolbarItem()
{
Text = "Back",
Command = new Command(() => BackButton_Clicked())
});
}
this.InnerPage = this.InnerPage ?? new NavigationPage();
this.InnerPage.Navigation.PushAsync(page);
this.OuterPage = this.OuterPage ?? new NavigationPage(this.InnerPage);
this.Detail = this.Detail ?? this.OuterPage;
}
Then, navigating backward calls one of two methods: "BackButton_Clicked()" or "ReturnToJobList()".
private void ReturnToJobList()
{
while (InnerPage.CurrentPage.GetType() != typeof(JobsPage))
{
var current = InnerPage.CurrentPage.ToString();
System.Diagnostics.Debug.WriteLine($"'{current}' attempting Navigation.Pop()");
InnerPage.PopAsync();
}
}
private void BackButton_Clicked()
{
this.InnerPage.PopAsync();
}
All of the pages that display read-only data have navigated without issue. When I'm done with the page, I have it raise a call to the MessagingCenter, and the root navigation receives the message and performs the desired navigation. For example, the "MenuPage_ItemSelected" event handler fires the code:
if (e.SelectedItem.ToString().ToLower() == "log out")
{
Device.InvokeOnMainThreadAsync(() =>
{
InnerPage.PopToRootAsync();
this.IsPresented = false;
});
}
This seems to be working fine, after I spent a while researching that when a secondary page calls 'pop to root' on a background thread, I have to invoke it on the main thread.
Ok, so finally to the problem page: The "update job notes" page. The page raises a call to the Messaging Center, and the Root Navigation page picks that up and executes the following code:
private async void SaveJobNotes(ContentPage sender)
{
if (sender is UpdateJobNotesPage notesPage)
{
bool result = await SaveNewJobNote(notesPage);
var message = result ? "Saved changes" : "An error occurred; changes not saved";
await DisplayAlert("Save", message, "OK");
}
ReturnToJobList();
}
Stepping through the code, it correctly executes SaveNewJobNote() and returns true. Then, it awaits displaying the alert "Saved Changes". Then, the code then gets stuck in an infinite while loop in the "ReturnToJobList()," forever printing out into the debug output [0:] 'Application.Views.UpdateJobNotesPage' attempting Navigation.Pop(). After about a million cycles I get tired of waiting and quit the debugger. I can't seem to do anything to make the page go away!
I've tried a bunch of stuff with investigating the differences between PopAsync and PopModalAsync. After checking what's on the navigation stacks for the different pages in question, everything looks exactly like what I'd expect -- there's nothing on the modal stacks for anything (because I never called PushModalAsync on anything), there's 0 on the RootNavigation stack, 1 on the OuterPage stack, and 4 on the InnerPage stack. That all makes perfect sense to me, but it still doesn't pop the Update Job Notes page. I also tried code with Navigation.RemovePage(page) with no success. The only difference there was that the debugger included printing a warning about my code and suggesting I use PopAsync() instead.
I also tried some different things with making the PopAsync() call from this.Navigation, this.Outer, this.Outer.Navigation, this.Inner, this.Inner.Navigation, all with no success.
I have already looked at a bunch of other questions on Stack Overflow including this question and this question but none of them seem to apply in this case. Any assistance would be greatly appreciated!!
I remember this was happening to me.
I forget exactly what was the cause, but I had some funky navigation going on as well. My problem was around popups and at one point, they were creating a new stack. So when I popped, I was getting unexpected results.
I would suspect you are also creating another stack somewhere, especially if you are at 0 in the debugger.
The culprit is most likely lurking around that InvokeOnMainThread().
I haven't really figured out what the problem is with the code block that I created that was supposed to call InnerNavigation.PopAsync() until the Job List page was visible. It seems like all the variables in that code block evaluate to values that I'd expect, but somehow it doesn't seem to be able to pop anything off the stack.
However, I did change my code block that handles saving Job Notes, and it does now pop the Save Job Notes page off the stack.
private async void SaveJobNotes(ContentPage sender)
{
this.InnerPage.PopAsync(); //I don't understand why this works and the
//other didn't, but it correctly pops the page
if (sender is UpdateJobNotesPage notesPage)
{
bool noteSaved = await SaveNewJobNote(notesPage);
bool progressSaved = await SaveJobProgress(notesPage);
var message = noteSaved && progressSaved ?
"Changes were save successfully" :
"An error occurred; changes not saved";
await DisplayAlert("Save", message, "OK");
}
}

Win 10 IOT thread deadlocking in UWP

What I want to do:
- synchronously (or even asynchronously) load settings from USB drive before first page loads
What I did:
- in OnLaunched method for App.xaml.cs I invoked this static function:
public static async void LoadSettings(string folderName = "Config", string fileName = "Settings.xml")
{
try
{
StorageFile configFile = null;
// scan through all devices
foreach (var device in await KnownFolders.RemovableDevices.GetFoldersAsync().AsTask().ConfigureAwait(false))
{
// folder that should have configuration
var configFolder = await device.GetFolderAsync(folderName).AsTask().ConfigureAwait(false);
if (configFile != null && configFolder != null && await configFolder.GetFileAsync(fileName).AsTask().ConfigureAwait(false) != null)
{
throw new Exception("More than one configuration file detected. First found configuration file will be used.");
}
else
configFile = await configFolder.GetFileAsync(fileName).AsTask().ConfigureAwait(false);
}
if (configFile == null)
throw new Exception("Configuration file was not found, please insert device with proper configuration path.");
string settingString = await FileIO.ReadTextAsync(configFile).AsTask().ConfigureAwait(false);
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
using (TextReader reader = new StringReader(settingString))
{
AppSettings = (Settings)serializer.Deserialize(reader); // store settings in some static variable
}
}
catch (Exception e)
{
//return await Task.FromResult<string>(e.Message);
}
//return await Task.FromResult<string>(null);
}
As you can see right now it's async void method, so I don't even want to synchronize it in any way with UI thread. It should just fire and do something. With ConfigureAwait(false) I want to be sure that it will never try to return to context. These returns at the end are remnants of other things I tried (I wanted to do this better way, this is the most primitive solution and it still doesn't work).
Anyway, because that's where the fun begins: everything works well when I debug application on local machine with Win 10. And I get deadlocked thread on Win 10 IOT installed on Raspberry Pi 3 (I installed it from the scratch today, last version).
But deadlock is not the weirdest thing. Weirdest thing is when it appears.
Like I said, invocation of this method looks like that:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Configuration.Settings.LoadSettings();
After that everything in this method goes normally, so I navigate to my first page somewhere below:
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(LogScreen), e.Arguments);
}
Window.Current.Activate();
}
Everything still works. User needs to write his code, I check if this code is available in settings and after that user can press "OK" to move to next page. Somewhere in LogScreenViewModel this method is responsible for that:
private void GoForward(bool isValid)
{
try
{
_navigationService.NavigateTo("MainPage"); // it's SimpleIoc navigation from MVVMLight
}
catch (Exception e)
{
Debug.WriteLine($"ERROR: {e.Message}");
}
}
And deadlock happens when _navigationService.NavigateTo("MainPage") is reached. Basically right now UI thread freezes. If I wait for long enough I will see catched exception in Output saying that messenger seemed occupied (I can't show the screen because I don't have access to that Raspberry right now) and after some timeout this thread was killed (like 30 seconds or something) - after that UI thread unlocks and application proceeds to MainPage. It doesn't happen on PC - MainPage appears immediately, no exceptions, no deadlocks.
I tried waiting on first page for like 1 minute to check if some deadlock exception would fire on it's own - but it doesn't. It will fire ONLY after I try to proceed to next page.
What else I tried instead of this fire-and-forget approach:
Making OnLaunched async and await LoadSettings returning Task - same thing happens in the same place, and no problem on PC.
Using:
Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => await Configuration.Settings.LoadSettings(); ).AsTask().Wait(); If I remember correctly it deadlocked immediately on Wait(), even with ConfigureAwait(false) everywhere, but it also happened on PC.
Allowing LogScreen to load, make it's OnNavigatedTo method async and await LoadSettings - same deadlock in same place
Allowing LogScreen to load and use Dispatcher from there like in point 2. It deadlocked the same way after reaching Wait(), on PC too.
Trying to force LoadSettings to be fully synchronous by replacing every await with AsTask().GetAwaiter().GetResults(). It worked well on PC... and of course deadlock on Raspberry.
What am I missing? What else can I try? Because to be honest right now it looks to me that Win 10 IOT .NET runtime is bugged or something.
I think I resolved the issue. This code was generally speaking not mine and after some digging I noticed that someone before me tried to list some other external devices while navigating to MainPage. It was not really async-safe code (someone probably wasn't aware of synchronization context) and it worked on Win 10 only because on desktop it was looking for COM0 device and I only have COM2, so method causing trouble was not even invoked at all.
I still have no idea how related it was to my configuration (because it somehow was working without it), but after I fixed issues with this old not-async-safe code it started to behave as expected.

WinForms window changes dimensions when it encounters an async call

I have a WinForms project which is several years old and has been retro-fitted with async event-handlers:
private async void dgvNewOrders_CellClick(object sender, DataGridViewCellEventArgs e)
Inside this method is an async call:
var projectTemplate = await GetProjectTemplateFile(companyId, sourceLang, targetLang);
When the program runs on a normal resolution screen, it runs as expected. However, when run on a high-DPI screen the window's dimensions - as well as those of all child controls - jump to half-size as soon as it encounters that inner async call. It's as if the program is suddenly run in a compatibility mode or the scaling has been disabled.
Currently, in an effort to debug the problem, the GetProjectTemplateFile method consists simply of
private async Task<ProjectTemplateFile> GetProjectTemplateFile(long companyId, string sourceLanguage, string targetLanguage)
{
return null;
}
It makes no difference whether GetProjectTemplateFile performs an async operation or not.
If I comment-out that async call to GetProjectTemplateFile then the program runs as expected without any jump in dimensions, even though there are still other async calls made in the CellClick event.
I've tried appending .ConfigureAwait(true) to the async call, which makes no difference. Nor does running the call synchronously with .GetAwaiter().GetResult().
Can anyone explain why the window's dimensions are changing with this particular async call, and/or how to prevent this from happening?
Update
As per a request, here is a code sample which elicits the explained behaviour. There's nothing unusual happening here that I can see but I assure you, this very code is causing the explained behaviour.
private async void dgvNewOrders_CellClick(object sender, DataGridViewCellEventArgs e)
{
var result = await _templateInteraction.GetProjectTemplateFile(1,
"en-US",
"de-CH");
return;
}
public class TemplateInteraction : ITemplateInteraction
{
public async Task<ProjectTemplateFile> GetProjectTemplateFile(long companyId, string sourceLanguage, string targetLanguage)
{
return null;
// elided code
}
// other methods
}
Some other information which might be relevant:
The FormBorderStyle of the window is "FixedToolWindow"
The window is given an explicit width in a startup method
AutoSize = False
AutoSizeMode = GrowOnly
The computer which it's being developed on does not have the Windows 10 1703
(Creator's) update which has new scaling logic
If the GetprojectTemplateFile method is not async, i.e. has signature public ProjectTemplateFile GetProjecttemplateFile(...) then there is no problem. This problem appears to exist only when the method call is async - even if I make it a blocking call.
UPDATE 2:
I've found the specific line(s) of code which cause this problem:
MessageBox.Show(...);
The inner async call, GetProjectTemplateFile, calls an API and then checks the response:
var responseMessage = await client.GetAsync(uri);
if (!responseMessage.IsSuccessStatusCode)
{
MessageBox.Show(...);
return null;
}
If I comment-out the MessageBox.Show(...) call then everything is normal, no scaling problems, no jump in dimensions.
But the problem occurs when the MessageBox.Show(...) call is in-place.
Furthermore, the API responds with a 200 (OK) so the MessageBox code isn't even being used. My guess is that the JIT compiler sees it as a possibility so... it re-renders the form?
Also, importantly, this code is not in the form's code-behind, it's in a class which the form is given an instance of in its constructor.
I guess you are using MessageBox from System.Windows namespace, referenced from PresentationFramework.dll, instead of System.Windows.Forms namespace?
// Causes DPI scaling problems:
System.Windows.MessageBox.Show() // loads WPF version from PresentationFramework.dll
// no DPI scaling issues:
System.Windows.Forms.MessageBox.Show() // uses standard winforms messagebox
So try using the standard MessageBox instead.
I've found that whenever any WPF-targeted dll gets loaded into memory, the DPI autoscaling gets reset. The specific function doesn't even need to be actually called - the dll's are loaded as soon as the parent function is called.
I had same problem by just having System.Windows.Input.Keyboard.IsKeyToggled(), which loaded PresentationCore.dll. Thought I was going mad as well...

Handle popup messages - Coded UI Test

Issue
I am running tests using the Coded UI Test builder and writing all the code from scratch. The issue I am facing is in the middle of the test there is a popup message with the results "Stay on this page" or "Leave this page". I want my test to be able to click "Stay on this page".
The popup sometimes appears straight after the event or sometimes appears a couple of seconds later.
Code
So the event that I run before the message appears is a button click:
ClickButton(browser, "login");
void ClickButton(UITestControl parent, string id)
{
var button = new HtmlButton(parent);
button.SearchProperties.Add(HtmlButton.PropertyNames.Id, id);
Mouse.Click(button);
}
I have tried Keyboard.SendKeys() but this just sends the keys to the browser window. I have also tried using the recording tool. Both are unsuccessful.
After this event I need to wait for the popup to appear and click "Stay on this page". Does anyone know how to achieve this?
We actually mapped this confirmation window and it works for us.
We start with a Window with name = Windows Internet Explorer
Followed by a Pane with name = Windows Internet Explorer
and finally with a Button of name Stay on this page
All with Technology = MSAA.
You must handle writing code to wait for readiness of the control and proper time out if you don't expect the confirmation every time.
Hope this helps.
Depending on what exactly that window is, you should be able to deal with it no problem. I would use the inspector to get the properties of the window and use one of the WaitFor* methods to give it some time. Here is an example of dealing with the security pop up that IE shows:
namespace CaptainPav.Testing.UI.CodedUI.PageModeling.Win
{
public class IESecurityWindow<T> : PageModelBase<WinWindow>
{
protected const string SecurityWindowName = "Internet Explorer Security";
internal protected override WinWindow Me => new WinWindow().Extend(WinWindow.PropertyNames.Name, SecurityWindowName, PropertyExpressionOperator.EqualTo);
protected WinButton AllowButton => this.Me.Find<WinButton>(WinButton.PropertyNames.Name, "Allow", PropertyExpressionOperator.EqualTo);
internal readonly T AllowModel;
public IESecurityWindow(T allowModel)
{
this.AllowModel = allowModel;
}
public T ClickAllow()
{
// if not IE, this will return false and the next model is returned; change the time if you need more or less wait
if (this.AllowButton.IsActionable(3000))
{
Mouse.Click(this.AllowButton);
}
return AllowModel;
}
}
}
In this case, the dialog is a Win* type, not Html*. There are some custom extension methods sprinkled in to make the searching and stuff easier, but this should give you an idea. If interested, the extensions (which are written by me) can be found on github.

How to fix "The requested resource is in use. (Exception from HRESULT: 0x800700AA)"

How can I solve this error?
"The requested resource is in use. (Exception from HRESULT: 0x800700AA)".
This appears while navigating to a different website using the WebBrowser control in C# .NET. Why?
The WebBrowser control is considered "in use" if either a navigation action is currently being processed, or any blocking dialog from the control is currently open (including context menu, Javascript alerts, NTLM login dialog, etc.). You can use the WebBrowser.IsBusy property to detect these states.
If due to a currently incomplete navigation action, you could try to stop the current navigation (if you indeed want to stop when the page is not completed loaded) or add the new navigation to a request queue and use a timer to wait until WebBrowser.IsBusy returns false.
If instead the busy state is due to one or more open blocking dialogs, you could do the same wait technique and perhaps Messagebox.Show() the user a message that pending navigation is delayed due to an open dialog window.
I had this same issue. Calling WebBrowser.Stop() did not help, and WebBrowser.IsBusy never became false.
It turns out that if the page creates any sort of dialog (alert() popups, javascript errors, NTLM login popups etc.) you can't navigate away from the page until the dialog is closed.
My solution was to prevent the dialogs from showing in the first place. Apparently preventing all of these popups is simple; just set
webBrowser.ScriptErrorsSuppressed = true;
bool go = false;
string SiteContent1 = string.Empty;
string SiteContent2 = string.Empty;
int index = 0;
WebBrowser wb = new WebBrowser();
void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
try
{
if (go)
{
SiteContent2 = wb.DocumentText;
// Code to compare to contents of the webbrowser
index++;
go = false;
steps = 1;
}
if (!go)
{
if (index >= TotalSiteCount)
{
Stop();
}
else if (steps == 1)
{
wb.Navigate(UrltocompareList[index].Url1);
}
else if (steps == 2)
{
SiteContent1 = wb.DocumentText;
wb.Navigate(UrltocompareList[index].Url2);
go = true;
}
steps++;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
UrltocompareList is a collection of 2 sites to compare.
TotalSiteCount is the number of items in UrltocompareList.
The form for this inherit IOleClientSite to remove media such as images, videos and no active X download to have a faster rendering time in webbrowser control.
I use this method instead of system.net.webclient to get the html of a webpage then compare them.
I got this error when it hits the wb.Navigate method.
An issue I ran into when running specflow tests with watin in windows 10 is that win10 by default uses MS Edge, so I had never opened IE, and when watin started it IE was stuck on the prompt for using default settings. Selecting options, closing browser and running tests again worked for me.
Just something to watch
This can be solved pretty easily.
This error occurs when the browser commits an action while he's already performing an action.
For example, you are navigating to some website while you rightclick in the web browser.
To solve this, I did the follow:
//if my webbrowser isn't performing any actions
if(!myWebBrowser.IsBusy)
{
//Navigate
myWebBrowser.Navigate("http://www.google.com");
}
First Try
1- Please Check Navigate URL's (if you check, please check again compiled folder)
2- Delete WebBrowser Control and Add New
Me forget copy original file App.Path + "\error.html" and see this problem.
Guarantee Method
I Fix This Error in VB6
Add WebBrowserControl wb(0) (Name wb , Index=0)
And Before Ever Navigate
For i = 1 To wb.UBound
Unload wb(i)
Next
Load wb(1)
wb(0).Visible = False
wb(1).Visible = true
wb(1).Navigate URL

Categories