I'm running an UWP app with 2 pages. In MainPage I start a ContentDialog Async method which opens page2 in a frame. In page2 I have a secondary ContentDialog which I open by pressing a button.
In the Button_Click method I want to close the running Async() method and await to start the next one until I am 100% sure the first one is closed.
The problem I encounter now is that the program crashes if I press the secondary ContentDialog's buttons too fast after it opens.
I've tried thread sleeping but that only delays the problem.
Mainpage(CustomerInfoPage):
transactionContent = new ContentDialog();
Frame transactionFrame = new Frame();
transactionFrame.Navigate(typeof(TransactionPage), selectedAccount);
transactionContent.Content = transactionFrame;
transactionContent.ShowAsync();
Page2(TransactionPage):
private async void DepositButton_ClickAsync(object sender, RoutedEventArgs e)
{
CustomerInfoPage.transactionContent.Hide();
ContentDialog confirmationDialog = new ContentDialog
{
Title = "Deposit Funds.",
Content = $"You will deposit {depositTextBox.Text} SEK.\nYour new balance will be: {ReceivedAccount.Balance + deposit} SEK",
PrimaryButtonText = "CONFIRM",
SecondaryButtonText = "CANCEL"
};
confirmationDialog.PrimaryButtonClick += ConfirmationDialog_PrimaryButtonClick;
await confirmationDialog.ShowAsync();
async void ConfirmationDialog_PrimaryButtonClick(ContentDialog _sender, ContentDialogButtonClickEventArgs args)
{
confirmationDialog.Hide();
}
CustomerInfoPage.transactionContent.ShowAsync();
}
So this works as long as you don't call the ConfirmationDialog_PrimaryButtonClick too fast by clicking on it instantly after it is opened.
This is introduced in the UWP documentation about Dialog.
There can only be one ContentDialog open per thread at a time. Attempting to open two ContentDialogs will throw an exception, even if they are attempting to open in separate AppWindows.
So you can't start the second CotnentDialog while the first ContentDialog is still running.
Best regards.
As #Richard Zhang - MSFT said in previous answer
So you can't start the second CotnentDialog while the first
ContentDialog is still running.
If you wish to close the ContentDialog from previous page right before opening new ContentDialog you can use the code from below which will close any ContentDialog which is currently open.
var openedpopups = VisualTreeHelper.GetOpenPopups(Window.Current);
foreach (var popup in openedpopups)
{
if(popup.Child is ContentDialog)
{
(popup.Child as ContentDialog).hide();
}
}
Related
I am creating a cross platform app on xamarin.forms.
I'm using a plugin to display a popup window.
However, it seems like the awaitkey is not working since the execution continues before the task is completed.
Besided that, if the button to display a popup is clicked fast many times consecutively, the popup will be displayed this many times clicked instead of displaying once and block all the rest.
I have a command attached to a button. Whenever the button is clicked, the command property is fired corectly but the awaitseems to have no effect.
public ICommand {
get => new Command(async () =>
{
if (ObjectivosDoMes.Count < 3)
await PopupNavigation.Instance.PushAsync(new NovoObjectivoDoMes(ObjectivosDoMes), true);
PodeAdicionarObjMes = ObjectivosDoMes.Count < 3 ? true : false;
});
}
I would like to the code after popup being displayed to be execute just after the popup is closed. This is the library i am using to display popup : https://github.com/rotorgames/Rg.Plugins.Popup
Within your code you are making the assumption that the task returned by PopupNavigation will finish when the popup is closed. Instead, once the popup page has been pushed to the navigation stack this task will finish. So awaiting this task will not be helpful to detect when the popup has been closed. You could hook into the Disappearing event of the popup page. Here is some working example that is self contained and does not depend on other Views/ViewModels.
// in constructor
ButtonTappedCommand = new Command(async () => await OnButtonTappedCommand()) ;
page = new Rg.Plugins.Popup.Pages.PopupPage();
}
private async Task OnButtonTappedCommand()
{
page.Content = new Button()
{
Text="Close",
// close the popup page on tap
Command = new Command(()=>PopupNavigation.Instance.PopAsync())
};
page.Disappearing += Popup_Disappearing;
await PopupNavigation.Instance.PushAsync(page);
}
private void Popup_Disappearing(object sender, EventArgs e)
{
page.Disappearing -= Popup_Disappearing;
Debug.WriteLine("Someone closed the popup");
}
await is working fine. The popup library you're using completes its PushAsync operation when the popup is shown, not when it is closed. See this issue for a workaround that allows you to await the popup being closed.
I'm having an issue closing a program when there is a large amount of dialogs open. I was able to reproduce the bug using the following functions. The parent form on which this runs on is created using Application.Run(mainform).
When button1 is clicked, it creates a very basic form that opens another dialog of itself when "ok" is pressed. It then initiates the OnShutdown() function. This waits 15 seconds, then attempts to close all windows and exit the program. When more than 6 dummy windows are open, the function closes all the dummy windows but not the main window.
The steps for me reproducing the problem is:
Click button1
Close created heavy dialog
Click on on the msgbox
Click on button1 again
On the new heavy dialog, I press Ok to open another heavy dialog
Continue to open more heavy dialog, until 6-10 are open concurrently
Once the 15 seconds are up, all the dialogs close, but the parent form remains open, when it should be closed.
private void button1_Click(object sender,EventArgs e) {
BasicTemplate heavy = new BasicTemplate();
heavy.ShowDialog();
OnShutdown();
}
private void OnShutdown() {
timerSignals.Enabled=false;//quit receiving signals.
string msg = "";
msg+=Process.GetCurrentProcess().ProcessName+" ";
msg+=Lan.g(this,"will shut down in 15 seconds. Quickly click OK on any open windows with unsaved data.");
MsgBoxCopyPaste msgbox = new MsgBoxCopyPaste(msg);
msgbox.Size=new Size(300,300);
msgbox.TopMost=true;
msgbox.Show();
ODThread killThread = new ODThread((o) => {
Thread.Sleep(15000);//15 seconds
//Also happens with BeginInvoke()
Invoke((Action)(() => { CloseOpenForms(true); }));
Thread.Sleep(1000);//1 second
Invoke((Action)Application.Exit);
});
killThread.Start(true);
return;
}
public void Start(bool isAutoCleanup) {
_isAutoCleanup=isAutoCleanup;
if(_thread.IsAlive) {
return;//The thread is already running.
}
if(_hasQuit) {
return;//The thread has finished.
}
_dateTimeStart=DateTime.Now;
_thread.Start();
}
//Code for "OK" button on the heavy dialog created above
private void butOK_Click(object sender,EventArgs e) {
BasicTemplate formp = new BasicTemplate();
formp.ShowDialog();
}
private bool CloseOpenForms(bool isForceClose) {
for(int f=Application.OpenForms.Count-1;f>=0;f--) { //Count backwards to avoid out of bounds
if(Application.OpenForms[f]==this) {
continue;
}
Form openForm=Application.OpenForms[f];//Copy so we have a reference to it after we close it.
openForm.Hide();
if(isForceClose) {
openForm.Dispose();
}
else {
openForm.Close();//Attempt to close the form
if(openForm.IsDisposed==false) {
openForm.Show();//Show that form again
return false;
}
}
}
return true;
}
When line 3's "heavy.ShowDialog()" is changed to "heavy.Show()", the holdup no longer takes place. When multiple heavy dialogs are opened using a different button, the issue no longer takes place (up to ~20 new dialogs).
The shutdown sequence is run as a thread to allow the user to save any changes they have made before the program's database updates. I'm not sure if what I've implemented correctly shows the bug, but it at least produces similar events.
I've got a WinForms project that scans a given network and returns valid IP addresses. Once all the addresses are found, I create a user control for each and place it on the form. My functions to ping ip addresses use async and Task which I thought would "wait" to execute before doing something else, but it doesn't. My form shows up blank, then within 5 seconds, all the user controls appear on the form.
Declarations:
private List<string> networkComputers = new List<string>();
Here's the Form_Load event:
private async void MainForm_Load(object sender, EventArgs e)
{
//Load network computers.
await LoadNetworkComputers();
LoadWidgets();
}
The LoadNetworkComputers function is here:
private async Task LoadNetworkComputers()
{
try
{
if (SplashScreenManager.Default == null)
{
SplashScreenManager.ShowForm(this, typeof(LoadingForm), false, true, false);
SplashScreenManager.Default.SetWaitFormCaption("Finding computers");
}
else
Utilities.SetSplashFormText(SplashForm.SplashScreenCommand.SetLabel, "Scanning network for computers. This may take several minutes...");
networkComputers = await GetNetworkComputers();
}
catch (Exception e)
{
MessageBox.Show(e.Message + Environment.NewLine + e.InnerException);
}
finally
{
//Close "loading" window.
SplashScreenManager.CloseForm(false);
}
}
And the last 2 functions:
private async Task<List<string>> GetNetworkComputers()
{
networkComputers.Clear();
List<string> ipAddresses = new List<string>();
List<string> computersFound = new List<string>();
for (int i = StartIPRange; i <= EndIPRange; i++)
ipAddresses.Add(IPBase + i.ToString());
List<PingReply> replies = await PingAsync(ipAddresses);
foreach(var reply in replies)
{
if (reply.Status == IPStatus.Success)
computersFound.Add(reply.Address.ToString());
}
return computersFound;
}
private async Task<List<PingReply>> PingAsync(List<string> theListOfIPs)
{
var tasks = theListOfIPs.Select(ip => new Ping().SendPingAsync(ip, 2000));
var results = await Task.WhenAll(tasks);
return results.ToList();
}
I'm really stuck on why the form is being displayed before the code in the MainForm_Load event finishes.
EDIT
I forgot to mention that in the LoadNetworkComputers it loads a splash form which lets the user know that the app is running. It's when the form shows up behind that, that I'm trying to avoid. Here's a screenshot (sensitive info has been blacked out):
The reason one would use async-await is to enable callers of functions to continue executing code whenever your function has to wait for something.
The nice thing is that this will keep your UI responsive, even if the awaitable function is not finished. For instance if you would have a button that would LoadNetworkComputers and LoadWidgets you would be glad that during this relatively long action your window would still be repainted.
Since you've defined your Mainform_Loadas async, you've expressed that you want your UI to continue without waiting for the result of LoadNetWorkComputers.
In this interview with Eric Lippert (search in the middle for async-await) async-await is compared with a a cook making dinner. Whenever the cook finds that he has to wait for the bread to toast, he starts looking around to see if he can do something else, and starts doing it. After a while when the bread is toasted he continues preparing the toasted bread.
By keeping the form-load async, your form is able to show itself, and even show an indication that the network computers are being loaded.
An even nicer method would be to create a simple startup-dialog that informs the operator that the program is busy loading network computers. The async form-load of this startup-dialog could do the action and close the form when finished.
public class MyStartupForm
{
public List<string> LoadedNetworkComputers {get; private set;}
private async OnFormLoad()
{
// start doing the things async.
// keep the UI responsive so it can inform the operator
var taskLoadComputers = LoadNetworkComputers();
var taskLoadWidgets = LoadWidgets();
// while loading the Computers and Widgets: inform the operator
// what the program is doing:
this.InformOperator();
// Now I have nothing to do, so let's await for both tasks to complete
await Task.WhenAll(new Task[] {taskLoadComputers, taskLoadWidgets});
// remember the result of loading the network computers:
this.LoadedNetworkComputers = taskLoadComputers.Result;
// Close myself; my creator will continue:
this.Close();
}
}
And your main form:
private void MainForm_Load(object sender, EventArgs e)
{
// show the startup form to load the network computers and the widgets
// while loading the operator is informed
// the form closes itself when done
using (var form = new MyStartupForm())
{
form.ShowDialog(this);
// fetch the loadedNetworkComputers from the form
var loadedNetworkComputers = form.LoadedNetworkComputers;
this.Process(loadedNetworkComputers);
}
}
Now while loading, instead of your mainform the StartupForm is shown while the items are loaded.. The operator is informed why the main form is not showing yet. As soon as loading is finished, the StartupForm closes itself and loading of the main form continues
My form shows up blank, then within 5 seconds, all the user controls appear on the form.
This is by design. When the UI framework asks your app to display a form, it must do so immediately.
To resolve this, you'll need to decide what you want your app to look like while the async work is going on, initialize to that state on startup, and then update the UI when the async work completes. Spinners and loading pages are a common choice.
I wrote a function that download something like a pic from net , and use it
in a click event of special button.
my problem is when i click on button and the app start downloading pic from
internet , all of my controls in form , lock (until download process is done!)
not just only controls , all of my form too.
so how can i handle this process in background of application and the
user can work with other control at Same time.
All you need is asynchronous programming
here is a very simple demo
private async void button1_Click(object sender, EventArgs e)
{
this.Text = "doing something...";
var result = await SomeHeavyWork();
this.Text = result.ToString();
}
private async Task<int> SomeHeavyWork()
{
using (var hc = new HttpClient())
{
var data = await hc.GetAsync("www.google.com");
return data.Content.Headers.Count();
}
}
What's happening here?
when you click button1, button1_Click will execute.
it first set the form text to "doing something..."
it now waits for SomeHeavyWork() to complete its work.
we are now exiting button1_Click function and do what we were doing before clicking button1(running the form message loop). but somewhere else we execute SomeHeavyWork() and waiting for it.
when SomeHeavyWork() job is finished we came back to button1_Click and execute the this.Text = result.ToString(); line.
please read this msdn article
In WindowsStoreApps I have three MessageDialogs which have to be showed one bye one by clicking a button in each MessageDialog.While doing this I get an 'System.UnauthorizedAccessException'.It seems we cant open another messageDialog while one is open.Is there a possiblity to close the previous dialog while opening the next one? or Can any one suggest me an idea how to handle this issue.
MessageDialog md = new MessageDialog("Have you been enjoying your experience with " + Modules.Title + " ?");
UICommand btn_ntreally = new UICommand("Not Really");
md.Commands.Add(btn_ntreally);
btn_ntreally.Invoked += btn_ntreally_invoked;
UICommand btn_yeah = new UICommand("Yeah!");
md.Commands.Add(btn_yeah);
btn_yeah.Invoked += btn_yeah_invoked;
md.ShowAsync();
I get system.unauthorised exception messagedialog.showasync.
private async void btn_yeah_invoked(IUICommand command)
{
MessageDialog md = new MessageDialog("Deal is deal");
UICommand btn_Sure_invoked = new UICommand("Sure");
md.Commands.Add(btn_Sure_invoked);
btn_ntreally.Invoked += btn_Sure_invoked;
UICommand btn_yeah = new UICommand("No thanks!");
md.Commands.Add(btn_tnks);
await md.ShowAsync(); //getting system.UnauthorizedAccessException here
}
private async void btn_Sure_invoked(IUICommand command)
{
UICommand btn_rateAppsure = new UICommand("Yes!");
md.Commands.Add(btn_rateAppsure);
btn_rateAppsure.Invoked += btn_rateAppsure_invoked;
UICommand btn_rateNothanks = new UICommand("No.Thanks");
md.Commands.Add(btn_rateNothanks);
await md.ShowAsync(); //getting system.UnauthorizedAccessException here
}
The MessageDialog is intended to block all other activities in your app and force the user to respond to the action your request from him. So it doesn't seem logical to fire 3 messagedialogs at the same time. That's most likely the reason your getting the exception.
Because your code sample isn't that clear about what you're trying to achieve with the 3 messagedialogs, I'll come up with some approaches that you might like:
1
Show a custom kind of message dialog where you use viewstates to differentiate the different questions that the user has to answer before he can continue.
2
Show a messagedialog where all 3 questions are shown and multiple buttons to answer the variants?
The last one isn't the nicest solution. If you eloborate a bit on what you exactly trying to achieve maybe we can come up with some better solutions to fix your problem.
If you really want this use this pattern (which I don't really recommend), you can set a timer to call you some time after the dialog has been closed, and then you will be able to call another dialog to open.