How to create MessageBox in Unity without event/delegate callback? - c#

There is one part in MessageBox that I need to re-create in Unity.
DialogResult result = MessageBox.Show(/*parameters*/);
//When code runs - appears MessageBox and next lines of code WAIT for result
//Only if button is pressed - code proceeds next lines.
if (result == DialogResult.Yes) { DoIt(); }
else { DoNotDoIt(); }
I've seen some realization with Action callbacks.
In that realization - MessageBox has Action fields and in MessageBox.Show() method you should add delegates to propriate Action's as parameters.
I can't use it in my case. I need to ensure that dialog result is set and only then continue to run code.

You can run your code in Update. DialogResult could have 3 states, None, Yes and No. With None, you do nothing, Update returns and comes back next frame:
void Update()
{
DialogResult result = MessageBox.Show(/*parameters*/);
if (result == DialogResult.Yes) { DoIt(); }
else if(result == DialogResult.No) { DoNotDoIt(); }
}
other way is coroutine which is basically same as update:
IEnumerator DialogSequence()
{
while(MessageBox.Show(/*parameters*/)== DialogResult.None) { yield return null;}
DialogResult result = MessageBox.Show(/*parameters*/);
if (result == DialogResult.Yes) { DoIt(); }
else if(result == DialogResult.No) { DoNotDoIt(); }
}

Related

Why is this C# code executing out of sequence? Not ASYNC (at least I don't think it is)

Can anyone help me understand why my call to dialogservice executes after the CanNavigateAway function has returned its value? (My goal is to warn the user they are about to navigate away from a view without saving their changes. If they click OK, the navigation is allowed. I'm using MVVM Light.
When I step through the code, it does reach the dialog service, but then proceeds to the end of CanNavigateAway before creating the dialog. The CanNavigateAway method is called by OnNavigatingFrom.
public bool CanNavigateAway()
{
if (!changesSaved && Model.IsModified && !continueNavigation)
{
dialogService.ShowMessage("Are you sure you want to continue?",
"Confirmation",
buttonConfirmText: "Continue", buttonCancelText: "Discard",
afterHideCallback: (confirmed) =>
{
if (confirmed)
{
// User has pressed the "confirm" button.
// ...
continueNavigation = true;
}
else
{
// User has pressed the "cancel" button
// (or has discared the dialog box).
// ...
continueNavigation = false;
}
});
return continueNavigation;
}
}
Here is the OnNavigatingFrom method from the MVVM Light Bindable Page class:
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
{
if (!navigableViewModel.CanNavigateAway())
{
e.Cancel = true;
}
}
}
I tried this a different way to get the dialog service out of the mix, but showConfirmationDialogAsync still does not seem to execute in time:
public bool CanNavigateAway()
{
continueNavigation = false;
if (!changesSaved && Model.IsModified && !continueNavigation)
{
showConfirmationDialogAsync();
return continueNavigation;
}
private async void showConfirmationDialogAsync()
{
continueNavigation = false;
ContentDialog noSaveConfirmation = new ContentDialog
{
Title = "Warning",
Content = "You have unsaved changes. Are you sure you want to leave this page without saving?",
PrimaryButtonText = "Leave without saving",
SecondaryButtonText = "Stay and finish"
};
ContentDialogResult result = await noSaveConfirmation.ShowAsync();
if (result == ContentDialogResult.Primary)
{
continueNavigation = true;
}
else if (result == ContentDialogResult.Secondary)
{
continueNavigation = false;
}
}
None of the solutions will work if you require a response from the user. The problem is that when the code is inside the navigation event handler, it is running on the UI thread and the user prompt runs asynchronously, so that the UI is free to present the dialog to the user. This however means that the event handler finishes before the user has a chance to respond.
However, you can use a workaround solution. Add a flag bool field like forceNavigation. Then inside the OnNavigatingFrom display the dialog to the user and set Cancel to true right away and display the user the confirmation dialog. If the user says yes, then set forceNavigaiton to true and trigger the navigation manually again. Now it will skip the confirmation part and navigate right away.
protected async override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
//if navigation is forced, skip all logic
if ( !forceNavigation )
{
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
{
e.Cancel = true;
//display the dialog to the user, if he says yes, set
//forceNavigation = true; and repeat the navigation (e.g. GoBack, ... )
}
}
}

Not to execute any further code until the async method is completed its execution

