I'm trying to show a MessageBox from another Thread using Dispatcher.Invoke.
The MessageBox did show up. But without clicking the OK button on the MessageBox, I can still control the main window.
I'm thinking if there's a way to block the main window input before clicking the OK button?
Any help is appreciated.
Code I have so far:
void ShowError(String message) //I call this function in another thread
{
if (this.Dispatcher.CheckAccess())
MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
else
{
this.Dispatcher.Invoke(
new Action(() =>
{
MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}));
}
}
The only way to be guaranteed that the message box blocks the window is by passing the window in to the message box.
void ShowError(String message, Window windowToBlock) //I call this function in another thread
{
if (this.Dispatcher.CheckAccess())
MessageBox.Show(windowToBlock, message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
else
{
this.Dispatcher.Invoke(
new Action(() =>
{
MessageBox.Show(windowToBlock, message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}));
}
}
Related
I've a form that runs a query from a DataGridView on a button click. The below code works how I expect it to;
Opens a form that says "Please Wait while query runs"
Runs query
Closes form when loaded and then fill the DataGridView with the
data.
I have added a picturebox with simple gif - purely for the UI and the user to see it is working. But the gif doesn't actually spin - just shows as a picture. Testing it, if I run it on a form on its own it is fine, I can only hazard a guess that the query running is stopping it from working how it should.
PleaseWaitForm pleaseWait = new PleaseWaitForm();
try
{
pleaseWait.Show();
Application.DoEvents();
this.singleCenTableAdapter.Fill(this.singleCenData.SingleCenTable, ((System.DateTime)(System.Convert.ChangeType(txtBookFrom.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtBookTo.Text, typeof(System.DateTime)))));
int RowC = singleCenTableDataGridView.RowCount;
pleaseWait.Close();
if (RowC == 0)
{
MessageBox.Show(GlobVar.NoResults, "", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
pleaseWait.Close();
}
catch (System.Exception exc)
{
GlobVar.vid.Open();
var LogName = GlobVar.LoginName.ExecuteScalar();
GlobVar.vid.Close();
MessageBox.Show
(
"An error has occured " + LogNam + ". Please try again. \n\n" +
exc.Message, "An error has occured", MessageBoxButtons.OK, MessageBoxIcon.Warning
);
}
finally
{
pleaseWait.Close();
}
The "Please Wait" form is just a label & picture so there is nothing but the Initalize in there at the minute ;
public PleaseWaitForm()
{
InitializeComponent();
}
Does anybody have any ideas on how to tackle this to make it work correct? Or anything particular that I am doing wrong? I know for the most part I might get a bit of stick for using Application.DoEvents() anyway, but any help is appreciated!
I read in your comment that you use .NET 4.0 and therefore can not use await and async with Task<>.
Before .NET 4.5, you can use Thread or BackgroundWorker to achieve this,
refer to the following example of BackgroundWorker:
Background Worker loading screen in Winforms
Your current code is running in synchronously, you are expecting it to work in asynchronously.
You need async and await to keep your UI responsive.
First make the method this code is in async as follows:
private async void Method()
{
...
}
Next place the following code in a new method:
this.singleCenTableAdapter.Fill(this.singleCenData.SingleCenTable, ((System.DateTime)(System.Convert.ChangeType(txtBookFrom.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtBookTo.Text, typeof(System.DateTime)))));
int RowC = singleCenTableDataGridView.RowCount;
For example:
private async Task<int> FillAndGetRowCount( ... ) // add parameters if needed
{
this.singleCenTableAdapter.Fill(this.singleCenData.SingleCenTable, ((System.DateTime)(System.Convert.ChangeType(txtBookFrom.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtBookTo.Text, typeof(System.DateTime)))));
return singleCenTableDataGridView.RowCount;
}
Finally, change your try block to:
try
{
pleaseWait.Show();
Application.DoEvents();
int RowC = await FillAndGetRowCount( ... );
pleaseWait.Close();
if (RowC == 0)
{
MessageBox.Show(GlobVar.NoResults, "", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
I want to create a "Are you sure?" system when user clicks the close button of window. Is it possible to catch FormClosing event in Unity ?
Thanks.
Edit:
void OnApplicationQuit()
{
DialogResult result = MessageBox.Show(
"Are you sure you want to cancel ?",
"Question",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
Application.Quit();
}
}
I tried this to open a dialog when user clicks (X) button. It works but, looks like it creates a new dialog each frame.
This question needs an updated answer as Application.CancelQuit() is deprecated and MonoBehaviour.OnApplicationQuit() doesn't prevent the application from closing.
To accomplish this exit confirmation method, it's better to use: Application.wantsToQuit.
From the docs:
Unity raises this event when the player application wants to quit.
Add an event handler to this event to receive a notification that application is attempting to quit.
When this event is raised the quit process has started but can be cancelled. This means the player is not guaranteed to quit. For a guaranteed quit event take a look at Application.quitting.
Return true and the quit process will continue. Return false and the quit process will cancel.
Example:
// When the application starts, append a method
// that will run when the user attempts to exit
[RuntimeInitializeOnLoadMethod]
static void RunOnStart() {
Application.wantsToQuit += WantsToQuit;
}
public static bool quitConfirmation = false;
static bool WantsToQuit() {
if(quitConfirmation) {
return true;
} else {
RequestQuitConfirmation();
}
return false;
}
static void RequestQuitConfirmation() {
DialogResult result = MessageBox.Show(
"Are you sure you want to cancel ?",
"Question",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
quitConfirmation = true;
Application.Quit();
}
}
Note: The return value of this event (Application.wantsToQuit) is ignored when exiting play mode in the editor. IMPORTANT: The return has no effect on iPhone. Application can not prevent termination under iPhone OS.
Have a look at MonoBehaviour.OnApplicationQuit() and Application.CancelQuit()
I have messagebox in one .cs file and i want to clear textbox which is in another .cs file on ok button click.
I have used
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
{
_upload._txtbxNotes.Text = "";
}));
for solving threading issue .But it doesnt help me .it still shows the same error.
Any help appreciated.
Update:
Hello,I also updated my code but it does not change textbox value .it remain as it is .I have one more thread on upload screen for progresssbar.
Here is my code:FileUpload.cs
if (String.Equals(objFileUploadResponse.responseCode, 102))
{
// MessageBox.Show("File Uploaded Successfully");
ipbup.ReportProgress(qpvsChunk);
DialogResult dialogResult = System.Windows.Forms.MessageBox.Show("File Uploaded", "", MessageBoxButtons.OK);
if (dialogResult == DialogResult.OK)
{
_upload.SetNotes(" ");
}
}
else
{
System.Windows.MessageBox.Show("File Uploaded Failed");
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Upload.cs:
public void SetNotes(string note)
{
_txtbxNotes.Dispatcher.Invoke(() =>
{
_txtbxNotes.Text = note;
});
}
This is probably because you are running on another thread. When you call the System.Windows.Threading.Dispatcher.CurrentDispatcher on another thread, it will create a new Dispatcher. But you don't want a new dispatcher, you want to use the Dispatcher who 'owns' the control/window.
To solve this, your controls/window has a Dispatcher property. (it referes to the Dispatcher it was created on)
You can try:
// use the Dispatcher from the _upload control.
_upload._txtbxNotes.Dispatcher.Invoke(() =>
{
_upload._txtbxNotes.Text = "";
}));
A 'better' aproach could be: Creating a method within your _upload class called (for example) _upload.SetNotes(string note); This way you keep logic separated. Today you want a TextBox tomorrow you might want a Label. This way only your upload window/control/class is inflicted.
// for example: (pseudo)
_upload.SetNotes("");
class UploadWindow
{
// ......
public void SetNotes(string note)
{
_txtbxNotes.Dispatcher.Invoke(() =>
{
_txtbxNotes.Text = note;
}));
}
}
I am alerting the user about the process complete like :
var result = MessageBox.Show("Completed!!", "Upload Status", MessageBoxButton.OK, MessageBoxImage.Information);
if (result == MessageBoxResult.OK)
{
result = MessageBox.Show("Do you want to Quit the application ?", "Quit Application", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
OpenWebSite();
// I want to close the message box and complete application.
}
}
If I write this.Close..It is giving exception, So I wrote it like this :
Dispatcher.BeginInvoke(new Action(() => { this.Close(); }));
Still the page is only closing after two click on [Yes] button, which is making the the OpenWebSite(); to be called two times. ...
Any idea on what is causing this?
Use Application.Current.Shutdown()
I am trying to create a login form. The problem that I am having is that the login process is taking too long and is locking up my GUI. I have read up on background worker, but I am still uncertain on how to have my program wait for the login process but not freeze my GUI. Here is my code to help explain it more.
Login.cs
public partial class Login : Form
{
public delegate bool Authenicate(string account, string password,string type);
public Authenicate authenicate;
public Login()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (txtAccount.Text == string.Empty)
{
MessageBox.Show("Must include account number", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (txtPassword.Text == string.Empty)
{
MessageBox.Show("Must include password", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!authenicate(txtAccount.Text, txtPassword.Text,cmbType.Items[cmbType.SelectedIndex].ToString()))
{
return;
}
this.DialogResult = DialogResult.OK;
}
private void Login_Load(object sender, EventArgs e)
{
cmbType.SelectedIndex = 0;
}
MainForm.cs
public partial class MainForm: Form
{
Ex.Service myService=new Ex.Service();
public MainForm()
{
InitializeComponent();
}
public bool Authenicate(string account, string password,string type)
{
try
{
//Login takes too long and locks up GUI
//Maybe try background worker, but how to wait until
//login is complete?
myService.Login(account,password,type);
return myService.IsLogin();
}
catch(Exception exception)
{
MessageBox.Show(exception.message);
}
return false;
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
myService.Logout(); //Logout from service
myService = null;
}
}
Thank you for your time.
The general steps are:
Add the Background Worker to your Login dialog
Create an event handler for the Background Worker's DoWork event that calls you authenticate delegate.
In btnLogin_Click disable the Login dialog so the user cannot click login again while the back ground worker is running.
In btlLogin_Click call the BackGround worker's RunWorkAsync method to start the worker running.
Create an event handler for the Background Worker's RunWorkerCompleted event. In that event enable the LoginForm and either close the dialog if the login result was successful or display en error message.
I would say create an event in the login form and subscribe in the main form. Within the login form you can use the thread to perform login task if it is taking too long. Once login is succesful or unsuccessful you can notify using this event to main form and send the additional information in the event arguments to the main form.
Upon recieving this event, main form can then proceed based on the conditions set by you.
Disable the pertinent UI elements (buttons, textfields, etc.) and then spin up a background worker thread. When it completes, update the UI as appropriate.
Communicating with the UI could take the form of LoginSucceeded and LoginFailed events, or similiar.