C# OOP concepts: is inheritance from Control class the right way? - c#

My question is about the right way of code writing. I use C# Winforms.
I created an instance of Control class (anyone available in designer), for example Panel class, set some properties of this object, subscribed events, wrote event handlers, etc. But usually I do this by next way: I create my CustomPanel class inherited from Panel and white above code (setting properties, subscribing events, event handlers) in CustomPanel class. So, when I want to use Panel with my setting and functionality, I can drag and drop CustomPanel object from designer. Is this a right way or interitance from Control classes isn't a good idea? Maybe I should create my own class (not inherited) contains Panel settings and bind it to Panel?

Use decorators when possible
Using Visual inheritance with the windows form designer is very very buggy, a lot of times the designer doesn't work and on top of that it never works when your controls are in a 64 bit assembly
http://support.microsoft.com/kb/967050

I would rely on Liskov Substitution Principle when making the decision. If what you're making ia a Panel in spirit, then it's OK to inherit (I feel it's OK).

In Windows Forms there are very limited capabilities to modify the controls(You could try WPF where are more ways to achieve such modification).
1) In WinForms you usually either end with enormous amount of event handlers:
winFormsControl.OnSomethingChanged += new delegate(object sender, SomeEventArgs e)
{
WinFormsControl control = (WinFormsControl)sender;
control.Property = value;
// ...
};
winFormsControl.OnSomethingOtherHappened += this.Handle_WinFormsControl_OnSomethingOtherHappened;
2) or build your own derived class if you have to add a lot of your properties and events or some methods to override like:
public class MyCustomPanel : Panel
{
//Constructor
public MyCustomPanel()
{
base.Layout += this.OnBaseLayout;
...
}
// Overriden methods(i am not sure if there could be any)
public override void SomeVirtualMethod() {...}
// Custom members
public CustomLayoutMode LayoutMode { get; set;}
public event EventHandler CustomLayoutCompleted;
}
3) Creating your own UserControl derived class is an option too.
4) Also you could try to wrap the existing control in a custom control derived directly from the Control:
public MyCustomControl : Control
{
private Panel WrappedPanel;
public MyCustomControl()
...
}
But I am a bit unsure about the way how you will paint inside your wrapper(but it is probably possible, just my WinForms are rusty).
Nonetheless most of the considered scenarios will be non-ideal - usually we need original properties and events of the Panel to be in Control's interface and no one likes boilerplate code.
The simplest way to avoid such problems is to inherit. No one says that it is the best. Especially when you will have to combine some hard-coded behaviours of custom controls into one.

Related

How to expose ALL properties of a Control in a class derived from Form?

I have class derived from Form and it contains a TableLayoutPanel and in it one Label and one Panel. When I create instance of this Form, all properties and events of controls in design editor are read-only. Is there any way how to expose whole object for editing? I know that I can expose properties one by one, but that is not the best way in case when you want all of them.
Have a look here:
Avoid Visual Inheritance
The TableLayoutPanel control does not support visual inheritance in
the Windows Forms Designer. A TableLayoutPanel control in a
derived class appears as locked at design time.
You can use internal or make a getter method / property
public Label GetLabel() => return someLabel;
or
public Label MyLabel { get { return someLabel; } }
or
internal Label someLabel;

C# Windows Forms: Access Controls(Labels, Buttons) in other Classes

I need to know, how I can access lables or buttons other than in my "Form1"-Class.
My Problem:
I created for example labels, buttons via the design viewer. Now I can access
them in my Form1 Class. (testlabel.Enabled == true) just for example.
What I CAN'T do: Access those labels, buttons in another class! Let's say
I have a class "second-class" and I want to have a method there, that changes
the property of a label to
`testlabel.Enabled == false`
That's not possible, because in that "second-class" it's not visible.
So, is there an obvious easy solution to make those controls accessible in other classes?
Create a method in that (Second class) which takes that component (Label or Button or whatever you want to modify) as parameter into that method.
public void disableLabel(Label inputLabel)
{
inputLabel.Enabled == false
}
Create a method like the above.
Now in the form1 class you just to need to call that method and pass your Label into that method to Disable it.
SecondClass objSecondClass = new SecondClass();
objSecondClass.disableLabel(testlabel);
Every control in a form class is created by default with its property Modifiers set to Private
If you change it to Public you could access the control instance from another class.
However this is really a bad practice to follow. Messing with the visibility of the control is dangerous and could cause very complicated bugs to resolve.
If you really need to change something in your form class then provide a public method and call this method to change the internal functionality of the target form

