To answer each of your questions:
What are the implications of the procedure I used?
In this instance, you are passing a reference from a parent object (the Main form) to the child object (the Settings form). The result of this is that the Settings form must know a lot of things about the Main form in order to work properly. Some not-so-desirable implications:
- The Settings form must know the names of all the controls in the Main form in order to work properly.
- You have to make a lot of things in the Main form public. Making things public isn't a mortal sin by any stretch, however now your Main form is advertising a lot of functionality that it doesn't need to advertise to the outside world. This is more of an issue when your project grows, or if you work with more than one developer, who will be poking around the public members of your objects to figure out how to work with your code from the outside.
- Down the road, if for some reason the main form and the settings form were in two different assemblies (DLL files), you might run into trouble because you cannot create circular references.
Why isn't it an object-oriented approach?
Well, what you are doing is object-oriented, however because you have a child object depending on a parent object, you are going against the Dependency Inversion Principle and are thus causing tight coupling between objects.
When people on SO and other sites talk about how "object oriented" something is, they often are referring to S.O.L.I.D., which are five key principles of object oriented design.
What would be the best practice to send information back and forth between classes/forms?
I think in this instance the easiest way to accomplish this would be to create properties in your Settings form that represent the values that are modified within the settings window.
For example, this code would be in your Settings form:
public string UserName
{
get { return txtUserName.Text; }
set { txtuserName.Text = value; }
}
And this code would be in your Main Form:
private void settingsToolStripButton_Click(object sender, EventArgs e)
{
settingsForm = new miniDldMngr.SettingsForm() { UserName = lblUserName.Text };
settingsForm.ShowDialog();
lblUserName.Text = settingsForm.UserName;
}
It is slightly more code, but now you are free to make as many changes as you want to the main form, and the settingsform won't need to be updated.
There are of course many other approaches you can take, which leads me to your last two questions:
How would you go about creating the event mentioned by codesparkle?
Creating and raising an event is another way to avoid tight coupling between two classes. Notice how the Windows Forms objects, such as ToolStrip are able to call methods in your code (settingsToolStripButton_Click), even though the Windows Forms code is not linked to your code. That is the beauty of events. My opinion is that it is overkill in this situation, since you are showing the form as a Dialog (where you can just read all of the values after the dialog closes), but nonetheless here is an example:
First, you would have code like this in your SettingsForm:
public string UserName
{
get { return txtUserName.Text; }
set { txtuserName.Text = value; }
}
public event EventHandler UserNameChanged;
protected void OnUserNameChanged(EventArgs e)
{
if(UserNameChanged != null)
{
UserNameChanged(this, e);
}
}
private void txtUserName_TextChanged(object sender, EventArgs e)
{
OnUserNameChanged(EventArgs.Empty);
}
Then, you would have code similar to this in your MainForm:
private void settingsToolStripButton_Click(object sender, EventArgs e)
{
settingsForm = new miniDldMngr.SettingsForm() { UserName = lblUserName.Text };
settingsForm.ShowDialog();
lblUserName.UserNameChanged +=
(sender, args) => lblUserName.Text = settingsForm.UserName;
}
I don't know anything about MVC or MVVM design patterns, so some links to a structured explanation would be appreciated.
The MVVM pattern is a pattern championed by Microsoft, primarily for use in WPF. You can read a nice description here:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
You can use this pattern in Windows Forms, however I wouldn't recommend it, as WPF has more of the basic plumbing complete in order to actually make it work.
Happy coding!
The modern consensus is that you should not do this at all.
Having a 'all instances of this class' design has proven to be troublesome in many aspects, the foremost being with regards to concurrency. Do you want all widgets, or all widgets owned by your thread? Do you really want to make that all widget list threadsafe? Remember that unit tests are almost always done in parallel, so "my app is single threaded" might not be good enough.
The other problem you run into is in your design. With the 'one and only' list of anything, be it global, static or singleton you'll quickly run into issues when you need more than one. You'll run into issues with temporary widgets or offscreen widgets if they're automatically added to the list. You'll run into errors where you forgot to add them to the list if they're not automatically handled.
Once you don't have the 'all instances of a class' requirement, then you end up with a plain old collection. Whatever consuming code is working with the widgets (even if that's some framework that is in turn consumed) can orchestrate how the collection is used and how/when the widgets get put in there. The widgets themselves should not care.
Best Answer
Completely ignoring any temptation to guess at the specifics of what you're trying to accomplish with your controls, or why, let me take a quick stab at answering your basic questions.
Up front; from your description, I think it sounds like your best option is just to have an instance field, not a static one, and not a singleton. You said "I actually need each instance of the control to have one such variable, not one variable across all the instances of the control in that single form." That is kind of a loose definition of an instance variable. Does it even really need to be public?
Actually, I would make it an instance property, because chances are pretty good that sooner or later you'll want to raise some event when the thing changes, or do something else that will end up making a public field feel like an unfortunate decision in retrospect. Hindsight is always 20/20. Or at least 20/30 or so. But in this case, a little foresight says that using a simple automatic property is cheap insurance:
public SomeDataType WidgetSettings{ get; set; }
So; there is only one instance of a static field/variable for the entire assembly. Every instance of the class containing that public static variable has a reference to the very same object in memory. So yes, all of your control instances are accessing the very same object/memory.
Any time you see "static" and "public" together in the same variable declaration line, it should be reason to pause and think. The thing is, if the variable is something like a string, or a numeric type, or a simple collection, you will have to use synchronization code everyplace that you access that field from. On the other hand, if it's a reference to a class with static getters and setters for various attributes, then you can put that synchronization code in the getters and setters, and you can test that class and its attributes in isolation (as a unit) and know that it works properly. I don't know if I expressed that as well as I meant to... But honestly, if you can what you need to do using instance variables and avoiding the whole thread-safety issue altogether, then do that instead.
If you have a static variable, it is absolutely for certain not thread-safe. At all. Maybe you're thinking that you're doing a Winforms app and all of the UI stuff executes on a single thread (because Winforms is not thread-safe), which is true enough if you follow the rules, except that we're all creative about using timers and background workers and such to loosen things up. So it is still possible for strange things to happen. So in the worst case, not only are all of your control instances accessing and updating the same variable, they might be trampling all over each other in the process.
If you're messing with WPF, then the UI library is thread-safe and the potential pitfalls of multithreading combined with static fields apply.
If the data that the variable points at is a type that requires more than one instruction to write completely, then it is only a matter of time before one control instance writes only half of a new value before the next control instance reads that field and gets a value composed of half of one write and half of a different write. I think the proper technical term for that situation is something along the lines of "fubar." Of course the thread that reads that mangled variable may well try to write back to it before it gets swapped out by the thread scheduler and the original thread gets another go, replacing the first thread's half-value with its own half-value before the first thread even gets a chance to write the second half of its value. This is likely to end in tears, or baldness, or more likely both tears and baldness.
Finally, the singleton has all the same potential thread-safety issues as the static field, because the whole notion of a singleton is that you grab your instance from a factory method, which serves the same single instance up to everybody who asks. So everybody who asks is, by definition, accessing the very same object.
In this case, my gut tells me that "singleton" is a code word for "fancy static field, which requires a lot more code and a lot more testing, and still isn't thread-safe unless you write error-prone and performance-robbing synchronization code to go with it."
When a code word has a definition that long, it's worth seriously looking at a different approach.
For what it's worth. :-) Without more detail it would be awfully difficult to provide any answers that relate directly to your question. What type of object is this static field pointing at? Is it a collection? Is it some kind of settings or configuration class with multiple properties? Is it an object that communicates with other parts of the system (events) in response to different signals you send it? Etc.