I don't know why you need static controls. Maybe you know something I don't. I've used a lot of visual inheritance but I've never seen static controls to be necessary. If you have a common treeview control, let every form instance have its own instance of the control, and share a single instance of the data bound to the treeviews.
Sharing control state (as opposed to data) between forms is also an unusual requirement. Are you sure FormB really needs to know about the state of buttons on FormA?Consider the MVP or MVC designs. Think of each form as a dumb "view" that knows nothing about the other views or even the application itself. Supervise each view with a smart presenter/controller. If it makes sense, a single presenter can supervise several views. Associate a state object with each view. If you have some some state which needs to be shared between views, let the presenter(s) mediate this (and consider databinding - see below).
Agreed, Visual Studio will give you headaches. When considering form or usercontrol inheritance, you need to carefully weigh the benefits against the potential (and probable) cost of wrestling with the form designer's frustrating quirks and limitations. I suggest keeping form inheritance to a minimum - use it only when the payoff is high. Keep in mind that, as an alternative to subclassing, you can create common "base" form and simply instantiate it once for each would-be "child" and then customize it on-the-fly. This makes sense when differences between each version of the form are minor compared to the shared aspects. (IOW: complex base form, only-slighty-more-complex child forms)
Do make use of usercontrols when it helps you prevent significant duplication of UI development. Consider usercontrol inheritance but apply the same considerations as for form inheritance.
I think the most important advice I can offer is, if you don't currently employ some form of the view/controller pattern, I strongly encourage you to start doing so. It forces you to learn and appreciate the benefits of loose-couping and layer separation.
response to your update
Which layer should store stuff like, the last pressed button, so that I can keep it highlighted for the user...
You can share state between views much like you would share state between a presenter and its view. Create a special class SharedViewState. For simplicity you can make it a singleton, or you can instantiate it in the main presenter and pass it to all views (via their presenters) from there. When the state is associated with controls, use data binding where possible. Most Control
properties can be data-bound. For example the BackColor property of a Button could be bound to a property of your SharedViewState class. If you do this binding on all forms which have identical buttons, you can highlight Button1 on all forms just by setting SharedViewState.Button1BackColor = someColor
.
If you aren't familiar with WinForms databinding, hit MSDN and do some reading. It's not difficult. Learn about INotifyPropertyChanged
and you're halfway there.
Here's a typical implementation of a viewstate class with the Button1BackColor property as an example:
public class SharedViewState : INotifyPropertyChanged
{
// boilerplate INotifyPropertyChanged stuff
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
// example of a property for data-binding
private Color button1BackColor;
public Color Button1BackColor
{
get { return button1BackColor; }
set
{
if (value != button1BackColor)
{
button1BackColor = value;
NotifyPropertyChanged("Button1BackColor");
}
}
}
}
It looks like you've fallen into some of the common pitfalls, but don't worry, they can be fixed :)
First you need to look at your application a little differently and start breaking it down into chunks. We can split the chunks in two directions. First we can separate controlling logic (The business rules, data access code, user rights code,all that sort of stuff) from the UI code. Second we can break the UI code down into chunks.
So we'll do the latter part first, breaking the UI down into chunks. The easiest way to do this is to have a single host form on which you compose your UI with usercontrols. Each user control will be in charge of a region of the form. So imagine your application had a list of users, and when you click on a user a text box below it is filled with their details. You could have one user control managing the display of the user list and a second one managing the display of the user's details.
The real trick here is how you manage the communication between the controls. You don't want 30 user controls on the form all randomly holding references to each other and calling methods on them.
So you create an interface for each control. The interface contains the operations the control will accept and any events it raises. When you think about this app, you don't care if the list box list selection changes, you are interested in the fact a new user has changed.
So using our example app, the first interface for the control hosting the listbox of users would include an event called UserChanged which passes a user object out.
This is great because now if you get bored of the listbox and want a 3d zoomy magic eye control, you just code it to the same interface and plug it in :)
Ok, so part two, separating the UI logic from the domain logic. Well, this is a well worn path and I'd recommend you look at MVP pattern here. It's really simple.
Each control is now called a View (V in MVP) and we've already covered most of what is needed above. In this case, the control and an interface for it.
All we're adding is the model and the presenter.
The model contains the logic that manages your application state. You know the stuff, it would go to the database to get the users, write to the database when you add a user, and so on. The idea is you can test all of this in complete isolation from everything else.
The Presenter is a bit more tricky to explain. It is a class which sits between the model and the View. It is created by the view and the view passes itself into the presenter using the interface we discussed earlier.
The presenter doesn't have to have its own interface, but I like to create one anyway. Makes what you want the presenter to do explicit.
So the presenter would expose methods like ListOfAllUsers which the View would use to get its list of users, alternatively, you could put an AddUser method the View and call that from the presenter. I prefer the latter. That way the presenter can add a user to the listbox when ever it wants.
The Presenter would also have properties like CanEditUser, which will return true if the user selected can be edited. The View will then query that every time it needs to know. You might want editable ones in black and read only ones in Gray. Technically that's a decision for the View as it is UI focused, whether the user is editable in the first place is for the Presenter. The presenter knows because it talks to the Model.
So in summary, use MVP. Microsoft provide something called SCSF (Smart Client Software Factory) which uses MVP in the way I've described. It does a lot of other things too. It's quite complex and I don't like the way they do everything, but it may help.
Best Answer
I'm gonna oppose you with different paradigm : Favor composition over inheritance.
Even when you start writing GUI code by hand and use inheritance to share behavior and visuals between forms, you will hit same problem as normal OOP design. Lets say you have DialogForm, that has OK/Cancel buttons and GridForm, that has grid. You derive all dialogs from DialogForm and all forms with Grid from GridForm. And then you find out you want form with both. How would you solve it? If you are using form inheritance, then there is no way solve it. On the other side, if you are using UserControls, then you can simply put GridControl on your form and be done with it. And you can still use designer with UserControls, so you don't have to waste time writing tedious GUI code.