This question already has answers here:
Is there a way to reach a `protected` member of another object from a derived type?
(7 answers)
Closed 9 years ago.
I'm coding a WP7 GUI and have designed a Control class, and a ParentControl class that derives from Control and has a list of child controls. However, when adding a child to a ParentControl instance, I'm unable to access the child's parent reference because I set it to be 'protected' from users of the controls.
The exact error is
"Cannot access protected member 'Control.Parent' via a qualifier of type 'Control';
the qualifier must be of type 'ParentControl' (or derived from it)"
public abstract class Control //such as a button or radio button
{
public ParentControl Parent { get; protected set; }
}
public abstract class ParentControl : Control //such as a panel or menu
{
protected List<Control> children = new List<Control>();;
public void AddChild(Control child, int index)
{
NeedSizeUpdate = true;
if (child.Parent != null)
child.Parent.RemoveChild(child);
child.Parent = this; //How do I access the parent?
children.Insert(index, child);
OnChildAdded(index, child);
}
}
How might I fix this?
Yes, this is because other things may derive from Control, and ParentControl can only access base members of controls it derives from. For instance, if Control2 derived from Control, then ParentControl would not derive from Control2 and so could not access it's base members.
So, you either make Parent a public property, or if you want to keep it hidden away from general users of the control, you could make access via an interface, and implement it explicitly:
interface IChildControl
{
ParentControl Parent { get; set; }
}
public abstract class Control : IChildControl //such as a button or radio button
{
ParentControl IChildControl.Parent { get; set; }
}
The explicit implementation (IChildControl.Parent) means that consumers with just a Control instance will not see the Parent property. It must be explicitly cast to IChildControl to access it.
Related
I created a C# class that extends Button, but associates a generic type like this:
public class DropButton<T> : Button where T : INamed
{
//contains lots of properties and methods that
//reference and operate on the objects of type T
}
...where INamed interface specifies what properties any class should have to be used with my DropButton:
public interface INamed
{
string Name { get; set; }
bool Selected { get; set; }
}
I can add an instance of the control on a WinForms form programmatically:
private DropButton<MySet> MyDropButton = new DropButton<MySet>();
//Then set properties of the control in the form constructor and add it.
It works splendidly and it keeps my form code a lot cleaner.
However, the new control does not show up in the Toolbox for the project. Nor does the designer recognize the code if I manually modify the .Designer.cs file with an instance of it.
So two questions:
Is there a way to trick the WinForms form designer to allow it in design mode?
Is there a better way to go about this pattern?
I have a main form that acts as a "Wizard" for a bunch of different user controls. I have one user control with relatively basic operations, and I am trying to create a new user control that inherits this basic user control.
However, the base user control has a variable containing the main form (so the user control can access the wizard control functions in the main form). When I create a new "inherited user control" the Designer complains that the reference to the main for has not been set to an instance of the object. I set the reference to the MainForm during runtime when I create an instance of the base user control.
Is there a way to make this work? Below is some simplified code demonstrating my problem.
MainForm.cs:
public partial class MainForm : Form
{
public string exampleString = "From MainForm";
public MainForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
BaseControl base = new BaseControl();
base.mainForm = this;
{
}
BaseControl.cs
public partial class BaseControl : UserControl
{
public MainForm mainForm { get; set;}
public TestPanel()
{
InitializeComponent();
string needed = mainForm.exampleString; //Object reference not set to an instance of an object here
}
}
So when I try to create a user control that inherits BaseControl through Solution -> Add -> New Item -> Inherited User Control and select BaseClass, the designer complains of the "Object reference not set to an instance of an object" error at the string needed = mainForm.exampleString line in BaseControl.cs.
Any help with this would be greatly appreciated. Hopefully this is enough information for you to understand what I am trying to do.
Thanks.
The code which you shared will not work, neither at run-time nor in design-time.
You are trying to use mainForm.exampleString in constructor of BaseControl while mainForm will be assigned just after creating an instance of BaseControl.
In such cases, specially when you want to have design-time support, you can derive from ISupportInitialize interface and override BeginInit and EndInit.
Example
The following control implements ISupportInitialize. If you drop an instance of the control on the form, at run-time, it tries to find the parent form and if it was MainForm tries to use public members of the MainForm:
using System.ComponentModel;
using System.Windows.Forms;
public class MyControl : Control, ISupportInitialize
{
public void BeginInit()
{
}
public void EndInit()
{
var parent = this.FindForm() as MainForm;
if (parent != null)
{
//Access to MainForm members
}
}
}
This is just an example that shows how to use ISupportInitialize. In action, it's not a good idea to have a dependency to a specific type of parent form. A better idea as already mentioned in Jimi's comment is relying on interfaces. For example you can have a property of ISomeInterface in your control. The interface should contain the methods or properties which you want to have for the parent of your control. Then implement the interface in some forms. Then after you dropped an instance of your control at run-time or design-time, assign the form to the property.
Problem: I currently have a class that takes in an object of type Control and does some work. I'm attempting to create a class that can take in either a Control, Button or a Label object. I can make this work however it would involve that I copy and paste the class two more times. One to work with Buttons and another to work with Labels. The logic and the members being called are exactly the same with the exception of the Type. I have simplified the concept I'm wishing to convey below:
// This class currently only does work on a Control object
public class takeControlType
{
public takeControlType(Control control)
{
string objectName = control.Name.ToString();
}
}
I could copy paste the code above and make it work by overloading the class Constructor like this:
public class takeAnyType
{
public takeAnyType(Control control)
{
string objectName = control.Name.ToString();
}
public takeAnyType(Button button)
{
string objectName = button.Name.ToString();
}
public takeAnyType(Label label)
{
string objectName = label.Name.ToString();
}
}
Am I correct in thinking that this just seems like a drag in productivity? I'm hoping I can reuse the same logic despite the Type being different as the only item that I would need to replace is the Type. The logic and properties being implemented in my class are exactly the same for Controls, Buttons and Labels. I've researched generics but due to the fact that I'm pulling back properties and methods specific to either a Control, Button or Label I can't seem to get generics to work with the object properties such as .Name or .Width or .Capture for example. The only methods the generic Type provides me with are
Equals()
GetHashCode()
GetType()
ToString()
I need access to a few of the properties I mentioned previously. How does one accomplish this in order that I might avoid having to copy/paste 266 lines of code that make up my class that currently is only able to work with Control objects?
Aside from attempting to make use of Generics I also tried to see if I could use base class type object as opposed to Control but that led me to the same issue I'm currently having with Generics. I no longer have access to the members that are associated with Controls, Buttons and Labels.
To clear up any confusion the example (non-working) code below is what I'm attempting to accomplish.
public class takeAnyType
{
public takeAnyType(anyType obj)
{
string objectName = obj.Name.ToString();
obj.Cursor = Cursors.SizeNESW;
obj.Capture = true;
obj.Width = 20;
obj.Top = 100;
}
}
Button and Label classes inherit from Control (indirectly). This means that if you only create a class for Control, you can still use it for objects of type Button or Label. You don't have to create special classes for those.
In C# (and OO languages in general), you can assign an instance of a derived class to a variable of a super class. For example, this is valid C# code:
Control control = new Button();
An answer addresses your example, but your problem seems to describe something more - doing some more convoluted login on Labels and Buttons. One way to do this is the following:
1) declare a base class to handle common issue (e.g. your name example)
2) declare a class for each Label and Button to handle specific logic
public class ControlHelper
{
public virtual String GetControlName(Control control)
{
return control.Name.ToString();
}
// it is not possible to do the logic on a generic control, so force derived classes to provide the logic
public abstract void DoSomeFancyStuffWithControl(Control control);
// other common functions may come here
}
public class LabelHelper : ControlHelper
{
// you may override virtual methods from ControlHelper. For GetControlName, it should not be the case
public override DoSomeFancyStuffWithControl(Control control)
{
var button = control as Label;
// ...
}
// does not have to be virtual, but allow further inheritance
public virtual String GetText(Label l)
{
return l.Text;
}
// other label specific methods come here
}
public class ButtonHelper : ControlHelper
{
public override DoSomeFancyStuffWithControl(Control control)
{
var button = control as Button;
// ...
}
public virtual bool GetEnabled(Button b)
{
return b.Enabled;
}
// other button specific functions may come here
}
I have a BaseForm with a collection or a list, containing some typed objects which I want to share with child form. The idea is, I want my child form to be able to add/remove those objects as well. Those objects contain some definition of certain icons/buttons which I want to do visual inheritance.
So I have the following Base Form
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<TItem> TItems
{
get { return this._TItems; }
}
public FormBase()
{
InitializeComponent();
//TItems.Add(new TItem());
}
and a simple object
public class TItem
{
public string T1 { get; set; }
}
Inheriting this base form I can freely modify the collection in designer for the child. But once I comment out that part which add an item from the parent form, the collection property in designer (for the child form) gray-out saying Read-Only.
I have tried different type of list, auto-property, changing DesignerSerializationVisibility.visible, initializing the list from child constructor... I just couldn't achieve what I want.
Can anyone point me to the right direction?
i have 1 aspx page and 1 user control. I want to access hidden field of aspx page in the code behind of user control page.
Please help me on this.
Use Page.FindControl
var hiddenField = this.Page.FindControl("hiddenField") as HiddenField;
Here's one way to do it:
Expose the hidden field control as a public property of the containing page.
In the user control, cast Page to the specific type of the containing page.
Access the property.
I don't really like this approach as it tightly couples the user control with the containing page type, but this seems to happen frequently in web forms.
Example
public class MyPage : Page
{
public HtmlInputHidden MyHiddenField
{
get{ return this.hdnField1; }
}
}
public class MyUserControl : UserControl
{
protected override OnLoad( EventArgs e )
{
MyPage p = (MyPage)Page;
HtmlInputHidden h = p.MyHiddenField;
}
}
Example 2 - Parent Initializes Child
This example is cleaner in that the child is agnostic of its parent. However, it requires the parent to initialize the child at the right time (which can be tricky with the web form page lifecycle) and requires that the parent have knowledge of the inner workings of the child.
public class MyPage : Page
{
protected override OnLoad( EventArgs e )
{
this.MyUserControl.Initialize( this.MyHiddenField );
}
}
public class MyUserControl : UserControl
{
public void Initialize( HtmlInputHidden input )
{
// now child user control has access to the data without needing to know
// about its parent
}
}