Windows Form wont close properly - c#

I am developing an add-in for outlook 2010.
Basically, I have a button on my ribbon that takes the selected email, and saves it to a text file. If the email contains a certain subject then the save is done automatically to a hard coded file path. If not, a windows form is opened asking the user to enter a filepath.
When the user has selected a path, and clicked 'OK' the save takes place and then the form closes... but then it re-opens... it seems to be creating a new instance of it or something... if I click 'Cancel' or 'X' it closes, but I can't see why it's not closing properly the first time.
Below is my code
//This is myRibbon.cs
private void btn_SaveFile_Click(object sender, RibbonControlEventArgs e)
{
//other code
if (subject = "xyz")
{
//other code
textFile.Save();
}
else
{
MyPopup popup = new MyPopup();
popup.ShowDialog();
}
}
//This is MyPopup.cs
private void btnOK_Click(object sender, EventArgs e)
{
var filePath = txtFilePath.Text;
if (!string.IsNullOrWhiteSpace(filePath))
{
SaveEmailToText(filePath);
this.Close();
}
else
{ //show message box with error }
this.Close();
}
private static void SaveEmailToText(string filePath)
{
//other code
textFile.Save();
}
I have simplified this quite a bit so its easier to read.
Any help would be greatly appreciated.

Consider to use OpenFileDialog instead of your popup form
Use your popup (or file dialog) only for getting file name
Keep email saving code in one place (otherwise you will have duplicated code)
Verify DialogResult of dialog form, before processing further
Forms are disposable - using statement will dispose them automatically
Do not close dialog form - set it's DialogResult property instead
Here is refactored code:
private void btn_SaveFile_Click(object sender, RibbonControlEventArgs e)
{
string filePath = defaultPath;
if (subject != "xyz")
{
using(MyPopup popup = new MyPopup())
{
// user can close popup - handle this case
if (popup.ShowDialog() != DialogResult.OK)
return;
filePath = popup.FilePath;
}
}
SaveEmailToText(filePath);
}
private void SaveEmailToText(string filePath)
{
//other code
textFile.Save();
}
And your popup, which should be replaced with OpenFileDialog:
private void btnOK_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(FilePath))
{
//show message box with error
DialogResult = DialogResult.Cancel;
return;
}
// you can assign default dialog result to btnOK in designer
DialogResult = DialogResult.OK;
}
public string FilePath
{
get { return txtFilePath.Text; }
}

Related

How to return Dialog result ok from button click only if some condition are met

