This is for a Windows 10 UWP app. When the user tries to navigate away from a page, I want to have the user confirm if he wants to save the current data.
I have overridden OnNavigatingFrom as shown below. However, after the async MessageDialog, setting e.Cancel=false doesn't work. The page still stays on the current page even e.Cancel is later set to false. Please help!
protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
e.Cancel = true; //if I don't put this at the top, the page navigates right away
var yesCommand = new UICommand("Yes", async cmd => {
try
{
await SaveWorkshetItem(false);
e.Cancel = false;
}
catch (Exception ex)
{
await new MessageDialog("Error saving Worksheet Item. Please contact you administrator." + ex.Message + Environment.NewLine + ex.StackTrace).ShowAsync();
}
});
var noCommand = new UICommand("No", cmd => { e.Cancel = false; });
var cancelCommand = new UICommand("Cancel", cmd => { e.Cancel = true; });
var dialog = new MessageDialog("Do you want to save the current item before navigating away?");
dialog.Options = MessageDialogOptions.None;
dialog.Commands.Add(yesCommand);
dialog.Commands.Add(noCommand);
dialog.Commands.Add(cancelCommand);
await dialog.ShowAsync();
base.OnNavigatingFrom(e);
}
To simplify this the below code causes the page to never leave even though I am changing back e.Cancel=false after the sample MessageDialog.
protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
e.Cancel = true; //if I don't put this at the top, the page navigates right away
await new MessageDialog("Do you want to save the current item before navigating away?").ShowAsync();
e.Cancel = false; //unconditionally setting this back to false and it still won't leave the page
base.OnNavigatingFrom(e);
}
To handle the navigation yourself, set Cancel=true (as you already do), then bring up the dialog to obtain user input. Once you know the user's choice, use the navigation APIs (e.g. Frame.GoBack) to perform the desired navigation (based on e.NavigationMode) if the user decided to allow the navigation to happen.
Here is some basic sample code:
private bool isNavigationConfirmed = false;
protected async override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
if (isNavigationConfirmed)
{
isNavigationConfirmed = false;
return;
}
e.Cancel = true;
var noCommand = new UICommand("No", cmd => { });
var yesCommand = new UICommand("Yes", cmd =>
{
if (e.NavigationMode == NavigationMode.Back)
{
Frame.GoBack();
}
else
{
isNavigationConfirmed = true;
Frame.Navigate(e.SourcePageType);
}
});
var dialog = new MessageDialog("Do you want to allow navigation?");
dialog.Options = MessageDialogOptions.None;
dialog.Commands.Add(yesCommand);
dialog.Commands.Add(noCommand);
await dialog.ShowAsync();
}
Related
I have made an windows form, with an initializing (form) which later redirects to the main form. The problem is it takes about three seconds to load, which make the user interface look really bad. The buttons are white, until they load, then they show the text and the colors. Is there any way to pre-load the form, but hide it, until the initializing (form) is finished?
For those who ask why it takes so long, I have a web browser which imports local HTML and it has InvokeText and addBase, addMaths and other items
This is the load script, how it loads the web browser
private async void TextEdit_Load(object sender, EventArgs e)
{
WebClient wc = new WebClient();
wc.Proxy = null;
try
{
RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", true);
string friendlyName = AppDomain.CurrentDomain.FriendlyName;
bool flag2 = registryKey.GetValue(friendlyName) == null;
if (flag2)
{
registryKey.SetValue(friendlyName, 11001, RegistryValueKind.DWord);
}
registryKey = null;
friendlyName = null;
}
catch (Exception)
{
}
webBrowser1.Url = new Uri(string.Format("file:///{0}/Files/TextEditor/Editor.html", Directory.GetCurrentDirectory()));
The next bit is the functionality of the web browser
await Task.Delay(500);
webBrowser1.Document.InvokeScript("SetTheme", new string[]
{
"Dark"
});
addBase();
addMath();
addGlobalNS();
addGlobalV();
addGlobalF();
webBrowser1.Document.InvokeScript("SetText", new object[]
{
""
});
}
I guess it is the problem with the webBrowser (text editor) because when I delete it, it no longer takes 3 seconds loading time.
For those who say use This.Hide(); and This.Show();, it does not work, because the web browser won't load at all.
if the main issue is that it takes about three seconds to load, then consider Threading on your form's Load event (provided you placed all pre-requisite loading there). This way you can initially disable controls prior to the form being displayed and enable controls once the entire process is finished. See below example:
private void Form1_Load(object sender, EventArgs e)
{
#region Disable controls here
textbox1.Enabled = false;
button1.Enabled = false;
combobox1.Enabled = false;
#endregion
Task.Factory.StartNew(() => {
try
{
// Do Long running processing of form prerequisites here.
...
// Enable controls here once processing is sucessful and complete.
Invoke((Action) (() => {
textbox1.Enabled = true;
button1.Enabled = true;
combobox1.Enabled = true;
}));
}
catch(Exception e)
{
Invoke((Action) (() => {
MessageBox.Show(e.Message);
}));
}
});
}
In my Xamarin.Forms project I have a login form that logs in the user and then redirects them to another page. I want to show an ActivityIndicator while it attempts to login, but setting IsVisible to true doesn't actually take effect before the login function is done. My code looks like this:
void OnLoginButtonClicked(object sender, EventArgs e)
{
LoadingIndicator.IsVisible = true;
Login();
}
public void Login()
{
var user = new User
{
Email = usernameEntry.Text,
Password = passwordEntry.Text
};
User validUser = AreCredentialsCorrect(user);
if (validUser != null)
{
Navigation.PushAsync(new ProfilePage());
}
else
{
messageLabel.Text = "Login failed";
passwordEntry.Text = string.Empty;
//It will only show the LoadingIndicator at this point.
}
}
If the user is correct, it never shows the LoadingIndicator because it navigates to another page before it can show it.
If the user is invalid, it will only show the LoadingIndicator after it hits the else clause and shows the "Login failed". Can anyone figure why that is, and what I can do to fix it?
Use Async calls and await the main block of operations. If anything you want to update/change in UI, do it using BeginInvokeOnMainThread.
void OnLoginButtonClicked(object sender, EventArgs e)
{
LoadingIndicator.IsVisible = true;
await Login();
}
public async void Login()
{
await Task.Run(() => {
var user = new User
{
Email = usernameEntry.Text,
Password = passwordEntry.Text
};
User validUser = AreCredentialsCorrect(user);
}).ContinueWith((a) => SomeMethod(validUser));
}
public void SomeMethod()
{
Device.BeginInvokeOnMainThread(() =>
if (validUser != null)
{
Navigation.PushAsync(new ProfilePage());
}
else
{
messageLabel.Text = "Login failed";
passwordEntry.Text = string.Empty;
//It will only show the LoadingIndicator at this point.
}
}
}
Try using async/await. Allow the UI to update while also navigating.
async void OnLoginButtonClicked(object sender, EventArgs e)
{
LoadingIndicator.IsVisible = true;
await Login();
}
public async Task Login()
{
var user = new User
{
Email = usernameEntry.Text,
Password = passwordEntry.Text
};
User validUser = AreCredentialsCorrect(user);
if (validUser != null)
{
await Navigation.PushAsync(new ProfilePage());
}
else
{
messageLabel.Text = "Login failed";
passwordEntry.Text = string.Empty;
//It will only show the LoadingIndicator at this point.
}
}
Two possible explanations here one it could be redirecting so fast that it doesn't have time to display the loadingindicator, I have experienced this problem before. We had a really nice loading symbol and animation but then we switched to Aurelia framework and it logs in so fast it just doesn't have time to display it even though it was actually working. As for code changes I would try to add it into the login function as well at least for right now to give some clarity to if its actually just logging in so fast its not displayed or its just not displaying at all. Her is my suggestion.
void OnLoginButtonClicked(object sender, EventArgs e)
{
LoadingIndicator.IsVisible = true;
Login(LoadingIndicator.IsVisible);
}
public void Login(Bool IsVisible)<--- might have type wrong not familiar with you custom defines types I would expect it to be a bool though.
IsVisible = true;
{
var user = new User
{
IsVisible = true;
Email = usernameEntry.Text,
Password = passwordEntry.Text
};
User validUser = AreCredentialsCorrect(user);
if (validUser != null)
{
IsVisible = true;
Navigation.PushAsync(new ProfilePage());
}
else
{
IsVisible = true;
messageLabel.Text = "Login failed";
passwordEntry.Text = string.Empty;
//It will only show the LoadingIndicator at this point.
}
}
If nothing else this might help clarify why it isn't being displayed. Also don't forget to use the web debugger f12 by default on most browsers and look for the element that will contain the Indicator.
Hope this helps! If not let me know and I'll remove the answer(I had to use an answer because I can't comment under 50 rep) Cheers!
I want to ask for confirmation if the user wants to leave the page when the Back button (hardware or AppViewBackButton) is pressed using the following code in my App.xaml.cs
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
...
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
...
}
Private async void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.Content.GetType() == typeof(GamePage))
{
var op = await ShowAsync("Do you want to exit?", "Exit?");
if (op == MessageBoxResult.OK)
{
rootFrame.GoBack();
e.Handled = true;
}
}
else
{
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
}
}
public async Task<MessageBoxResult> ShowAsync(string messageBoxText, string caption)
{
MessageBoxResult result = MessageBoxResult.None;
MessageDialog md = new MessageDialog(messageBoxText, caption);
md.Commands.Add(new UICommand("OK",
new UICommandInvokedHandler((cmd) => result = MessageBoxResult.OK)));
md.Commands.Add(new UICommand("Cancel",
new UICommandInvokedHandler((cmd) => result = MessageBoxResult.Cancel)));
md.CancelCommandIndex = (uint)md.Commands.Count - 1;
var op = await md.ShowAsync();
return result;
}
The problem is var op = await md.ShowAsync(); does not await the result. The app showed the dialog briefly and without waiting for any input, just close the app.
I do not have my Windows 10 Mobile device to test at the moment so I am testing with the emulator. Is the problem with the emulator or the code?
This code works fine on my desktop though.
I would like to prevent changing the current page by pressing Back button or any navigation events while awaiting not finished. Because then exception happens, its should shown in the same page, on the other case it would be difficult to understand what action generate this exception
private async void AppBarButton_Click(object sender, RoutedEventArgs e)
{
MessageDialog dialog = null;
try
{
progressRing.IsActive = true;
this.IsEnabled = false;
commandBar1.IsEnabled = false;
await GlobalVars.API.Call(CC.ChangeDate, date);
var notify = new MessageDialog("Done");
await notify.ShowAsync();
}
catch (Exception ex)
{
dialog = new MessageDialog(ex.Message);
}
finally
{
progressRing.IsActive = false;
this.IsEnabled = true;
commandBar1.IsEnabled = true;
}
if (dialog != null) await dialog.ShowAsync(); // must be shown in the same page
}
You can intercept the back button using:
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
}
Obviously you can do whatever you want in there.
I want to open the StdinfoPage page after clicking the Go to Admin button on the message box
how can i do that?
here is my button coding
private void button_login_Click(object sender, RoutedEventArgs e)
{
if (MyReader.HasRows && this.Frame != null)
{
while (MyReader.Read())
{
if (privilege == 1)
{
DisplayMsgBox("click to open the admin page ", "Go to Admin");
this.Frame.Navigate(typeof(StdinfoPage));
}
else
{
DisplayMsgBox("privilege 0", "ok");
}
}
}
else
{
DisplayMsgBox("sucess else", "ok");
}
conn.Close();
}
}
here is message box code
private async void DisplayMsgBox(string displayMsg, string displayBtn)
{
try
{
// Create the message dialog and set its content
var messageDialog = new MessageDialog(displayMsg);
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand(displayBtn, new UICommandInvokedHandler(this.CommandInvokedHandler)));
messageDialog.Commands.Add(new UICommand("Close", new UICommandInvokedHandler(this.CommandInvokedHandler)));
// Set the command that will be invoked by default
messageDialog.DefaultCommandIndex = 0;
// Set the command to be invoked when escape is pressed
messageDialog.CancelCommandIndex = 1;
// Show the message dialog
await messageDialog.ShowAsync();
}
catch (Exception)
{
}
}
Based on the example from here: http://msdn.microsoft.com/library/windows/apps/windows.ui.popups.messagedialog.aspx, you need to create this method that will be executed when Go to Admin or Close button is clicked
private void CommandInvokedHandler(IUICommand command)
{
// Check which button is clicked
if (command.Label == "Go to Admin")
{
// open StdinfoPage
this.Frame.Navigate(typeof(StdinfoPage));
}
}
and delete this.Frame.Navigate(typeof(StdinfoPage)); inside if (privilege == 1) block
if (privilege == 1)
{
DisplayMsgBox("click to open the admin page ", "Go to Admin");
}