Not to execute any further code until the async method is completed its execution. Please let me know how to achieve it.
Following is sample code :
// Parent Form code
private void btnOpenForm1_Click(object sender, EventArgs e)
{
Form1 form1 = new Form1();
var result = form1.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK)
{
// do something
}
}
// Child form code
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult result = MessageBox.Show(string.Format("Do you want to save changes?", "Confirmation", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (result == System.Windows.Forms.DialogResult.Cancel)
{
e.Cancel = true;
}
else
{
if (result == DialogResult.Yes)
{
e.Cancel = true;
this.DialogResult = System.Windows.Forms.DialogResult.OK;
// HERE I NEED TO WAIT COMPULSARILY TILL THE OPERATION IS COMPLETED, NO NEXT STATEMENT SHOULD BE EXECUTED (NEITHER IN PARENT FORM)
var isSaveCompleted = await HandleSave().ConfigureAwait(false);
if(isSaveCompleted == true)
{
// dispose some objects
}
}
else // if No is clicked
{
this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
// dispose some objects
}
}
}
public async Task<bool> HandleSave()
{
await doWork();
//
// some code here
//
}
public doWork()
{
//
// some code for I/O operation
//
}
In the above code, I don't want to execute the any of the next statements (not even in the Parent Form) until the HandleSave() method is completed its execution.
There's an XY problem here. Trying to force an asynchronous method to run synchronously in order to block closing the form is the wrong solution.
Instead of fighting window lifetimes, you need to figure out how to work with them. One solution for this scenario is to hide the form rather than closing it; and then actually close it when the asynchronous save completes. You can enable your parent form to detect when the child form is actually closed by using a TaskCompletionSource<DialogResult>.

Giving owner to MessageBox which is getting called from another thread

I am using CefSharp 37 in winforms. I have implemented IRequestHandler and want to write some ResourceHandler code in method OnBeforeResourceLoad.
in OnBeforeResourceLoad() I check some condition and on that basis I display a MessageBox with OK and Cancel buttons. By pressing Cancel I want to return true otherwise false. Code as below:
public bool OnBeforeResourceLoad(IWebBrowser browser, IRequest request, IResponse response)
{
if (!request.Url.Contains(ContentHelper.requestTrapKey)
{
var handler = browser.ResourceHandler;
if (handler != null)
{
handler.RegisterHandler(request.Url, ResourceHandler.FromStream(File.OpenRead(ContentHelper.contentRootPath), Path.GetExtension(ContentHelper.contentRootPath + final));
}
}
else if (!request.Url.Contains(ContentHelper.requestTrapKey + "course") && request.Url.Contains(ContentHelper.requestTrapKey))
{
if (MessageBox.Show("message", "title", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Cancel)
{
return true;
}
}
return false;
}
It is observed that sometime MessageBox() goes behind to main form and user waits for messagebox to come and also next code also in wait state to execute. I know that as MessageBox.Show() is called on other thread than main thread so that's why it's going behind.
So is there any way where I can show MessageBox or a message to user without going it behind and take input from user and accordingly return true or false to load resource.
Update
I tried as #Adil said in answer to wrap the code by MethodInvoker then MessageBox remain on top of form but as when I press any button on message box application get hanged.
I have written an parametrized constructor of MyRequestHandler class as :
public MyRequestHandler(MainForm mainform)
{
this.mainform = mainform;
}
Then as said by #Adil :
bool returnValue = false;
mainform.Invoke((MethodInvoker)(() =>
{
if (MessageBox.Show(mainform,"message", "title", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Cancel)
{
returnValue = true;
}
}));
return returnValue;
I tied with checking InvokeRequired of mainform but still it's
throwing same exception.
You not only need to check the InvokeRequired but you need to invoke on GUI thread. You can do that by using MethodInvoker.
If you have return with MethodInvoker delegate it will return from delegate instead of the method having MethodInvoker. You can set value of some bool variable to true, which you can use after delegate finishes its execution to return from the method.
bool returnValue = false;
mainform.Invoke((MethodInvoker)(() =>
{
if (MessageBox.Show(mainform,"message", "title", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Cancel)
{
returnValue = true;
}
}));
return returnValue;

Boolean Resets When App Loads Wp8

Hi I am still fairly new to C# & windows phone.
When the app Loads I wanted popup asking the user if they would like to do something
MessageBoxResult m = MessageBox.Show("Info.", "Question?", MessageBoxButton.OKCancel);
if (m == MessageBoxResult.Cancel)
{ }
else if (m == MessageBoxResult.OK)
{ //Do Something }
Now that works fine, if the user says no I wanted a popup that asked the user if they would like reminding next time so U used
MessageBoxResult m = MessageBox.Show("Info.", "Question?", MessageBoxButton.OKCancel);
if (m == MessageBoxResult.Cancel)
{
MessageBoxResult r = MessageBox.Show("", "Would You Like Reminding Next Time ?",MessageBoxButton.OKCancel);
if (r == MessageBoxResult.Cancel)
{ }
else if (r == MessageBoxResult.OK)
{ }
}
else if (m == MessageBoxResult.OK)
{ //Do Something }
I need some kind of a switch, so when the app starts for the first time
app checks switch which is on,
they get asked a question
if they answer cancel,
they get asked if they want reminding
if they answer no,
set switch to off
I've tried to use a boolean but it just resets to true when the app closes, if i use a string it says a string cant be used as a bool
Any Advice ?
Use IsolatedStorageSettings.ApplicationSettings to quickly save small values for example
// this will save my "your_key" to false;
IsolatedStorageSettings.ApplicationSettings.Add("your_key", false);
IsolatedStorageSettings.ApplicationSettings.Save(); // make sure you call save
// so the next time the app runs I can get it back doing this
bool your_key = (bool) IsolatedStorageSettings.ApplicationSettings["your_key"];
But, should always enclose it in a try catch because the key might not exist
bool your_key = false; // or default value
try
{
your_key = (bool) IsolatedStorageSettings.ApplicationSettings["your_key"];
}
catch(Exception ex)
{
}
More Information can be found here:
How to: Store and Retrieve Application Settings Using Isolated Storage
if(!IsolatedStorageSettings.ApplicationSettings.Contains("first"))
{
// Do your stuff
IsolatedStorageSettings.ApplicationSettings["first"] = true;
IsolatedStorageSettings.ApplicationSettings.Save();
}
This is all the code you need.
Place everything you want to do only on first launch into this if statement. Then execute this code either in main page Loaded event or OnNavigatedTo.

Alert user about the running process during installation

protected override void OnBeforeInstall(IDictionary savedState)
{
base.OnBeforeInstall(savedState);
DialogResult result = DialogResult.None;
if (isExcelIsRunning())
{
result = MessageBox.Show("Excel is still running. Please save your work and close Excel, and then click \"Retry\" button to continue with installation.", "", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning);
}
while (result == DialogResult.Retry)
{
if (!isExcelIsRunning())
{
break;
}
}
}
//check if excel is currently running
private bool isExcelIsRunning()
{
bool flag = false;
Process[] xlProc = Process.GetProcessesByName("excel");
if (xlProc != null)
flag = true;
return flag;
}
The above is the code I am about to use for my Installer Class.
What I want to achieve here is that I need the installer to check whether or not Excel is currently running during the installation. And if it is running, then there should be a pop-up message at the start of installation to alert user to close off Excel and the installer should pause until there's no Excel instance found in the process list any more.
Back to the code, I don't think that while block is quite right, as it could cause an endless loop after user clicks "Retry" button if in the mean time Excel is still running.
So what'd be the better approach? Would anyone here take a look at the above code and see where I can improve?
I think your code would be something like this:
while(isExcelRunning())
{
var result = MessageBox.Show("Excel is still running. Please save your work and close Excel, and then click \"Retry\" button to continue with installation.", "", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning);
if (result != DialogResult.Retry)
{
//Handle Cancel
break;
}
}
This will keep displaying the alert in a loop, either until Excel exits, or they press cancel, at which you can do whatever you need to do; then we exit the loop (or you could return entirely out of the method).
The key here is display the alert repetitively, either until Excel is gone or they choose to cancel. If you need to do some code after the loop based on the response; perhaps something like this:
var userHasCancelled = false;
while(!userHasCancelled && isExcelRunning())
{
var result = MessageBox.Show("Excel is still running. Please save your work and close Excel, and then click \"Retry\" button to continue with installation.", "", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning);
if (result != DialogResult.Retry)
{
userHasCancelled = true;
}
}
if (userHasCancelled)
{
//User cancelled...
}
else
{
//Continue.
}

Categories