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..
Related
Wierd behaviour when passing values to and from second form.
ParameterForm pf = new ParameterForm(testString);
works
ParameterForm pf = new ParameterForm();
pf.testString="test";
doesn't (testString defined as public string)
maybe i'm missing something? Anyway I'd like to make 2nd variant work properly, as for now - it returns null object reference error.
Thanks for help.
Posting more code here:
calling
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(doc.GetElementById(ParametersButton.Tag.ToString()));
pf.ShowDialog(this);
pf.test = "test";
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
};
definition and use
public partial class ParameterForm : Form
{
public string test;
public XmlElement node;
public delegate void ParameterSubmitResult(object sender, XmlElement e);
public event ParameterSubmitResult Submit;
public void SubmitButton_Click(object sender, EventArgs e)
{
Submit(this,this.node);
Debug.WriteLine(test);
}
}
result:
Submit - null object reference
test - null object reference
pf.ShowDialog(this); is a blocking call, so pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit); is never reached: switch the order.
Submit(this,this.node); throws a null object reference because no event is assigned to it (see above). Generally, you should always check first: if (Submit != null) Submit(this,this.node);
You should change ``pf.ShowDialog(this);topf.Show(this);` so that your main form isn't disabled while your dialog box is open, if that's what you want, or use the model below (typical for dialog boxes.)
I'm not sure what pf_Submit is supposed to do, so this might not be the best way to go about it in your application, but it's how general "Proceed? Yes/No" questions work.
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(testString);
pf.ShowDialog(this); // Blocks until user submits
// Do whatever pf_Submit did here.
};
public partial class ParameterForm : Form
{
public string test; // Generally, encapsulate these
public XmlElement node; // in properties
public void SubmitButton_Click(object sender, EventArgs e)
{
Debug.WriteLine(test);
this.Close(); // Returns from ShowDialog()
}
}
When you want to use your second variant, you have to use a getString()-Method, where you can put the e.g. "testString". The way you wrote it, "testString" should be a method (and got brackets).
EDIT (a bit more precise):
You could write:
pf.getString(testString);
, if "pf" is an instance of your own class, otherwise you had to look up, whether you can retrieve a String in this class.
the thing was in line order :)
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
and
pf.Test = "test";
should have been set before
pf.ShowDialog(this);
my mistake thingking that parameter can be passed after 2nd form was displayed
thnx for answers
We have built a huge winforms project, already in progress for multiple years.
Sometimes, our users get an exception which looks like this one.
The resolution of this problem seems to be:
don't acces UI components from a background thread
.
But since our project is a very big project with a lot of different threads, we don't succeed in finding all these.
Is there a way to check (with some tool or debugging option) which components are called from a background thread?
To clarify:
I created a sample winforms project with a single Form, containing two Button
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Clicked!";
}
private void button2_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
button2.BackColor = Color.Red; //this does not throw an exception
//button2.Text = "Clicked"; //this throws an exception when uncommented
});
}
}
The background color of button2 is set to red when the button is clicked. This happens in a background thread (which is considered bad behavior). However, it doesn't (immediately) throw an exception. I would like a way to detect this as 'bad behavior'. Preferably by scanning my code, but if it's only possible by debugging, (so pausing as soon as a UI component is accessed from a background thread) it's also fine.
I've got 2 recommendations to use together, the first is a Visual Studio Plugin called DebugSingleThread.
You can freeze all the threads and work on one at a time (obviously the non-main-UI threads) and see each threads access to controls. Tedious I know but not so bad with the second method.
The second method is to get the steps in order to reproduce the problem. If you know the steps to reproduce it, it will be easier to see whats causing it. To do this I made this User Action Log project on Github.
It will record every action a user makes, you can read about it here on SO: User Activity Logging, Telemetry (and Variables in Global Exception Handlers).
I'd recommend you also log the Thread ID, then when you have been able to reproduce the problem, go to the end of the log and work out the exact steps. Its not as painful as it seems and its great for getting application telemetry.
You might be able to customise this project, eg trap a DataSource_Completed event or add a dummy DataSource property that sets the real Grids DataSource property and raises an INotifyPropertyChanged event - and if its a non-main thread ID then Debugger.Break();.
My gut feeling is you're changing a control's (eg a grid) data source in a background thread (for that non-freeze feel) and thats causing a problem with synchronisation. This is what happened to the other DevExpress customer who experienced this. Its discussed here in a different thread to the one you referenced.
Is your app set to ignore cross threading intentionally?
Cross-thread operations should be blowing up all the time in winforms. It checks for them like crazy in just about every method. for a starting point check out https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs.
Somewhere in your app, somebody might have put this line of code:
Control.CheckForIllegalCrossThreadCalls = False;
Comment that out and run the app, then follow the exceptions.
(Usually you can fix the problem by wrapping the update in an invoke, e.g., in a worker thread if you see textbox1.text=SomeString; change it to `textbox.invoke(()=>{textbox1.text=SomeString;});.
You may also have to add checking for InvokeRequired, use BeginInvoke to avoid deadlocks, and return values from invoke, those are all separate topics.
this is assuming even a moderate refactor is out of the question which for even a medium sized enterprise app is almost always the case.
Note: it's not possible to guarantee successful discovery of this case thru static analysis (that is, without running the app). unless you can solve the halting problem ... https://cs.stackexchange.com/questions/63403/is-the-halting-problem-decidable-for-pure-programs-on-an-ideal-computer etc...
I did this to search for that specific situation but of course, need to adjust it to your needs, but the purpose of this is to give you at least a possibility.
I called this method SearchForThreads but since it's just an example, you can call it whatever you want.
The main idea here is perhaps adding this Method call to a base class and call it on the constructor, makes it somewhat more flexible.
Then use reflection to invoke this method on all classes deriving from this base, and throw an exception or something if it finds this situation in any class.
There's one pre req, that is the usage of Framework 4.5.
This version of the framework added the CompilerServices attribute that gives us details about the Method's caller.
The documentation for this is here
With it we can open up the source file and dig into it.
What i did was just search for the situation you specified in your question, using rudimentary text search.
But it can give you an insight about how to do this on your solution, since i know very little about your solution, i can only work with the code you put on your post.
public static void SearchForThreads(
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
var startKey = "this.Controls.Add(";
var endKey = ")";
List<string> components = new List<string>();
var designerPath = sourceFilePath.Replace(".cs", ".Designer.cs");
if (File.Exists(designerPath))
{
var designerText = File.ReadAllText(designerPath);
var initSearchPos = designerText.IndexOf(startKey) + startKey.Length;
do
{
var endSearchPos = designerText.IndexOf(endKey, initSearchPos);
var componentName = designerText.Substring(initSearchPos, (endSearchPos - initSearchPos));
componentName = componentName.Replace("this.", "");
if (!components.Contains(componentName))
components.Add(componentName);
} while ((initSearchPos = designerText.IndexOf(startKey, initSearchPos) + startKey.Length) > startKey.Length);
}
if (components.Any())
{
var classText = File.ReadAllText(sourceFilePath);
var ThreadPos = classText.IndexOf("Task.Run");
if (ThreadPos > -1)
{
do
{
var endThreadPos = classText.IndexOf("}", ThreadPos);
if (endThreadPos > -1)
{
foreach (var component in components)
{
var search = classText.IndexOf(component, ThreadPos);
if (search > -1 && search < endThreadPos)
{
Console.WriteLine($"Found a call to UI thread component at pos: {search}");
}
}
}
}
while ((ThreadPos = classText.IndexOf("Task.Run", ++ThreadPos)) < classText.Length && ThreadPos > 0);
}
}
}
I hope it helps you out.
You can get the Line number if you split the text so you can output it, but i didn't want to go through the trouble, since i don't know what would work for you.
string[] lines = classText.Replace("\r","").Split('\n');
Try that:
public static void Main(string[] args)
{
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(exception handler);
// Set the unhandled exception mode to force all Windows Forms errors to go through the handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += // add the handler here
// Runs the application.
Application.Run(new ......);
}
Then you can log the message and the call stack and that should give you enough information to fix the issue.
I recommend you update your GUI to handle this situation automatically for your convenience. You instead use a set of inherited controls.
The general principle here is to override the property Set methods in a way to make them Thread Safe. So, in each overridden property, instead of a straight update of the base control, there's a check to see if an invoke is required (meaning we're on a separate thread the the GUI). Then, the Invoke call updates the property on the GUI thread, instead of the secondary thread.
So, if the inherited controls are used, the form code that is trying to update GUI elements from a secondary thread can be left as is.
Here is the textbox and button ones. You would add more of them as needed and add other properties as needed. Rather than putting code on individual forms.
You don't need to go into the designer, you can instead do a find/replace on the designer files only. For example, in ALL designer.cs files, you would replace System.Windows.Forms.TextBox with ThreadSafeControls.TextBoxBackgroundThread and System.Windows.Forms.Button with ThreadSafeControls.ButtonBackgroundThread.
Other controls can be created with the same principle, based on which control types & properties are being updated from the background thread.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ThreadSafeControls
{
class TextBoxBackgroundThread : System.Windows.Forms.TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.Text = value; });
else
base.Text = value;
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.ForeColor = value; });
else
base.ForeColor = value;
}
}
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.BackColor = value; });
else
base.BackColor = value;
}
}
}
class ButtonBackgroundThread : System.Windows.Forms.Button
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.Text = value; });
else
base.Text = value;
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.ForeColor = value; });
else
base.ForeColor = value;
}
}
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.BackColor = value; });
else
base.BackColor = value;
}
}
}
}
I'm getting my way around c# slowly but surely lol in this code:
// create an instance of the main form
public formMain _formMain;
public void btnDynaDotCheck_Click(object sender, EventArgs e)
{
if (_formMain.bgWorker.IsBusy != true)
{
this.btnDynaDotCheck.Enabled = false;
_formMain.bgWorker.RunWorkerAsync("dr_begin_dd_check");
}
else
{
_formMain.returnMessage("Please wait untill the current task is finished...");
return;
}
}
I'm trying to access the background worker in formMain.cs from anotherForm.cs there is no errors in VS, but when run i get
"An unhandled exception of type 'System.NullReferenceException'
occurred in " and "Additional information: Object reference not set to
an instance of an object."
On this line:
if (_formMain.bgWorker.IsBusy != true)
So i'm not really getting access in this case eh?
Use dependency injection to inject a reference to your mainform into the otherone : somewhere in your mainform code do the following :
anotherForm _anotherForm = new anotherForm(this);
_anotherForm.Show();
assuming you are creating anotherform from code within the mainform, this is actually referring to the mainform.
In the constructor of anotherFrom do this :
public anotherForm(MainForm formMain){
_formMain = formMain;
}
This is by far the most elegant way to solve this issue. Because it makes clear that there is a dependency from one form to the other and makes the design intention clear.
Using a parent is also fine, but only if the mainform is really a parent of the other form.
Going via Application object will work, but the application object is a global and you hide your dependency that way.
_formMain = Application.OpenForms["formMain"];
Add this code in button click and try it.
When accessing _formMain from anotherForm:
I assume anotherForm is instantiated and called from _formMain like this:
anotherForm _anotherForm = new anotherForm();
_anotherForm.Show();
there's now several ways to access _formMain from _anotherForm but the easiest I think is to set _formMain as the parent of _anotherForm:
_anotherForm.Parent = this; // insert before _anotherForm.Show()
this way you can get hold of it in _anotherForm like this
public void btnDynaDotCheck_Click(object sender, EventArgs e)
{
...
formMain _formMain = this.Parent as formMain;
if(_formMain != null)
{
... // do whatever ever you have to do
}
}
but be careful... getting your BackgroundWorker in _formMain requires public methods you can call and return your BackgroundWorker.
Hope this helps!
Thanks for the help guys :)
I now have:
// create an instance of the formMain
formMain _formMain = new formMain();
public void btnDynaDotCheck_Click(object sender, EventArgs e)
{
if (_formMain.bgWorker.IsBusy != true)
{
this.btnDynaDotCheck.Enabled = false;
_formMain.bgWorker.RunWorkerAsync("dr_begin_dd_check");
}
else
{
_formMain.returnMessage("Please wait untill the current task is finished...");
return;
}
}
Which works :) it gets through to the main form:
public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// action to string format
string action = e.Argument as string;
if (action == "dr_begin_dd_check")
{
BeginInvoke((Action)(() =>
{
statusLabel.Text = "Access the bgw...";
}
));
} // dr_begin_dd_check
I'm now getting the error in the formMain:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
I'm not sure where the error lies in my above code or actually in the formMain section, or should i open a new question? :)
cheers guys
Graham
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 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();