Static member inside the form - c#

In my project on the WindowsForms, if I have a static instance inside the form, when I'm opening my form at the first time, it works. But if I'll close it and open again, the form will be empty. Why can it be?
public partial class Computer : Form
{
static Indicators indicators = new Code.Indicators();
}
P.S. I'm making it static, because I want to save it's value after the form will be closed.
Edit 1: Opening the form
private void button3_Click(object sender, EventArgs e)
{
Computer computer = new Computer();
computer.ShowDialog();
}
Edit 2: Computer Form
namespace WF
{
public partial class Computer : Form
{
static Code.Indicators indicators = new Code.Indicators();
public Computer()
{
if (indicators.isComputerAlreadyRunning == false)
{
InitializeComponent();
pictureBox1.Image = Properties.Resources.Computer1;
indicators.isComputerAlreadyRunning = true;
}
}
// My not successful try to save the value of the variable
public Code.Indicators ShowForm()
{
return new Code.Indicators(indicators.isComputerAlreadyRunning);
}
}
}

I don't think that static members work well with the Windows Form lifecycle.
I suggest you make Indicators a normal instance member of your form. To preserve state beyond the life of a form you can copy your state from the form and copy it back to the form when you open it.
// Keep this in the proper place
var indicators = new Code.Indicators();
...
// Copy back and forth for the life time of the form
using (var form = new Computer())
{
form.Indicators.AddRange(indicators);
form.Close += (s, e) =>
{
indicators.Clear();
indicators.AddRange(form.Indicators);
}
}
...

According to the constructor in the Computer class, the indicators.isComputerAlreadyRunning is set to true the first time the form is created.
So when Computer is created the second time, the if condition will fail and the whole if block will be skipped. That means your InitializeComponent(); won't get run and hence nothing in the form will shows up.
Put the InitializeComponent(); outside the if clause to make it work.

Related

Closing of all Previous Forms and Opening of New Form in C#

How to Close background Forms whenever a new form is opened in Windows Forms C#?
It should not be the specific form to be closed.
It should close all the background forms whenever a form is opened.
Two approaches.
First is using Application.OpenForms like this:
foreach (Form form in Application.OpenForms)
{
if(Form is YourMainFormClassName) //Check if current form is your main form and do not close it since your app would close. You can use .Hide() if you want
return;
form.Close();
}
Other approach is using List but you cannot do List<Form> because when removing you will have problem if you want to remove specific form and you go like yourList.Remove(this) it will remove all items with class of that form. Of course it will happen only if you open one form multiple times but to avoid that we will use Form.Tag property.
An Object that contains data about the control. The default is null
So we will use it to store our Id of the form.
So now when we prepared system let's write it:
First we need List<Form> that is accessible from all classes so we will create it like public static property.
public static class Settings //Class is also static
{
public static List<Form> OpenedForms = new List<Form>();
public static int MaxIdOfOpenedForm() //With this method we check max ID of opened form. We will use it later
{
int max = -1;
foreach(Form f in OpenedForms)
{
if(Convert.ToInt32(f.Tag) > max)
max = Convert.ToInt32(f.Tag);
}
return max;
}
public static void RemoveSpecificForm(Form form) //Remove specific form from list
{
for(int i = 0; i < OpenedForms.Count; i++)
{
if((OpenedForms[i] as Form).Tag == form.Tag)
{
OpenedForms.Remove(form);
return;
}
}
}
public static void CloseAllOpenedForms()
{
for(int i = 0; i < OpenedForms.Count; i++)
{
OpenedForms.Remove(OpenedForms[i]);
}
}
}
Now we have list but need to populate it every time we open new form so we will do it like this:
public partial class YourForm
{
public YourForm()
{
InitializeComponents();
this.Tag = Settings.MaxIdOfOpenedForm() + 1; //We are setting Tag of newly opened form
Settings.OpenedForms.Add(this); //Adding new form to opened forms.
}
}
And when we close form we need to remove form from list:
private void YourFormClosed(object sender, EventArgs e)
{
RemoveSpecificForm(this);
}
and when we set this up just call CloseAllOpenedForms().
This method could have some improvements in performance but this is basic and you expand it further.
well as only one form can be active and in the foreground, so when openening a new form you can close the previous one:
In your main form:
Form previous_form = null;
and when creating any form:
if( previous_form != null)
previous_form.Close();
SomeForm someform = new SomeForm();
previsous_form = some_form;
someform.Show();