Correct UserControl Usage?

I just started breaking up my GUI application into UserControls. I have a TabControl with a bunch of TagePages. Obviously my MainForm.cs file was filled up with tons of events and controls etc and it got very messy quick.
So a previous question gained me the insight of how to create a UserControl. I intend on creating a UserControl for each TabPage and I was wondering how I can interact with Components on the main form or other UserControls.
Here is an example of a TabPage that I have made using a UserControl, which needs to Enable or Disable a button depending which TabPage is currently selected. Is this proper usage or is there a better way?
public partial class TabDetails : UserControl
{
private RequestForm fRequestForm;
public TabDetails()
{
InitializeComponent();
}
public void CustomInitialization(RequestForm pRequestForm)
{
fRequestForm = pRequestForm;
pRequestForm.TabControl_Main.SelectedIndexChanged += SelectedTabIndexChanged;
}
private void SelectedTabIndexChanged(object pSender, EventArgs pEvents)
{
fRequestForm.Button_SubmitRequest.Enabled = fRequestForm.TabControl_Main.SelectedTab != fRequestForm.Tab_Details;
}
}
In the MainForm.cs constructor I call:
this.tab_Details1.CustomInitialization(this);
This doesn't look like a good use of a user control. The user control should not decide how things in the form should behave when something is changed in the user control. A user control should be unaware of its container and should operate in any container.
The user control should notify the form that something has changed without telling what's the internal implementation and the form should decide what to do.
Example:
A user control named "NameUserControl" consists of TitleComboBox, FirstNameTextBox and LastNameTextBox. The user control wants to notify when one of the values has changed.
Wrong Way:
Create events:
TitleComboBox - SelectedIndexChanged.
FirstNameTextBox, LastNameTextBox - TextChanged.
The problems here:
You expose the internal controls behavior. What will happen if you want to change the TitleComboBox to TextBox? You'll have to change the event name and implementation.
You expose the fact that you use exactly 3 different controls. What will happen if you want to use the same text box for first and last name? You'll have to delete one event and change the name of the other.
Good Way:
Create only a single event: NameChanged and expose 1 property of FullName or three different properties for the values.
Either way the form subscribe to the event and decide what to do next.
Another thing to think about: the more you add more functionality to your user control, you either make it less reusable or you make its code more complex. For example, if you add validation inside the user control, you'll find one day that you need it without validation, so you'll add a property "bool ValidateData" or it will be so complicated that you'll need to build another control. One way to solve that is to build very small user controls, but combine them in one or more bigger user controls that fit all your current needs.

How to access inherited controls in the winforms designer

