I am new to c# and kind of winging it. using Microsoft Visual C# 2010
I have checked many similar posts and none of the suggestions seem to help
I am getting the following error: "Cannot access a disposed object"
which references the main form here
private void btn_RunPkgs_Click(object sender, EventArgs e)
{
RunPackages rp = new RunPackages();
this.Hide();
rp.ShowDialog();//The error points to this line
this.Show();
}
here is the code that blows up when the security check fails.
private void securityCheck()
{
if (MyGlobals.FormCheck("RUN_JOBS") == 1)
{
InitializeComponent();
}
else
{
//this.BeginInvoke(new MethodInvoker(this.Close));
//this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
MessageBox.Show("You do not have permission to access this form!");
//this.Close();
this.Dispose();
}
}
EDIT
It looks like I am going to go with Adriano Repetti's idea of putting the security where I call the page, but I am a little nervous now having any security on the page.
private void btn_RunPkgs_Click(object sender, EventArgs e)
{
if (MyGlobals.FormCheck("RUN_JOBS") == 1)
{
RunPackages rp = new RunPackages();
this.Hide();
rp.ShowDialog();
this.Show();
}
else
{
MessageBox.Show("Not for You!");
}
}
private void btn_ListUpdater_Click(object sender, EventArgs e)
{
if (MyGlobals.FormCheck("MDM") == 1)
{
ListUpdater lu = new ListUpdater();
this.Hide();
lu.ShowDialog();
this.Show();
}
else
{
MessageBox.Show("Private!");
}
}
EDIT2
Came up with the following possible solution but am nervous to use it because I am new at this and don't know what issues there might be. Any problems with just creating an event handler for form load?
namespace RunPackages
{
public partial class ListUpdater : Form
{
public ListUpdater()
{
InitializeComponent();
this.Load += new EventHandler(securityCheck);
}
private void securityCheck(object sender, EventArgs e)
{
if (MyGlobals.FormCheck("MDM1") == 0)
{
MessageBox.Show("Not Allowed!");
this.Close();
}
}
You can't dispose of the form within the form itself. The ShowDialog() method tries to access the form on exit for things such as DialogResult.
After a form has been disposed almost all of its methods can't be accessed (and most of its properties are invalid).
In your first line of btn_RunPkgs_Click() you create an object and you dispose it inside its constructor. Per se, even if pretty bad habit you may even call Dispose() from within constructor, it may even work but then you try to use such object ShowDialog() will generate ObjectDisposedException. Note that this code will also lead to same result (an exception):
RunPackages rp = new RunPackages();
rp.Dispose();
Yes you may check IsDisposed but that won't make code readable and problem (IMO) is you're mixing things. Constructor shouldn't contain such logic.
The point isn't just where you dispose your form. What's better is to don't even create such form (let me assume, because you call InitializeComponent(), that securityCheck() is called inside form constructor), for this you may use a factory static method:
public static bool TryShowDialog(Form currentForm)
{
if (MyGlobals.FormCheck("RUN_JOBS") != 1)
return false;
if (currentForm != null)
currentForm.Hide();
RunPackages dlg = new RunPackages();
dlg.ShowDialog();
if (currentForm != null)
currentForm.Show();
return true;
}
Your calling function will then be reduced to:
private void btn_RunPkgs_Click(object sender, EventArgs e)
{
RunPackages.TryShowDialog(this);
}
Note that such function is highly eligible for some refactoring (for example to extract code to hide/show existing form). Something like this:
public static bool ShowDialog<T>(Form currentForm, string authorizationId)
where T : Form, new()
{
if (MyGlobals.FormCheck(authorizationId) != 1)
return false;
if (currentForm != null)
currentForm.Hide();
T dlg = new T();
T.ShowDialog();
if (currentForm != null)
currentForm.Show();
return true;
}
Used like this (now code is reused everywhere):
SecurityHelpers.ShowDialog<RunPackages>(this, "RUN_JOBS");
Please note that calling code may be simplified (authorizationId may be an attribute on RunPackages, for example, and also currentForm can be deduced from current active form).
EDIT Calling Close() isn't better, if window handle has not been created (let's simplify little bit: it's created when window is shown) internally it'll call Dispose() (then above applies).
I would not try to disrupt the chaining of events that lead to the form creation.
The side effects are difficult to predict and what works today could not work in future versions.
Instead I would try a different approach
private void securityCheck()
{
if (MyGlobals.FormCheck("RUN_JOBS") == 1)
{
InitializeComponent();
}
else
{
Label message = new Label();
message.Dock = DockStile.Fill;
message.Text("You do not have permission to access this form!.");
message.TextAlign = ContentAlignment.MiddleCenter;
this.Controls.Add(message);
}
}
In this way I let the form show with just one label that covers the entire form surface with your message. The user could only close the form (provided that you have not removed the Control Box)
By the way, this has the advantage of avoiding dangerous oversights because it doesn't require any change on the calling code and the final effect is to effectively block the use of the form.
If you insist in closing the form during its constructor phase then you could get some advices from this question
I came up with the following, can anyone tell me if there are any issues with this?
namespace RunPackages
{
public partial class ListUpdater : Form
{
public ListUpdater()
{
InitializeComponent();
this.Load += new EventHandler(securityCheck);
}
private void securityCheck(object sender, EventArgs e)
{
if (MyGlobals.FormCheck("MDM1") == 0)
{
MessageBox.Show("Not allowed!");
this.Close();
}
}
etc...
Use a flag. For example change the code, like this:
public bool IsDisposed;
private void securityCheck()
{
if (MyGlobals.FormCheck("RUN_JOBS") == 1)
{
InitializeComponent();
}
else
{
//this.BeginInvoke(new MethodInvoker(this.Close));
//this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
MessageBox.Show("You do not have permission to access this form!");
//this.Close();
this.Dispose();
this.IsDisposed = true;
}
}
Then:
private void btn_RunPkgs_Click(object sender, EventArgs e)
{
RunPackages rp = new RunPackages();
if(rp.IsDisposed)
return;
this.Hide();
rp.ShowDialog();//The error points to this line
this.Show();
}
Related
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).
I have been stuck with this for some time now. I can't open a new form on button click.
If i create and .Show() form in the start form constructor i will work. I dont get it! :-(
StartUp Form
public Form1()
{
InitializeComponent();
startmessage();
br = Logic.loadXML("theshiiiiiittt.xml");
br2 = br.Clone();
loadboxes();
//serializeTest();
t = new Thread(contactDBUpdate);
//t.IsBackground = true;
t.Start();
}
Button event:
private void resultButton_Click(object sender, EventArgs e)
{
ResultForm rf = new ResultForm(this);
rf.Show();
this.Enabled = false;
}
Hope this is enough.
In my case it was caused by the fact that i wanted to make my forms non-modal. So i changed them from form.ShowDialog(parentForm) to form.Show().
But that caused the ObjectDisposedException if i try to show a form a second time because somewhere in the code was this.Close();. Form.Close also disposes it.
MSDN:
When a form is closed, all resources created within the object are
closed and the form is disposed.
I just needed to change
this.Close();
to
this.Hide();
Found my code problem.
I took one more look at the Stack trace and found i a message "Icon".
this.Icon.Dispose();
Startupform had this line.
This code fixed my problem:
private void resultButton_Click(object sender, EventArgs e)
{
ResultForm rf = new ResultForm(this);
rf.Icon = this.Icon;
rf.Show();
this.Enabled = false;
}
Thanks for the helping hands...
The problem is that your form object loose the scope and is disposed off.
If you want to keep the dialog open, use Form.ShowDialog();
Try this:
private void resultButton_Click(object sender, EventArgs e)
{
using(ResultForm rf = new ResultForm(this))
{
rf.ShowDialog();
}
this.Enabled = false;
}
Wile Implementing singleton pattern on windows form I got this error too.
The solution is that you have to assign a null value to the static reference in
protected override void Dispose(bool disposing)
by putting simple line.
obj=null; //obj is the static reference in the class.
Whilst developing a current project which contains a number of WinForms, I'm finding myself becoming cluttered with lines of code simply to handle open / close events for the forms. Currently I'm handling them like so..
//Declare forms
myForm mForm1;
myForm2 mForm2;
private void btnSomething_Click(object sender, EventArgs e)
{
if (mForm1 == null)
{
mForm1 = new myForm();
mForm1.FormClosed += new FormClosedEventHandler(mForm1_FormClosed);
mForm1.Show();
}
else
if (mForm1.WindowState == FormWindowState.Minimized)
mForm1.WindowState = FormWindowState.Normal;
mForm1.Focus();
}
void mForm1_FormClosed(object sender, FormClosedEventArgs e)
{
mForm1 = null;
}
And then another set of voids to handle each forms open / close. Now imagine that instead of 2 forms, I've got, say, 5 forms. Now I'm even more cluttered. Is there a way to generalize this to have all forms have the same event handlers?
I've thought of perhaps using the object sender in an "as" statement, but i'm not sure how i'd find the relevant declared form instance from there.
sender as (form)
Any ideas?
Make your Forms implementing some IFormWithMyEvents.
You can generalize that code easily:
//Declare forms
myForm mForm1;
myForm mForm2;
private void btnSomething_Click(object sender, EventArgs e)
{
ShowOrUpdateForm<myForm>(ref mForm1);
}
void ShowOrUpdateForm<T>(ref Form form) where T : Form
{
if (form == null)
{
form = new T();
form.FormClosed += new FormClosedEventHandler(mForm1_FormClosed);
form.Show();
}
else if (form.WindowState == FormWindowState.Minimized)
form.WindowState = FormWindowState.Normal;
form.Focus();
}
void mForm1_FormClosed(object sender, FormClosedEventArgs e)
{
// you cannot refactor this easily
if (sender == mForm1)
mForm1 = null;
else if (sender == mForm2)
mForm2 = null;
}
If you want to further generalize the closed event handler, you should consider moving forms' declarations to some sort of an array, list of dictionary. That way you can easily generalize that method.
EDIT: converted the ShowOrUpdateForm function to generic.
I want to do something after the document have completely load... I dont want to use WebBrowser.DocumentCompleted Event, so please don't suggest me this.
I tried 2 ways to do it but they not work. Can someone tell me what I doing wrong?
Example 1
wb.Navigate("http://www.google.com");
while(wb.ReadyState != WebBrowserReadyState.Complete) { }
richtextdocument.Text = wb.DocumentText;
Example 2
wb.Navigate("http://www.google.com");
while(wb.isBusy == true) { }
richtextdocument.Text = wb.DocumentText;
Try to use timer to validate document load state.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
if(webBrowser1.ReadyState == WebBrowserReadyState.Complete)
{
timer1.Enabled = false;
richTextBox1.Text = webBrowser1.DocumentText;
}
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
webBrowser1.Navigate("http://www.google.com");
}
}
What you are dealing with here is trying to call an inherently asynchronous method synchronously.
As you mentioned in the comments to your question that the reason for not using DocumentCompleted is that you will need to use that event for other purposes, what I suggest you do is to use the DocumentCompleted event, coupled with a private class boolean flag to determine if this is the special case of DocumentCompleted or not.
private bool wbNeedsSpecialAction; //when you need to call the special case of Navigate() set this flag to true
public Form1()
{
InitializeComponent();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
}
void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (wbNeedsSpecialAction)
{
richtextdocument.Text = wb.DocumentText;
wbNeedsSpecialAction = false;
}
else
{
//other cases of using DocumentCompleted...
}
}
public void Browse()
{
wbNeedsSpecialAction = true; //make sure the event is treated differently
wb.Navigate("http://www.google.com");
}
This will still allow you to control other cases within the event handler.
You must take special care to ensure that your user is not able to trigger another call to Navigate() before this 'special action' page finishes loading, otherwise it may steal the special case event. One way could be to block the UI until the page finishes loading, e.g.:
Cursor.Current = Cursors.WaitCursor;
while(wb.ReadyState != WebBrowserReadyState.Complete) {application.doevents }
RESOLVED: Turned out to be a visual studio problem. Closed visual studio, cleaned and rebuilt, and the value started showing. Thanks all for the help, sounds like I need to switch to VS2010.
This may not be the best, safest, or preferred way to pass values between forms, but this is the way I am attempting for the moment. So, please do help me to get this way working. After you provide an answer, you're more than welcome to add in some better ways of doing this.
The problem is, when the modal dialog box closes and I go back to the owner, the textbox value from the modal is an empty string rather than the actual value. I've read in several places this should not be the case, as the data should persist even after the modal box disposes. Here's my code.
public partial class PreferencesForm : Form
{
public PreferencesForm()
{
InitializeComponent();
}
private void okButton_Click(object sender, EventArgs e)
{
if (masterRadioButton.Checked == true)
{
if (password1TextBox.Text != password2TextBox.Text)
{
errorLabel.Text = "Passwords do not match, please re-enter both passwords and try again.";
this.Refresh();
}
else if (password1TextBox.Text == "" && password2TextBox.Text == "")
{
errorLabel.Text = "You must enter a password.";
}
else
{
okResultButton_Click(null, null);
}
}
else if (singleRadioButton.Checked == true)
{
okResultButton_Click(null, null);
}
}
private void cancelButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Dispose();
}
private void okResultButton_Click(object sender, EventArgs e)
{
// invisible button
this.DialogResult = DialogResult.OK;
this.Dispose();
}
And here is the code that calls the above form as a modal dialog box.
private void setPreferencesToolStripMenuItem_Click(object sender, EventArgs e)
{
PreferencesForm pf = new PreferencesForm();
DialogResult result = pf.ShowDialog();
if (result == DialogResult.OK)
{
if (pf.password1TextBox.Text != "")
{
masterPassword = pf.password1TextBox.Text;
}
else
{
masterPassword = null;
}
}
}
Thanks for any assistance. I'm getting pretty frustrated over here. >:(
Note: The ReadOnly property of the password1TextBox variable is correctly shown as true or false, depending on what I select in the modal form, but the text property will still not correctly display.
I'm guessing that Dispose will also dispose the controls it contains. After the controls have been disposed, the text is likely no longer valid either. Try Close rather than Dispose and then Dispose in the caller.
You should listen to the people answering your question. Dispose is supposed to clear out memory allocated, it doesn't matter if you can still get the ReadOnly property.
Don't call Dispose in the form, call dispose from the calling code, as in the example code from the ShowDialog method documentation (http://msdn.microsoft.com/en-us/library/c7ykbedk.aspx#Y851). Note that Dispose is called just before the testDialog variable goes out of scope.
public void ShowMyDialogBox()
{
Form2 testDialog = new Form2();
// Show testDialog as a modal dialog and determine if DialogResult = OK.
if (testDialog.ShowDialog(this) == DialogResult.OK)
{
// Read the contents of testDialog's TextBox.
this.txtResult.Text = testDialog.TextBox1.Text;
}
else
{
this.txtResult.Text = "Cancelled";
}
testDialog.Dispose();
}
I propose just save the string of the control of your Dialog into string property, and retrieve value of that class property and not control's property value after Dialog is closed, and stop worrying about Dispose or not Dispose, or whatever else.
Hope this helps