C# MDI child not showing - c#

I am using a method sice I will have alot of child forms. Now it does show when I call a form by clicking on a button, but not after the login child is closed.
Method:
private void mForms(Form f)
{
if (this.MdiChildren.Contains(f))
{
f.WindowState = FormWindowState.Normal;
}
else
{
f.MdiParent = this; f.Show();
}
}
This just simply checks if the MDIcontainer already contains the requested form. If yes, put it back up again (in case it's minimized), if not, show it.
Now I can call the forms using this:
private void ts_bestand_studenten_add_Click(object sender, EventArgs e)
{
if (add_student.IsDisposed)
{
add_student = new add_student();
}
mForms(add_student);
}
This checks if it is disposed already or not. If so, redefine it. Then it calls to the method to open the right form. This works as it should.
Now the problematic part:
After the login screen is closed and user is logged in, the userlevel is defined. I should be able to open another form. This is the method I use for that: (NOTE: the userlevels work fine since it does reach the Messagebox)
// Predefines start_screen_admin
Form start_screen_admin = new start_screen_admin();
public void mCommitRights()
{
if (userlevel.gCheckLevel == 0)
{
// Admin, no changes
MessageBox.Show("Admin");
mForms(start_screen_admin);
}
... more of the same to check for userlevels
}
Now you think this should work. I don't have to redefine it because it's the first time it opens and it is already predefined. The MessageBox shows, but the form does not. I really can't see a problem in this. I tried everything I could think of..

I think you need to include the Select() method to bring the form to the front:
Try changing it to this:
private void mForms(Form f) {
if (this.MdiChildren.Contains(f)) {
f.WindowState = FormWindowState.Normal;
f.Select();
} else {
f.MdiParent = this;
f.Show();
}
}

Related

Updating Toolstripstatuslabel in Thread of closing form

I have a Order form. Once a order is complete, I use a thread to email the order to the supplier. The Thread is use to prevent the system hanging while the order is exported to pdf and sent.
The Problem: I would like to place an message on the MDIParent Toolstripstatuslabel once the threat completes without error to confirm the order was sent. But I get an error: "System.NullReferenceException: Object reference not set to an instance of an object". Which I could be wrong, refers to the Child windows disposed the toolstripstatuslabel reference on the parent form when it closed, so the threat cant access it anymore. I know the easy solution would be to use a MessageBox to confirm all went good and well...but why make it easy if you can do it elegant?
So my question: How can I reference a control in the parent form from the threat? I tried looking at invoke, but not sure how to implement it or if it is actually the correct direction.
EDIT:
My code from the childform
public partial class frm_n_order : Form
{
.
.
private void bProcess_Click(object sender, EventArgs e)
{
.
.
.
new Thread(new ThreadStart(delegate
{
fExportOrder(strOrderNo);
fSendMailv2(strPlant, strSupCode, strOrderNo);
})).Start();
this.close();
}
private void fExportOrder(string strOrderNo)
{
//export order to pdf
}
private void fSendMailv2(string strPlant, string strSupCode, string strOrderNo);
{
// get pdf
// get email address
try
{
// send email
((MDIParent1)MdiParent).tsslMain.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails; //here I need to find a different way of accessing the Toolstripstatuslabel in the parent form
}
catch
{
MessageBox.Show("Email did not send");
}
}
}
EDIT:
Ok, so after spending more than a day trying to figure out how to use Invoke, I realize while it seems like good practice when working with threads, its not my answer. My problem is directly related to the childform closing disposing all the controls so it looses its reference to the MdiParent. To solve the problem I did the following:
In my child class I added:
public static Form IsFormAlreadyOpen(Type FormType)
{
foreach (Form OpenForm in Application.OpenForms)
{
if (OpenForm.GetType() == FormType)
return OpenForm;
}
return null;
}
I dont think it is the most elegant solution but the theory is that my Parent form will always be open when I need to access the Toolstripstatuslabel. So I basically loop through all the open forms to find the reference to the active MdiParent instance which then gets passed back to the caller. In the thread I then use the following code.
MDIParent1 fm = null;
if ((fm = (MDIParent1)IsFormAlreadyOpen(typeof(MDIParent1))) != null)
{
fm.Toolstripstatuslabel1.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails;
}
I'm still looking for a better approach, but for now this works.
Ok, so after spending more than a day trying to figure out how to use Invoke, I realize while it seems like good practice when working with threads, its not my answer. My problem is directly related to the childform closing disposing all the controls so it looses its reference to the MdiParent. To solve the problem I did the following:
In my child class I added:
public static Form IsFormAlreadyOpen(Type FormType)
{
foreach (Form OpenForm in Application.OpenForms)
{
if (OpenForm.GetType() == FormType)
return OpenForm;
}
return null;
}
I dont think it is the most elegant solution but the theory is that my Parent form will always be open when I need to access the Toolstripstatuslabel. So I basically loop through all the open forms to find the reference to the active MdiParent instance which then gets passed back to the caller. In the thread I then use the following code.
MDIParent1 fm = null;
if ((fm = (MDIParent1)IsFormAlreadyOpen(typeof(MDIParent1))) != null)
{
fm.Toolstripstatuslabel1.Text = "Order No:" + strOrderNo + " was successfully send to " + strEmails;
}
I'm still looking for a better approach, but for now this works.
It's hard for me to overlook someone saying "but why make it easy if you can do it elegant?"
Awesome!
I figure if we can do something elegantly, then in the future it should be easy.... right?
Anyways, hoping you find the below helpful.
A note: It looked to me like you were declaring your thread as a local variable and not storing it outside the local scope. If we want something to live past the end of the scope, it's a good idea to store a reference to it (which is done using a private Task field in the below example).
Sure, the thread would get added to the threadpool and stored somewhere in the framework even if it's just a local variable, so I think it wouldn't abort due to garbage collection as you have it, but I don't like the idea of instances floating around that I don't have references to.
public class MyChildForm : Form
{
private Task longRunningTask;
private Task closeTask;
public string ResultOfTimeConsumingOperation { get; private set; }
protected override Dispose(bool disposing)
{
if (disposing)
{
longRunningTask?.Dispose();
closeTask?.Dispose();
}
base.Dispose(disposing);
}
private void TimeConsumingOperation1()
{
Thread.Sleep(TimeSpan.FromSeconds(8));
ResultOfTimeConsumingOperation = "Hooray we finished the work lol";
this.closeTask =
Task.Factory.FromAsync(
BeginInvoke(new Action(Close)),
EndInvoke);
}
protected override void OnLoad()
{
base.OnLoad();
this.longRunningTask =
Task.Run(TimeConsumingOperation1);
}
}
public class MyParentForm : Form
{
private List<Form> childForms;
public MyParentForm() : base()
{
childForms = new List<Form>();
}
protected override void OnLoad()
{
base.OnLoad();
RunChildForm();
}
private void RunChildForm()
{
var childForm = new MyChildForm();
childForms = childForms.Append(childForm).ToList();
childForm.FormClosing += ChidlForm_FormClosing;
childForm.Show();
}
private void ChildForm_FormClosing(object sender, FormClosingEventArgs e)
{
var childForm = sender as MyChildForm;
childForm.FormClosing -= ChildForm_FormClosing;
if (childForms.Contains(childForm))
childForms =
childForms.
Except(new Form[] { childForm }).
ToList();
// tada
myStatusLabel.Text = childForm.ResultOfLongRunningProcess;
}
// main window is closing
protected override void OnFormClosing(FormClosingEventArgs e)
{
// let's close any windows we left open
var localForms = childForms.ToList();
childForms = new List<Form>();
foreach (var form in localForms)
form.Close();
}
}

Disallowing interaction with background form

On my application's first run, two forms open. The topmost form needs to take priority, and disallow any interaction with the form in the background. I have tried ShowDialog() as referenced here, however this hides the form in the background which I do not wish to do. Is there a method of accomplishing this?
public Form1()
{
InitializeComponent();
if (!fileexists(#"c:\Management Tools\Absence Tracker\bin\data\tbase.skf"))
{ firstrunactions(); }
}
void firstrunactions()
{
//open the get-started form and invite user to populate serialisable objects
firstrun frwindow = new firstrun();
frwindow.ShowDialog();
}
When you are using .ShowDialog() the execution of the containing method is paused until you close the newly opened window. So make sure to do everthing else before you call .ShowDialog(). Otherwise your program gets stuck in this method. If you are calling .ShowDialog() before the background window is shown will cause problems.
But using .ShowDialog() here is totally correct and has the right functionality.
Example how not to do it (causes the same behavior like in your problem):
public Form1()
{
InitializeComponent();
//this is the wrong place for showing a child window because it "hides" its parent
Form frwindow = new Form();
frwindow.ShowDialog(this);
}
The magical place where it works:
private void Form1_Shown(object sender, EventArgs e)
{
Form frwindow = new Form();
frwindow.ShowDialog(this);
}
Edit: In your case it is enough moving if(!fileexistst...) into the Form1_Shown()-event.
Try with frwindow.ShowDialog(this);
Or instead "this" pass the other form as parameter.
Also move this part if (!fileexists(#"c:\Management Tools\Absence Tracker\bin\data\tbase.skf"))
{ firstrunactions(); }
}
in OnLoad override.

Add a default user control everytime any other user control is closed in a panel

I have been trying to load a default user control every time any other user control on the same panel is closed by the user. I have a panel named MainContainer and when the main form loads I am calling the following method to load that default user control named welcome.
public void AddUserControlWelcome()
{
MainContainer.Controls.Clear();
welcome.Dock = DockStyle.Fill;
MainContainer.Controls.Add(welcome);
}
I have a menustrip button which calls the following method,
private void sellItemsToolStripMenuItem_Click(object sender, EventArgs e)
{
AddUserControlSellManager();
}
And it is defined as,
public void AddUserControlSellManager()
{
MainContainer.Controls.Clear();
sellManager.Dock = DockStyle.Fill;
MainContainer.Controls.Add(sellManager);
}
So, there is a button on sellManager user control which actually closes sellManager. And after that I am invoking AddUserControlWelcome() again from MainContainer_ControlRemoved(object sender, ControlEventArgs e) but the application is crashing and I don't know why.
I think, it is clear why you having this issue. MainContainer_ControlRemoved called not only when you remove your "sell" but "welcome" too. So, the culprit I believe is the fact that you do add control on such event as MainContainer_ControlRemoved, which you shouldn't do. As good as .Net is, sometimes you have to stay away from using certain events for certain purposes , or you run into issues.
Try to do something like this. Considering that your surface can host only one control at the time
class SurfaceManager
{
private Control _defaultCtrl;
private bool _currentDefault;
private Control _surface;
void SurfaceManager(Control _surface, Control defaultCtrl)
{
_surface = surface;
_defaultCtrl = defaultCtrl;
_surface = surface.Controls.Add(_defaultCtrl);
_currentDefault = true;
}
public Control Add(Control ctrl)
{
Control c = null; // Returning removed control so you can do something else with it
if (_surface.Controls.Count > 0)
{
if (!_currentDefault)
c = _surface.Controls[0];
_surface.Controls.Clear();
}
_surface = surface.Controls.Add(ctrl);
_currentDefault = false;
Return c;
}
public Control Remove()
{
if (_currentDefault) Return // Current is default - do nothing
Control c = null; // Returning removed control so you can do something else with it
if (_surface.Controls.Count > 0)
{
c = _surface.Controls[0];
_surface.Controls.Clear();
}
_surface = surface.Controls.Add(_defaultCtrl);
_currentDefault = true;
Return c;
}
}
Now, in your class create instance of this manager and use Add or Remove. Remove will automatically bring on the Welcome screen

Reset/Reload current form

I've been trying to reset my current form to it's original state by closing it, and opening a new one. I want the form objects to be reset,the variables to be re-declared, the class objects to be cleared etc I've got everything working but the class being cleared, no matter what I do it won't create a new one with blank data.
Here is my code:
if (btnRandom.Text == "Reset")
{
SetupScreen form = new SetupScreen();
form.Show();
this.Dispose();
//Create new class for form / or launch load events as normal
form.Mybattleship = new battleship()
form.SetupScreen_Load(this, null);
}
I've tried many methods over the internet and none have worked.. even the overly complicated ones..
Oh I forgot to mention I need the new form to act as if it's just been loaded as normal, so the load events etc trigger
You would be better off making a method that you can call that will set default values for items that you can use when opening form and resetting...
public SetupScreen()
{
InitializeComponent();
SetDefaultValues();
}
private void SetDefaultValues()
{
//start values..
}
public void ResetBtn_Click(object sender, EventArgs e)
{
SetDefaultValues();
}

C# WinForms cannot set focus

Im currently facing the problem that when i try to set focus on some control (textBox), nothing happens, maybe i just overlooked something.(somewhere i found that focus is "low-level" method and that select() should be used instead, however, it doesnt work as well)
From form Login, i launch new instance of EncryptPSW form
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
EncryptPSW ePSW = new EncryptPSW();
ePSW.setOsLog(false, this);
ePSW.ShowDialog();
}
On Button(which is located on EncryptPSW form ) click event i call fill method
public void fill()
{
if (textBoxPSW.Text.Length == 8)//psw has to be 8 chars long
{
if (save)//determinating whether save or fetch of data should be done
{ login.launchSave(textBoxPSW.Text,this); }
else { login.launchOpen(textBoxPSW.Text,this); }
}
else { MessageBox.Show("The password must contain 8 characters");}
}
Which launches either save or open method from Login (my problem is just with open, since during save i dont need to do anything with Focus)
public void launchOpen(string psw,EncryptPSW ePSW)
{
ePSW.Close();
Encryptor.DecryptFile("loggin.bin", psw, this); //decrypting data and setting textBoxes Text property into the fetched ones
setFocus();
}
After all the work is done, setFocus() should be called in order to set focus and other properties.
public void setFocus()
{
textBoxDatabase.Focus();
textBoxDatabase.SelectionStart = textBoxDatabase.TextLength - 1;
textBoxDatabase.SelectionLength = 0;
}
I tried so many different ways, like:
Calling setFocus() from within EncryptPSW_FormClosed
Calling whole open process after the EncryptPSW is closed (from within EncryptPSW_FormClosed)
and many more, however i dont remember it all.
In the case of Form_Closed the weird thing is, that when i tried to show a message box from there instead of setting focus (just to see where the problem might be), it's showed before the EncryptPSW form is closed.
My only guess about this is that the instance of EncryptPSW is somehow blocking Login form and it's controls
I hoped i described my problem well enough and that it makes at least a bit of sense ;]
Thanks in advance,
Regards,
Releis
Since the textbox is in the login form, and you are opening the EcryptPWS from it as a dialog (child), your login form will not be able to set focus to anything. You will need to set focus after it is closed. You can do this:
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
using(EncryptPSW ePSW = new EncryptPSW())
{
ePSW.setOsLog(false, this);
if (ePSW.ShowDialog() == DialogResult.OK)
{
textBoxDatabase.Focus();
}
}
}
public void launchOpen(string psw,EncryptPSW ePSW)
{
ePSW.DialogResult = DialogResult.OK;
ePSW.Close();
Encryptor.DecryptFile("loggin.bin", psw, this); //decrypting data and setting textBoxes Text property into the fetched ones
}
OK this maybe the ugliest thing I saw round this but.
using
public void setFocus()
{
textBoxDatabase.Focus();
textBoxDatabase.SelectionStart = textBoxDatabase.TextLength - 1;
textBoxDatabase.SelectionLength = 0;
}
Change your code at
public void launchOpen(string psw,EncryptPSW ePSW)
{
ePSW.Close();
Encryptor.DecryptFile("loggin.bin", psw, this); //decrypting data and setting textBoxes Text property into the fetched ones
setFocus();
}
to
delegate void settingfocus();
public void launchOpen(string psw,EncryptPSW ePSW)
{
ePSW.Close();
Encryptor.DecryptFile("loggin.bin", psw, this); //decrypting data and setting textBoxes Text property into the fetched ones
settingfocus sf = new settingfocus(setFocus);
this.BeginInvoke(sf);
}
This worked for me
(Sorry for apparently thinking insert "this" before procedure, and change line x to this was legable)

Categories