"Invoke or BeginInvoke cannot be called on a control until" on a Label

I've read several questions/answers related to this problem but couldn't find a solution applicable to problem.
I have a form (MainForm) and a button (Upload) on it. When I click on the button (after selecting a file from a ComboBox to be uploaded to the server), it opens another form (UploadBackupForm) and uploads a file to the server. The upload process is controlled in UploadBackupForm and the form looks like this:
This works as long as upload is done once, I mean, the UploadBackupForm is called one time. The second time I click on the Upload button, UploadBackupForm opens and (after uploading some data) it throws an error saying:
System.InvalidOperationException: 'Invoke or BeginInvoke cannot be called on a control until the window handle has been created.'
at this specific line(s):
DurationLabel.Invoke((MethodInvoker)delegate
{
DurationLabel.Text = Count2Duration(count);
});
This has puzzled me because it works when it's done once, and doesn't work at the second time. I have basic knowdledge in C#, so I don't know what's causing this and how to solve it.
MainForm:
private void Upload2ServerButton_OnClick(object sender, EventArgs e)
{
Form UBF = new UploadBackupForm();
UBF.ShowDialog();
}
UploadBackupForm:
public partial class UploadBackupForm : Form
{
public UploadBackupForm()
{
InitializeComponent();
}
public static System.Timers.Timer timer = new System.Timers.Timer();
public static int count = 0;
private void UploadBackup_Load(object sender, EventArgs e)
{
timer.Interval = 1000;
timer.Elapsed += new ElapsedEventHandler(delegate {
count++;
// didn't do any good (this.IsHandleCreated or DurationLabel.IsHandleCreated)
// if (!this.IsHandleCreated)
// {
// this.CreateControl();
// }
DurationLabel.Invoke((MethodInvoker)delegate
{
DurationLabel.Text = Count2Duration(count);
});
});
// upload the archive to the server
new Thread((ThreadStart)delegate
{
FTP.Item[] items = FTP.ListDirectoryDetails(DataIO.FTP.Server, DataIO.FTP.Username, DataIO.FTP.Password, DataIO.FTP.UploadDir);
// here, I upload the file to the server and update the progress bar and the uploaded / total labels
Because the timer variable is static it remains even after the form is closed. It contains a reference to a delegate which holds a reference to the form so the previous instances are kept alive through the lifetime of your application. Also, the single timer posts callbacks to all previous instances along with the current one.
As correctly noted in the comments by Evk, make the timer and count non-static so they are dedicated to each instance of the form.

Why do these two List<string> always contain the same number of items?

I have a very simple program that for some reason has me stumped. I put it down, came back at it again this morning and I'm still stumped. First off, I'm aware this is not an ideal solution. I have two forms: Main and Log. The Main form has a button that adds to List _debugLog when clicked. When btnDebug is clicked, it opens the Log form, passing _debugLog to it. Everything is fine, the timer is setup and runs, everything is normal. The event log.UpdateLog() is triggered every 2.5 seconds to update the Log form with the updated log. However, mainFormLog.Count and _log.Count are always the same and they BOTH increase when btnAdd is clicked on the main form. How does _log have the new _debugLog (mainFormLog) from the tick event?
namespace Tool
{
public partial class Main : Form
{
private List<string> _debugLog = new List<string>();
public Main()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, EventArgs e)
{
_debugLog.Add("message!");
}
private void btnDebug_Click(object sender, EventArgs e)
{
Log log = new Log(_debugLog);
log.Show();
Timer dt = new Timer();
dt.Interval = 2500;
dt.Enabled = true;
dt.Tick += delegate {
log.UpdateLog(_debugLog);
};
}
}
public partial class Log : Form
{
private List<string> _log;
public Log(List<string> log)
{
InitializeComponent();
_log = log;
}
public void UpdateLog(List<string> mainFormLog)
{
if (mainFormLog.Count > _log.Count)
{
MessageBox.Show("Log has been updated!");
}
else
{
MessageBox.Show("Nothing new!" + mainFormLog.Count.ToString() + " / " + _log.Count.ToString());
}
}
}
}
Well, you're passing the reference to the list from Main to Log, so it's actually the same list.
If you want a separate list that gets initialized with the list from Main you can use:
public Log(List<string> log)
{
InitializeComponent();
_log = new List<string>(log);
}
Maybe this helps to understand the difference between variables and references:
For a value type, the value is the information itself. For a reference
type, the value is a reference which may be null or may be a way of
navigating to an object containing the information.
For example, think of a variable as like a piece of paper. It could
have the value "5" or "false" written on it, but it couldn't have my
house... it would have to have directions to my house. Those
directions are the equivalent of a reference. In particular, two
people could have different pieces of paper containing the same
directions to my house - and if one person followed those directions
and painted my house red, then the second person would see that change
too. If they both just had separate pictures of my house on the paper,
then one person colouring their paper wouldn't change the other
person's paper at all.
All your variables _debugLog, mainFormLog, and _log are pointing to the same list in memory. You've only created one list, and when you assign a new variable to that list, it's just a pointer to some location in memory, it doesn't automatically create a new copy of the list.

Have only one instance of a subform open at a time c#

I'm writing an application that uses a wizard-like series of 5 simple forms. The first form, NewProfile, is opened from a menu item on the main application, MainForm, so is a subform of MainForm. The second form, TwoProfile, is opened from a button on NewProfile. The third form, ThreeProfile is opened from a button on TwoProfile, and so on for all 5 forms. Here is the sequence:
MainForm --> NewProfile <--> TwoProfile <--> ThreeProfile <--> FourProfile <--> FiveProfile. My problem is that when any form (NewProfile, TwoProfile, ThreeProfile, FourProfile or FiveProfile) is open, I don't want a user to be able to create an instance of NewProfile.
I started out by implementing a Singleton pattern, which half-way works. It works if NewProfile is open and I go to MainForm and try to create another instance of NewProfile. It does not work if NewProfile has been destroyed, by advancing to the next form and one of TwoProfile, ThreeProfile, FourProfile or FiveProfile is open. It tells me that NewProfile.IsDisposed is true, giving me a bad reference to the Singleton instance.
What I can't figure out is how to do my logic so that NewProfile won't be created if one of TwoProfile, ThreeProfile, FourProfile or FiveProfile is open or if NewProfile itself is open.
I hope this made sense. I don't really have much code to post, except what I did for my Singleton.
private static NewProfile _instance = null;
public static NewProfile Instance
{
get
{
if (_instance == null)
{
_instance = new NewProfile();
}
return _instance
}
}
Thank you :)
As suggested in the comments, each "form" could actually be a usercontrol you swap. That way, you only have one form and multiple pages. Alternatively, you can hide the form.
If you want multiple forms instead, then you could loop through all the open forms and see if the the ones you want to check are open. If not, you can open NewProfile.
bool shouldOpenNewDialog = true;
foreach (Form f in Application.OpenForms)
{
//give each dialog a Tag value of "opened" (or whatever name)
if (f.Tag.ToString() == "opened")
shouldOpenNewDialog = false;
}
if(shouldOpenNewDialog)
np = new NewProfile();
It's untested, but it should loop through all open forms and look for any that has a Tag saying opened. If it comes across one, then it set the shouldOpenNewDialog flag to false and NewProfile won't be called.
The way that we handle this is to have a static window manager class that keeps track of the open form instances. When the user performs an action that would cause a new window to open, we first check the window manager to see if the form is already open. If it is, we set focus on it rather than creating a new instance.
Every form that is opened inherits from a base form implementation that automatically registers itself with the window manager when it is opened and removes its registration when it is closed.
Here is a rough outline of the WindowManager class:
public class WindowManager
{
private static Dictionary<string, Form> m_cOpenForms = new Dictionary<string, Form>();
public static Form GetOpenForm(string sKey)
{
if (m_cOpenForms.ContainsKey(sKey))
{
return m_cOpenForms[sKey];
}
else
{
return null;
}
}
public static void RegisterForm(Form oForm)
{
m_cOpenForms.Add(GetFormKey(oForm), oForm);
oForm.FormClosed += FormClosed;
}
private static void FormClosed(object sender, FormClosedEventArgs e)
{
Form oForm = (Form)sender;
oForm.FormClosed -= FormClosed;
m_cOpenForms.Remove(GetFormKey(oForm);
}
private static string GetFormKey(Form oForm)
{
return oForm.Name;
}
}
And you can use it as follows:
Form oForm = WindowManager.GetOpenForm("Form1");
if (oForm != null)
{
oForm.Focus();
oForm.BringToFront();
}
else
{
oForm = new Form1();
WindowManager.RegisterForm(oForm);
// Open the form, etc
}

C# - using ColorDialog across forms

I have a windows form application. On the main form a user will enter a number of item, etc and then click a button which will open a new form (either a small form or a large form depending on a checkbox). Now on my main application I have a file menu - under which is settings - change background colour. This opens the colordialog. If a user does not pick anything the background colours will stay default. However if they change it on the main entry form i change the background of a few textboxes - code below.
private void warning1ToolStripMenuItem_Click(object sender, EventArgs e)
{
colorDialog1.ShowDialog();
Warn1Color = colorDialog1.Color.ToString();
if (Warn1Color != null)
{
tbWarn1Hrs.BackColor = colorDialog1.Color;
tbWarn1Mins.BackColor = colorDialog1.Color;
tbWarn1Secs.BackColor = colorDialog1.Color;
tbWarn1Msg.BackColor = colorDialog1.Color;
}
}
Now my problem is how to I get this to then change the background in the other form that opens. I was hoping I could pass the string across in the new form constructor as i do with a number of other values.
i.e - here is my code in the new form....(note - string Warn1Color was passed across in constructor and then made = to the string _Warn1Color. If it is null then background will be default yellow but it cant convert type string to system.drawing.color. Does anyone see an easy solution to this or what I could do to get this working easily.
if (_Warn1Color == null)
{
this.BackColor = System.Drawing.Color.Yellow;
}
else
this.BackColor = _Warn1Color;
Pass the Color on via the Constructor not a string. If this is not possibly for whatever reason, you could create a ColorConfigClass that holds the required Color and you can set it when used.
You should create a static class to store your configuration data such as this colour style. You can then set this value once you have prompted the user for the change and you can also call the Color value from any other form when you need to use it.
Your static class should look something like this...
public static class StyleSettings{
private static Color _warn1Color = Color.FromArgb(255, 0, 0);//default colour
public static Color Warn1Color {
get { return _warn1Color; }
set { _warn1Color = value; }
}
}
Then you can use this in your example method like...
private void warning1ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (colorDialog1.ShowDialog() == DialogResult.OK)
{
StyleSettings.Warn1Color = colorDialog1.Color;
tbWarn1Hrs.BackColor = StyleSettings.Warn1Color;
tbWarn1Mins.BackColor = StyleSettings.Warn1Color;
tbWarn1Secs.BackColor = StyleSettings.Warn1Color;
tbWarn1Msg.BackColor = StyleSettings.Warn1Color;
}
}
I assume you used a string because you wanted to be able to pass null, and System.Drawing.Color being a struct can not be null.
In which case either use Nullable ( http://msdn.microsoft.com/en-us/library/b3h38hb0%28v=vs.80%29.aspx ) which can be null or you can consider some other value as "default", say alpha=0.
To pass a value in your constructor simply go to the code file for the form (the one where you code the stuff for the events) and find the constructor function (has the same name as the form) e.g.:
namespace MyApp
{
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
...
And add the parameters to it:
namespace MyApp
{
public partial class MyForm : Form
{
public MyForm(System.drawing.color background)
{
InitializeComponent();
...do whatever you want with background...
}
...
Of course you also need to edit the places you create this form, e.g. change
form = new MyForm();
form.Show();
to
form = new MyForm(backgroundColour);
form.Show();

Categories

Resources