I'm making some controls which all have to share the same look and some common behavior, although they are meant for different kind of inputs. So I made a BaseClass which inherit from UserControl, and all my controls inherit from BaseClass.
However, if i add controls for BaseClass in the designer, such as a TableLayoutPanel, i can't access them when I'm designing the inherited classes. I see the TableLayoutPanel, but even though he is "protected", i can't modify it or put controls in it through the designer. I've no trouble accesing it by code, but i don't want to lose the ability to use the designer.
Right now, i simply removed all controls from BaseClass, added the layout and all the common controls in each of the inherited class, then use references to manipulate them inside BaseClass. But that doesn't satisfy me at all. Is there a way to make the designer work with inherited protected member controls ?
Environment : C#, .NET 3.5, Visual Studio 2008
EDIT to answer SLaks's suggestion. I tried setting a property, and although I'm not used to use them it doesn't seem to work. Here is the code i tried :
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public TableLayoutPanel TableLayoutPanel1
{
get { return tableLayoutPanel1;}
set { tableLayoutPanel1 = value;}
}
}
public partial class UserControl2 : UserControl1
{
public UserControl2()
{
InitializeComponent();
}
}
When you try to access from the inherited control with the designer to the TableLayoutPanel declared in the base control, you're using a feature in WinForms called "Visual Inheritance".
Unfortunately TableLayoutPanel doesn't support visual inheritance:
http://msdn.microsoft.com/en-us/library/ms171689%28VS.80%29.aspx
That's why the TableLayoutPanel appears blocked in the inherited controls.
Try adding this attribute to the definition of the panel (this may or may not help):
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
You have to design the base controls on their own. Changes are reflected in the designer after you successfully rebuild the controls project. If you make the members public you can edit them but the changes won't persist.
Try making a ParentControlDesigner for your control, overriding InternalControlDesigner, and returning (designerHost.GetDesigner(tableLayoutPanel) as ControlDesigner). designerHost is (IDesignerHost) component.Site.GetService(typeof(IDesignerHost)).
I vaguely remember solving a similar problem by putting the base class it its own DLL and building it first. I've had a rummage but I can't find the project. Sorry.

DataGridView locked on a inherited UserControl

I have a UserControl with some predefined controls (groupbox,button,datagridview) on it, these controls are marked as protected and the components variable is also marked as protected.
I then want to inherit from this base UserControl to another UserControl, however the DataGridView is always locked in the designer.
I suspect it may have something to do with the DataGridView implementing ISupportInitilize.
public class BaseGridDetail : UserControl
Has a DataGridView control (et al) defined.
public class InheritedDetail : BaseGridDetail
The DataGridView control is locked
Does anyone have any ideas how to make this control available in the designer after inheritenace?
By the looks of it, DataListView (and some other controls) do not support visual inheritance. There's a connect issue logged here which doesn't look hopeful.
There have been similar issues logged with other form controls, e.g. flowlayoutpanel.
I'm unable to find a way to force visual inheritance.
Here's the official answer on connect:
"For this particular release, the DataGridView was not designed to be used in visual intheritance. We will keep your suggestion in mind when we plan our future release"
That is of 26/05/2006.
Update: found this blog post which may have the answer
Edit: I was unable to verify the blog post's claims.
Looks like might be the latest on this issue
It looks like you can still manipulate the DataListView at runtime though, so you might be able to set visual properties (and other settings). It's not a great compromise.
[1] create Your Custom UserControl
[2] make your custom userControl use the below Inherited DataGridView:
[Designer(typeof System.Windows.Forms.Design.ControlDesigner))]
public class InheritedDataGridView : DataGridView { }
[3] Inherit from your Custom UserControl , And viola !!
[4] Ohh dont forget to add "System.Design" dll
Enjoy.
Add the datagrid to a panel control and set the modifiers of the panel and the datagrid to protected. This will all your inherited form design time access to the properties of the grid.
I left an answer but re-read your question and decided to delete it.
What is it about the DataGridView that you're trying to modify in the inherited control? It's columns? I've been able to do this by setting up a protected method in my base UserControl and passing the grid's column collection into it, like this:
// in base UserControl
public BaseGridDetail()
{
InitializeComponent();
InitGridColumns(dataGridView1.Columns);
}
protected virtual void InitGridColumns(DataGridViewColumnCollection columns)
{
columns.Clear();
}
Now your derived control can simply override that method, like so:
// in InheritedDetail
protected override void InitGridColumns(DataGridViewColumnCollection columns)
{
base.InitGridColumns(columns);
// add my own custom columns
}
Change property to defined [private] to [protected] defined at xx.designer.cs
which is originally machine generated code.
for example
private System.Windows.Forms.Button btnSave;
to
protected System.Windows.Forms.Button btnSave;
and rebuild it.
then you can change the property of inherited control.

Categories