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();
Related
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();
i want to call anything from mainform (mainform.cs) from GraficDisplay (namespace)
in the other (namespace) : GraphLib , but i can't , would any one tell me why? and how can i resolve this problem? Its been giving me hard time since the start of the project, every time I try these errors appear:
When I call:
mainform.toolstriplabel1.text = "87";
this appears:
The name 'mainform' does not exist in the current context
and if I call this:
GraficDisplay.MainForm.toolstriplabel1.text = "87";
this appears:
The name 'GraficDisplay' does not exist in the current context
I mean I even can't call the GraficDisplay (namespace) in GraphLib (namespace)
also the MainForm is public and partial.
I usually don't follow links here either but CodeProject is a rather reliable source imo, so I had a look.
Edit: I was confused as to what you want. Here is what you seem to actually wnat:
The problem is about referencing a form or part of it from another form or part of it. It is further a problem of dealing with a Library, that really shouldn't be messed up be adding namespaces of an example application or dependencies etc..
So what you want is loose coupling.
Here is a solution that uses references in the library objects and register methods to fill the references. If you don't register anything the library will work normally.
This solution can be changed and expanded but I'll leave it at registering two objects: One is a Control, eg.g a TextBox; the other is a Component e.g. a ToolStripItem. If you want to reference only the ToolStripItem you can omit the references to the Control and the RegisterCtl methods.
In hat case you can and should also substitute 'Component' for 'ToolStripItem' to make things thighter!
First you go to the ultimate consumer of the actions, PlotterGraphSelectCurvesForm. Here you add these two blocks of code:
public partial class PlotterGraphSelectCurvesForm : Form
{
private int selectetGraphIndex = -1;
private PlotterGraphPaneEx gpane = null;
// block one: a Control reference (if you like!):
Control myTextCtl;
public void RegisterCtl(Control ctl) { if (ctl != null) myTextCtl = ctl; }
// block one: a Component reference:
Component myTextComp;
public void RegisterComp(Component comp) { if (comp != null) myTextComp = comp; }
//..
Next you code what you want to happen, maybe like this:
void tb_GraphName_TextChanged(object sender, EventArgs e)
{
if (selectetGraphIndex >= 0 && selectetGraphIndex < gpane.Sources.Count)
{
DataSource src = gpane.Sources[selectetGraphIndex];
String Text = tb_GraphName.Text;
// all controls have a Text:
if (myTextCtl != null) myTextCtl.Text = Text;
// here you need to know the type:
if (myTextComp != null) ((ToolStripItem) myTextComp).Text = Text;
//..
}
In theory all you now need to do is to register the TextBox and/or the ToolStripItem in the Mainform.. However, there is a complication: The PlotterGraphSelectCurvesForm is not called from the Mainform! Instead it is called from a UserObject PlotterGraphPaneEx, which in turn is sitting in the MainForm. In the same sprit of not messing up the library by creating dependencies you simply add the very same references and registration methods to this UO as well:
public partial class PlotterDisplayEx : UserControl
{
#region MEMBERS
Control myTextCtl;
public void RegisterCtl(Control ctl) { if (ctl != null) myTextCtl = ctl; }
Component myTextComp;
public void RegisterComp(Component comp) { if (comp != null) myTextComp = comp; }
//..
Now you can actually register things, both in the MainForm..:
public MainForm()
{
InitializeComponent();
display.RegisterCtl(aDemoTextBox);
display.RegisterComp(toolstriplabel1);
//..
..and in the UO:
private void selectGraphsToolStripMenuItem_Click(object sender, EventArgs e)
{
if (GraphPropertiesForm == null)
{
GraphPropertiesForm = new PlotterGraphSelectCurvesForm();
GraphPropertiesForm.RegisterCtl(myTextCtl);
GraphPropertiesForm.RegisterComp(myTextComp);
}
//..
Now when you open the Properties form and change the LabelText you can see both the text in the Graphs and the text in both the Menu and the TextBox change..
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.
What is the proper way to change Form language at runtime?
Setting all controls manually using recursion like this
Save language choice to file > Restart Application > Load languge
choice before InitializeComponent();
Using Form constructor to replace instance of active from (if this is even possible)
Something else
There is so much half written threads about this but none provides real answer on what is proper way to do this?
UPDATE:
To clarify my question:
Doing something like this:
public Form1()
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de");
this.InitializeComponent();
}
works fine and all my controls and everything else in resources get translated correctly.
And doing something like:
private void button1_Click(object sender, EventArgs e)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en");
}
does nothing, Form stays in language I set up before InitializeComponent();
I believe the solution shown in Hans Passant's comment might be the only (general) solution.
Personally, I use this base class for all forms that need to be localized:
public class LocalizedForm : Form
{
/// <summary>
/// Occurs when current UI culture is changed
/// </summary>
[Browsable(true)]
[Description("Occurs when current UI culture is changed")]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[Category("Property Changed")]
public event EventHandler CultureChanged;
protected CultureInfo culture;
protected ComponentResourceManager resManager;
/// <summary>
/// Current culture of this form
/// </summary>
[Browsable(false)]
[Description("Current culture of this form")]
[EditorBrowsable(EditorBrowsableState.Never)]
public CultureInfo Culture
{
get { return this.culture; }
set
{
if (this.culture != value)
{
this.ApplyResources(this, value);
this.culture = value;
this.OnCultureChanged();
}
}
}
public LocalizedForm()
{
this.resManager = new ComponentResourceManager(this.GetType());
this.culture = CultureInfo.CurrentUICulture;
}
private void ApplyResources(Control parent, CultureInfo culture)
{
this.resManager.ApplyResources(parent, parent.Name, culture);
foreach (Control ctl in parent.Controls)
{
this.ApplyResources(ctl, culture);
}
}
protected void OnCultureChanged()
{
var temp = this.CultureChanged;
if (temp != null)
temp(this, EventArgs.Empty);
}
}
Then instead of directly changing Thread.CurrentThread.CurrentUICulture, I use this property in static manager class to change UI culture:
public static CultureInfo GlobalUICulture
{
get { return Thread.CurrentThread.CurrentUICulture; }
set
{
if (GlobalUICulture.Equals(value) == false)
{
foreach (var form in Application.OpenForms.OfType<LocalizedForm>())
{
form.Culture = value;
}
Thread.CurrentThread.CurrentUICulture = value;
}
}
}
I have found another way:
Move initialization form code in a private method like below:
private void FormInitialize() {/*Your code here*/}
In the form constructor use it like this:
public Form1()
{
InitializeComponent();
FormInitialize();
}
And from Button, menuItem or other call method like this:
private void ChangeCultureToFrench_Click(object sender, EventArgs e)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr");
this.Controls.Clear();
this.InitializeComponent();
FormInitialize();
}
I hope this helps ;-)
I've discovered this kind of approach a few minutes ago. Just quick and simple restart of the main form. Meybe it will help to someone. Event is created inside the form on my own, called when user selects the language from menu but after the selected culture's name is saved into the settings. Culture names are then loaded from that settings. Works exactly as I need and looks like proper solution.
static class Program
{
private static bool doNotExit = true;
private static FormMain fm;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
while(doNotExit)
{
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo(Properties.Settings.Default.language);//
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(Properties.Settings.Default.language);//
doNotExit = false;
fm = new FormMain();
fm.lanugageChangedEvent += new EventHandler(main_LanugageChangedEvent);
Application.Run(fm);
}
}
static void main_LanugageChangedEvent(object sender, EventArgs e)
{
doNotExit = true;
fm.Close();
}
}
In reference to your ColumnHeader .NET framework bug, I also discovered this bug recently and posted a question about it (to which I haven't received any responses). I was able to fix the problem by hardcoding the changes for the ColumnHeaders. For example:
resources.ApplyResources(_myHeader, "_myHeader", culture);
You basically just replace the call to .Name with a literal string of the name. I have tested this and it works. Unfortunately this means it won't fit as part of the code you use to change all of the controls. You will have to add a line for each ColumnHeader object you need to change. If you have a listview with a variable number of columns, that could get tricky. Another option is to create localized resource files. I assume you probably already have them for stuff like message box text and other strings. Then you can add an entry in your resource file like "columnHeader_myHeader" and set the appropriate language text for each language. Finally, you can manually change the text to your column headers by using:
_myHeader.Text = myResourceFileName.columnHeader_myHeader;
This will select the appropriate language based on the current thread culture.
Hans was correct in that there doesn't seem to be a foolproof "proper" way to perform localization in .NET, though there are a variety of tools you can use. For something like a job application, even though it is probably already too late for this advice, my suggestion would be to learn as many different methods as you can for localization, learn the pros and cons, and then just pick a system and be able to argue why you believe it is the "proper" choice. They are probably more concerned with your logic and reasoning and demonstration of prior experience than they are with the actual method.
Hope this would help anyone, I found it best for me cause i needed to change buttons location according the lang (browse on the right or left of the search box and labels next to text fields).
save a public var on the main that will hold the lang.
created a class which handles the visual part
created xml files that will hold any language data and more (in my xml tag name=object name).
sent that class's constructor the form (to save and work with)
connect to that current xml file
From main form call whenever you want to initialView (part of the view class) and change lang (and more) anytime (just connect to the right xml file):
public void initialView()
{
//Set rightToLeft values
initialIndent(mainForm);
//set visual text values
initialTxt();
}
private void initialTxt()
{
// Are there any more controls under mainObj (Form1)?
Boolean endOfElemsUnderPnl = false;
// The current Control im working on
Object curObj = mainForm;
do
{
// MenuStrip needs to be handled separately
if (typeof(MenuStrip).ToString().Equals(curObj.GetType().ToString()))
{
foreach (ToolStripMenuItem miBase in ((MenuStrip)(curObj)).Items)
{
miBase.Text = mainForm.dbCon.getData(miBase.Name.ToString());
foreach (ToolStripMenuItem miInnerNode in miBase.DropDownItems)
{
miInnerNode.Text = mainForm.dbCon.getData(miInnerNode.Name.ToString());
}
}
}
// Any other Control i have on the form
else
{
((Control)(curObj)).Text = mainForm.dbCon.getData(((Control)(curObj)).Name.ToString());
}
curObj = mainForm.GetNextControl(((Control)(curObj)), true);
// Are there any more controls under mainObj?
if (null == curObj)
{
endOfElemsUnderPnl = true;
}
} while (!endOfElemsUnderPnl);
}
private void initialIndent(frmMyFileManager parent)
{
if (parent.Language.Equals("Hebrew"))
{
parent.RightToLeft = RightToLeft.Yes;
}
else if (parent.Language.Equals("English"))
{
parent.RightToLeft = RightToLeft.No;
}
else
{
parent.RightToLeft = RightToLeft.No;
}
}
And this is an example of how easy it is for my at runtime:
private void selectLanguageToolStripMenuItem_Click(object sender, EventArgs e)
{
DialogResult res = MessageBox.Show(this, "click yes for english and no for hebrew", "Select language", MessageBoxButtons.YesNoCancel);
if (DialogResult.Yes == res)
{
Language = "English";
}
else if (DialogResult.No == res)
{
Language = "Hebrew";
}
dbCon = new CDBConnector("****\\lang" + Language + ".xml");
view.initialView();
}
I have a main form that has most of the functionality in it. I was just wondering how would I pass on a variable from say a pop up form, to that main form.
Like for instance:
I have a main form that needs some connection info. So when you click the button "Enter Connection Info", it opens up a new form that the user can type in the IP Address he wants to use for his connection.
On this new form, I have a textbox and a button and once you enter the information it should close and pass on the string that contains the ip back to the original form.
Any suggestions? Do you think there is a better method of accomplishing this than using a windows form, and just going ahead and using a windows form or something? I'm quite perplexed on this issue at the moment.
Expose the textbox text as a public read only property. Show the connection form as a dialog and when it closes, get the connection from the property and then dispose the form:
in open form handler (button click, menu, whatever)
string connectionString = null;
using (ConnectionForm form = new ConnectionForm())
{
DialogResult result = form.ShowDialog();
if (result == DialogResult.Ok)
connectionString = form.ConnectionString
}
In you connection form:
public class ConnectionForm: Form
{
....
public string ConnectionString { get { return textBox1.Text; } }
}
You can create a public property in your main form and pass main form instance in pop-up constructor. In this way you can change the main form property.
You can also create an event in your pop-up form and hook it in your main form.
I like to use a pattern sort of like this (bear with me, c# is not my first language):
public class ValueForm: Form
{
public static string GetFromUser(string originalValue)
{
using (ConnectionForm form = new ConnectionForm())
{
form.TheValue = originalValue;
var result = form.ShowDialog();
if (result == DialogResult.Ok)
return form.TheValue;
else
return originalValue;
}
}
public string TheValue {
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
/* also some code behind your OK & cancel buttons to set
DialogResult appropriately,
and do any validation that you need to do
*/
}
and then you would use this like:
string newValue = ValueForm.GetFromUser(oldValue);
Reference Bind the controls on the dialog Form to properties of the Parent Form.
public dlgDbConnProps ( Form Owner )
{
// TODO: Complete member initialization
InitializeComponent ( );
owner = Owner;
}
private void cbo_ProviderLst_SelectedIndexChanged ( object sender, EventArgs e )
{
owner.Provider = cboLst.Text;
}
Here is another method that I have implemented:
... pass a Func to the child form constructor:
public dlgRequestLogin ( Func<string, string, bool> LoginMethod )
{
InitializeComponent ( );
p_loginMethod = LoginMethod;
}
... then handle on button click (or other appropriate event):
private void cmd_SendLoginCredentials_Click ( object sender, EventArgs e )
{
bool res = p_loginMethod.Invoke ( txt_UserID.Text, txt_UserPassword.Text );
}