I am tryin to create a costume messagebox with datavalidation(username , Password) in Visual studio winforms
I already made another one with the password only that will take the text box text encrypt it and compare it the the stored encrypted password
private void TextBoxPass_TextChanged(object sender,EventArgs e)
{
//Storedpassword is the stored password and ButtonOk is the confirm Button
//Encrypt is the encription function
if(Encrypt(TextBoxPass.Text)==StoredPassword)
ButtonOk.DialogueResult=DialogueResult.OK;
}
this way i can easily capture the returned dialogue result.
but in the case where i have to verify Username And password from the database, I can't use the text change event cause it will connect to the database eachtime the User name or password text are changed so i tried the code below
This Part is the Dialogue result I call in the class passwordvalidation
public static DialogResult UserLogin()
{
using (FORMS.USER_LOGIN MSGPASS = new FORMS.USER_LOGIN())
{
DialogResult DLresult = MSGPASS.ShowDialog();
return DLresult;
}
}
And this is the form USER_LOGIN form code
private DialogResult drslt(string USRNME,string Pss)
{
//Some code to Verify UserName and Password
//from database and return DataTable dt
DialogResult DR;
//COMPARE
if (dt.Rows.Count == 1)
{
//login successful
DR = DialogResult.OK;
}
else
{
//login unsuccussful
DR = DialogResult.None;
}
return DR;
}
private void BULCONFIRM_Click(object sender, EventArgs e)
{
DialogResult DRS =drslt(TBULNAME.Text,TBULPassword.Text);
if (DRS == DialogResult.OK)
this.Close();
else
CLASS.CostumeMessages.ShowMessage("","",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
when I try to capture Dialogue result, it close the ShowDialogue form but it doesn't give the correct Dialogue result value
//Calling the DialogueResult
if(PasswordValidation.UserLogin()==DialogResult.OK)
//do some stuff;
at the same time when I use the button Cancel which has the dialogueresult proprety =Cancel, the result works normally.
So what i want to know is if there is a mistake that i am making or if there is another way to return DialogueResult.OK when I click the CONFIRM button??
Here is a simple solution:
In MainForm you open dialog form with this code (in a button click or where you want):
DialogForm form = new DialogForm();
if(form.ShowDialog() == DialogResult.OK)
{
// show menu
}
and in Dialog form you on Login and in Cancel events you need to have similarity to this:
private void Login_Click(object sender, EventArgs e)
{
//Check if password is ok
if(txtPassword.Text == "test")// check the password
{
this.DialogResult = DialogResult.OK;
}
else
{
// show error if password did not match
}
}
private void Cancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}

How can I cleanly shortcircuit my app from a canceled login form, and invoke my main form when login is successful?

I have a login form that displays prior to the main form, using this code in Program.cs:
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += Unhandled;
frmLogin loginForm;
using (loginForm = new frmLogin())
{
if (loginForm.ShowDialog() == DialogResult.OK)
{
NRBQConsts.userName = loginForm.UserName;
NRBQConsts.pwd = loginForm.Password;
NRBQConsts.currentSiteNum = loginForm.SiteNumber;
}
else
{
Application.Exit(); // klang!
}
}
Application.Run(new frmMain());
}
static void Unhandled(object sender, UnhandledExceptionEventArgs exArgs)
{
ExceptionLoggingService.Instance.WriteLog(String.Format("From
application-wide exception handler: {0}", exArgs.ExceptionObject));
}
If the user chooses the "Close" button (rather than attempting to log in), the login form should close, the main form should not display, and the app should shut down. If the user chooses the "OK" button, the login form should close, and the main form should then display.
What happens is choosing the "Close" button causes the app to crash with the clangy, Gong Show-esque "crash" sound. There is this exception msg in my log file:
Date: 4/9/2009 8:57:51 PM
Message: Reached frmLogin.buttonOK_Click
Date: 4/9/2009 8:57:51 PM
Message: From application-wide exception handler: System.NullReferenceException: NullReferenceException
at NRBQ.frmLogin.GetSiteNum()
at NRBQ.frmLogin.buttonOK_Click(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.ButtonBase.WnProc(WM wm, Int32 wParam, Int32 lParam)
at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
at Microsoft.AGL.Forms.EVL.EnterModalDialog(IntPtr hwnModal)
at System.Windows.Forms.Form.ShowDialog()
at NRBQ.Program.Main()
Here is the pertinent code in the login form:
public partial class frmLogin : Form
{
public string UserName { get { return textBoxUsername.Text; } }
public string Password { get { return textBoxPwd.Text; } }
public string SiteNumber { get { return GetSiteNum(); } }
private string GetSiteNum()
{
String candidateSiteNum =
listBoxSitesWithFetchedData.SelectedItem.ToString();
if (String.IsNullOrEmpty(candidateSiteNum))
{
candidateSiteNum = "42"; // TODO: Remove before deploying
}
return candidateSiteNum;
}
public frmLogin()
{
InitializeComponent();
}
private void buttonClose_Click(object sender, EventArgs e)
{
ExceptionLoggingService.Instance.WriteLog("Reached
frmLogin.buttonClose_Click");
}
private void buttonOK_Click(object sender, EventArgs e)
{
ExceptionLoggingService.Instance.WriteLog("Reached
frmLogin.buttonOK_Click");
if (SanityCheck())
{
this.Close(); // closing this login form should make the main
form active/visible
}
else
{
MessageBox.Show("You have not yet provided some key data; be
sure to enter a username and a password");
}
}
private bool SanityCheck()
{
// For now, anyway, no site has to be selected; if none, use "42" by
default
return ((!(String.IsNullOrEmpty(textBoxUsername.Text.Trim())))
&& (!(String.IsNullOrEmpty(textBoxPwd.Text.Trim()))));
}
Note: The "OK" button has its DialogResult set to "OK"; the "Close" button has its DialogResult set to "Cancel"
Why does Application.Exit() cause the app to crash? What is the kinder, gentler way to terminate the app?
And why does choosing the Okay button not allow the main form to subsequently display? The login form does close, even when selecting the OK button (if the sanity check passes), but that's all...the main form does not display.
UPDATE
I modified my code, based on the answers below, and now the Cancel works fine (closes the app with no klang/crash), but I'm still not seeing my main form after selecting the "OK" button on the login form, even though I'm programatically setting the DialogResult to OK in the login form code.
Pertinent code below.
PROGRAM.CS
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += Unhandled;
frmLogin loginForm;
using (loginForm = new frmLogin())
{
if (loginForm.ShowDialog() == DialogResult.OK)
{
HHSConsts.userName = loginForm.UserName;
HHSConsts.pwd = loginForm.Password;
HHSConsts.currentSiteNum = loginForm.SiteNumber;
// TODO: Verify that these vals are valid
Application.Run(new frmMain());
}
}
}
FRMLOGIN.CS
private void buttonClose_Click(object sender, EventArgs e)
{
ExceptionLoggingService.Instance.WriteLog("Reached
frmLogin.buttonClose_Click");
this.DialogResult = DialogResult.Cancel; // <= necessary?
this.Close();
}
private void buttonOK_Click(object sender, EventArgs e)
{
ExceptionLoggingService.Instance.WriteLog("Reached
frmLogin.buttonOK_Click");
this.DialogResult = DialogResult.OK;
if (SanityCheck())
{
this.Close();
}
else
{
MessageBox.Show("You have not yet provided some key data; be
sure to enter a username and a password");
}
}
This is what I see in my log file:
Date: 4/9/2009 9:56:47 PM
Message: Reached frmLogin.buttonOK_Click
Date: 4/9/2009 9:56:48 PM
Message: From application-wide exception handler: System.NullReferenceException: NullReferenceException
at HHS.frmLogin.GetSiteNum()
at HHS.frmLogin.get_SiteNumber()
at HHS.Program.Main()
As Merle Haggard sang, "So now it's back, to the coderoom again..."
UPDATE 2
I added a messagebox to GetSiteNum():
private string GetSiteNum()
{
ExceptionLoggingService.Instance.WriteLog("Reached
frmLogin.GetSiteNum()");
String candidateSiteNum =
listBoxSitesWithFetchedData.SelectedItem.ToString();
// TODO: Remove after testing
MessageBox.Show(String.Format("candidateSiteNum = {0}",
candidateSiteNum));
if (String.IsNullOrEmpty(candidateSiteNum))
{
candidateSiteNum = "03"; // TODO: Remove before deploying
}
return candidateSiteNum;
}
...and I don't see it, so it's blowing up on:
String candidateSiteNum = listBoxSitesWithFetchedData.SelectedItem.ToString();
...right after selecting the "OK" button on this login form.
Don't call Close() against your login form, just set DialogResult like below. Also note that I'm not setting DialogResult.OK until after SanityCheck() tells us we are good to go:
private void buttonClose_Click(object sender, EventArgs e)
{
ExceptionLoggingService.Instance.WriteLog("Reached frmLogin.buttonClose_Click");
this.DialogResult = DialogResult.Cancel;
}
private void buttonOK_Click(object sender, EventArgs e)
{
ExceptionLoggingService.Instance.WriteLog("Reached frmLogin.buttonOK_Click");
if (SanityCheck())
{
this.DialogResult = DialogResult.OK;
}
}
You should also make sure an item in the ListBox has been selected in your SanityCheck() function:
private bool SanityCheck()
{
if (listBoxSitesWithFetchedData.SelectedIndex == -1)
{
MessageBox.Show("You must select a site first!");
return false;
}
bool pass = ((!(String.IsNullOrEmpty(textBoxUsername.Text.Trim())))
&& (!(String.IsNullOrEmpty(textBoxPwd.Text.Trim()))));
if (!pass)
{
MessageBox.Show("You have not yet provided some key data; be sure to enter a username and a password");
}
return pass;
}
This is what the docs say on Application.Exit:
Informs all message pumps that they must terminate, and then closes all application windows after the messages have been processed.
This means that it will start a nice controlled shutdown - and not exit immediately, causing your code to continue to Application.Run, which will apparently then crash and burn.
One option would be to use Environment.Exit instead, which will immediately terminate, but it's a messy solution. Another option would be to return right after Application.Exit, so you never reach Application.Run. But still, not very nice.
Instead simply try:
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += Unhandled;
frmLogin loginForm;
using (loginForm = new frmLogin())
{
if (loginForm.ShowDialog() == DialogResult.OK)
{
NRBQConsts.userName = loginForm.UserName;
NRBQConsts.pwd = loginForm.Password;
NRBQConsts.currentSiteNum = loginForm.SiteNumber;
Application.Run(new frmMain());
}
}
}
Returning from Main will also exit the application, so nothing else is needed.
Edit: Apparently this wasn't the issue you were having, but anyway, this answers the question in the title.
It looks like you never set the Dialog result in your OK button.
Setting your dialog result like this:
this.DialogResult = DialogResult.OK;
after you sanity check and right before the this.Close() should solve the issue since the error is due to a Null reff.
You will also need to set the canceled result other places your form returns (or as I tend to do, on form load, assume a failure until you know its a success and return.)

Custom message box class has terminal "pop up" when the form does

Alright, this is a little strange, but essentially I needed a message box class with buttons that did not come as options within the message box class in c#. Therefore, I read up on how to create my own class (here is the link if your interested: http://social.msdn.microsoft.com/Forums/vstudio/en-US/1086b27f-683c-457a-b00e-b80b48d69ef5/custom-buttons-in-messagebox?forum=csharpgeneral), and used an example provided inside of that. Here is the relevant code:
public partial class Form1 : Form
{
public static bool MessageSucceded { get; set; }
public static string MessageContent{ private get; set; }
private void buttonMyMessageBox_Click(object sender, EventArgs e)
{
using (MyMessageForm myForm = new MyMessageForm())
{
myForm.ShowDialog();
if (MessageSucceded = myForm.ShowDialog(this) == DialogResult.OK)
{
if (MessageContent== "Yes do it")
{
//HERE DO WHAT YOU WANT IF THE USER CLICKS ON BUTTON YES DO IT
}
else if (MessageContent== "No, don`t do it")
{
//HERE DO WHAT YOU WANT IF THE USER CLICKS ON BUTTON NO, DON`T DO IT
}
}
}
}
}
public partial class MyMessageForm : Form
{
private void MyMessageForm_Load(object sender, EventArgs e)
{
this.StartPosition = FormStartPosition.CenterScreen;
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.MinimizeBox = false;
this.MaximizeBox = false;
this.buttonYes.Text = "Yes do it";
this.buttonNo.Text = "No, don`t do it";
this.labelMyForm.Text = "Are you sure you want to… ?";
}
private void buttonYes_Click(object sender, EventArgs e)
{
Form1.MessageSucceded = true;
Form1.MessageContent= buttonYes.Text;
this.DialogResult = DialogResult.OK;
this.Close();
}
private void buttonNo_Click(object sender, EventArgs e)
{
Form1.MessageSucceded = true;
Form1.MessageContent= buttonNo.Text;
this.DialogResult = DialogResult.OK;
this.Close();
}
}
This works exactly as I intended it to, except for one small detail. Just before the message box pops up, for a split second, a terminal version of the message box form opens up, before it closes on its own and the windows form version opens and functions as expected. I can't understand why this is happening, and it isn't causing any performance issues that I can notice, but aesthetically it looks very bad. Does anyone know why this is happening or how to stop it? I'm using Visual Studio 2010 Express for what its worth. Thanks for any help you have and taking the time to read this.
Edit: heres my code for main:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
You're invoking ShowDialog() twice:
myForm.ShowDialog();
if (MessageSucceded = myForm.ShowDialog(this) == DialogResult.OK) {
Form has been closed so it'll close immediatly, right way to do it is to save its first result and check that in your if condition:
var result = myForm.ShowDialog();
if (MessageSucceded = (result == DialogResult.OK)) {
Or directly:
MessageSucceded = myForm.ShowDialog() == DialogResult.OK;
if (MessageSucceded) {
Moreover if you returns different results according to clicked button then you do not need to compare MessageContent (it's also a bad idea to have a reference to caller just to return a result):
private void buttonYes_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Yes;
this.Close();
}
private void buttonNo_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.No;
this.Close();
}
Note that now you can simply remove such code and add proper DialogResult property to each button (because assign Form.DialogResult and call Form.Close() is default behavior if Button.DialogResult property is assigned). Done that you can simply use it like this:
switch (myForm.ShowDialog())
{
case DialogResult.Yes:
break;
case DialogResult.No:
case DialogResult.Cancel: // Unless ControlBox is also Hidden
break;
}
As last note: if you're targeting Vista+ you may also check Task Dialog instead of writing a custom form (you may keep it as fallback for XP's users).

How to reload/redisplay a form from within itself?

I have a form which I use as a modal dialog box for data entry. When the user clicks on the OK button on the form, I want the button handler to perform data validations and if there is any error the form should reload/redisplay itself instead of returning to the caller. Is this possible?
Caller code:
DatasetProperties propsWindow = new DatasetProperties();
if (propsWindows.ShowDialog() == DialogResult.Cancel)
return;
// Do other stuffs here
Form code:
public partial class DatasetProperties : Form
{
// Constructor here
// OK button handler
private void btnOK_Click(object sender, EventArgs e)
{
// Do data validations here
if (errorsFound)
{
// How to reload/redisplay the form without return to caller?????
}
}
}
Thanks for any help,
Don't let the user close the form without validation.
Use the FormClosing event. Here's an example. In lieu of the messageBox, include your validation code. If it doesn't validate, e.cancel = true.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("Are you sure you want to cancel without saving any changes?", "Confirm Cancel", MessageBoxButtons.YesNo) != DialogResult.Yes)
e.Cancel = true;
}
You can set the Form.DialogResult inside your DatasetProperties.btnOK_Click method to DialogResult.None, this way your DatasetProperties form will not return to the caller Form ('close').
// OK button handler
private void btnOK_Click(object sender, EventArgs e)
{
// Do data validations here
if (errorsFound)
{
this.DialogResult = System.Windows.Forms.DialogResult.None;
// How to reload/redisplay the form without return to caller?????
}
}
This way you can 'stay' in your DatasetProperties form as long as you have errors.
From the msdn, when the DialogResult Enumeration is set to None Nothing is returned from the dialog box. This means that the modal dialog continues running.
as user1646737 mentioned you can use FormClosing event like this:
private void btnOK_Click(object sender, EventArgs e)
{
// Do data validations here
Close();
}
Event:
private void DatasetProperties_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = errorsFound;
}

get user input in form2 and display data in form1 in c#

in the left picture, there is search button. when click, it will popup the second form (right picture).
when entering the keyword on search form (form2), the data will appear at the form1. how to pass the word enter by user in form2 to form1?
this is the code in form1.
private void button5_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.ShowDialog(); //open form2-search form
//kene get data input dr form2
XmlDocument xml = new XmlDocument();
xml.Load("C:\\Users\\HDAdmin\\Documents\\SliceEngine\\SliceEngine\\bin\\Debug\\saya.xml");
XmlNodeList xnList = xml.SelectNodes("/Patient/Patient/Name");
foreach (XmlNode xn in xnList)
{
string name = xn.InnerText;
listBox21.Items.Add(name);
}
}
this is the code in form2.
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
MessageBox.Show("Please enter keyword to search");
}
else
{
//send data input to form1.
}
can anyone help me with this? thank you
===EDIT===
i am referring to this link to solve this problem. There are two ways and i am using the second method and it works perfectly. I am crying out loud for this. thank you to the blogger owner.
i also found that, in order to view the data, i need to view it in TextBox and not ListBox. what i did before is im trying to view this in ListBox. i am not sure why but that is it. anyway, this problem SOLVE! thanks again for those who help me with this topic. i am grateful.
You can, for example, simply use a property:
Form2:
public string UserText { get; set;}
...
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
MessageBox.Show("Please enter keyword to search");
}
else
{
UserText = textBox1.Text; // set the Text
}
Form1:
private void button5_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.ShowDialog(); //open form2-search form
string text = from2.UserText; get the Text
....
Create a property (or properties) on Form2 exposing the values of the controls you want. So if you want the search term do it like:
public string SearchTerm
{
get { return this.textBox1.Text; }
}
Also, on a side-note; don't forget to check if the user actually did press search.
The way you have it now, when a user closes the form with the x it will also search. That doesn't seem logical to the user.
Make the button on your Form2 ModalResult.OK and do it like this:
if (form2.ShowDialog() == ModalResult.OK)
{
// Do your thing
}
You can sign for Form2 button clicked event in Form1 class:
// Form1's button5 clicked event handler.
private void OnButton5Clicked(object sender, EventArgs e)
{
form2.button1.click += this.OnSearchButtonClicked;
}
// form2.button1 clicked event handler.
// this method will rise when form2.button1 clicked.
private void OnSearchButtonClicked(object sender, EventArgs e)
{
if (form2.textBox1.Text == "")
{
MessageBox.Show("Please enter keyword to search");
}
else
{
// unsign from event!!!
form2.button1.click -= this.OnSearchButtonClicked;
// here you can use form2.textBox1.text
string searchRequest = form2.textBox1.Text;
}
// your business-logic...
}
However, answers proposed by #BigYellowCactus and #Gerald Versluis are simpler and more preferable.
By the way, do not use default button names. It'll be hard to understand their purposes in future. You can rename form1.button5 in form1.showFindWindowButton and form2.button1 in form2.startSearchButton.
I used a simple solution in my project and few days ago.
I recommend using inner-class form.
create a normal form to get the seach string (just like you did), for example fSearch, then use ShowModal to display it instead of Show().
here is an example (psuedo c#):
class MainClass : form
{
String search = String.Empty;
private void button5_Click(object sender, EventArgs e)
{
SearchString s = new SearchString();
s.ShowModal();
search = s.search;
}
.
.
class SearchString : Form
{
public String strString = String.Empty;
private void btnOK_Click(object sender, EventArgs e)
{
this.strString = text1.text;
this.close();
}
}
}

Categories

Resources