C# UserControl multiple inheritance - c#

I would like to create my own user controls which implement more properties and methods than the ones given from .NET Framework. First of all I would like to have a custom UserControl class, which I would call MyUserControl and of course will inherit from UserControl:
public class MyUserControl : UserControl {
public MyUserControl() : base() {
}
...
}
After that I would like to have my own MyTestBox, which will inherit from TextBox.
public class MyTextBox : TextBox {
public MyTextBox() : base() {
}
...
}
My problem now is that I want MyTextBox to inherit from MyUserControl also, because I have properties and methods implemented there, that I need in MyTextBox also.
The only solution I could think of is to make MyTextBox inherit just from MyUserControl and not from TextBox, but add a TextBox in it in the constructor:
public class MyTextBox : MyUserControl {
public MyTextBox() : base() {
Add(new TextBox());
}
...
}
but then I would have to re-implement every single property and method of TextBox in MyTextBox. Is there a better way to achieve this?

Separate out all the common code into a separate class and use delegation to handle it.
ie. something like this:
public class MyUserControl : UserControl
{
private MyExtraControlCode _Extras;
public MyUserControl()
{
_Extras = new MyExtraControlCode(this);
}
public int GetInt32Value()
{
return _Extras.GetInt32Value();
}
}
public class MyTextBox : TextBox
{
private MyExtraControlCode _Extras;
public MyTextBox()
{
_Extras = new MyExtraControlCode(this);
}
public int GetInt32Value()
{
return _Extras.GetInt32Value();
}
}
or something similar.
Not as straightforward, but multiple inheritance is just not supported in .NET.

After looking at the problem in more detail, I finally reached to this conclusion: There are 3 ways to achieve what I want, but all of them require some extra implementation.
First of all multiple inheritance is not supported in C# .NET. In my case it would make things more complex anyway, because in MyTextBox I wanted in a way to be able to access custom properties/methods from both MyUserControl and TextBox. But both of these classes inherit from Component Class, so multiple inheritance (if it were possible) would mess thing up here. The question I asked myself is: What do you finally want to do? The answer is, that I simply want to be able to "extend" all my UserControls (or Components) in general (namely regarding all UserControls) and also specifically (namely regarding TextBox, Combobox, ListView, etc), so that when I create a MyTextBox object, I could do both:
myTextBox.MyUserControlStuff();
and
myTextBox.TextBoxStuff();
So here are my solutions:
1. Use extensions
and extend UserControl (or Component) class. In that case, I have:
public class MyTextBox : TextBox
{
public void MyTextBoxStuff()
{
...
}
}
public static class UserControlExtensions
{
public static void MyUserControlStuff(this UserControl control)
{
...
}
}
And MyTextBox can also do all UserControl stuff and TextBox stuff
Downside: every (specific) extension method applies to UserControl also.
2. Use an interface
public class MyTextBox : TextBox, IUserControlStuff
{
public void MyTextBoxStuff()
{
...
}
public void MyUserControlStuff()
{
...
}
}
public interface IUserControlStuff
{
public void MyUserControlStuff();
}
Downside: I will have to re-implement MyUserControlStuff each time again and again.
3. Use an "extra code" class and make it a member of each custom UserControl & MyUserControl (like #Mr.Karlsen suggested)
See the answer of #Mr.Karlsen for more details. I would say this is the best solution, but it has its downside
Downside: A little messy. A developer, new to my project would find it difficult to understand when seeing it for the first time. I would personally avoid it.
Finally I decided to go with the interface, because my UserControl properties/methods are specific to my needs so it wouldn't be good to extend UserControl with "specific" stuff. I have to write more code in my case and even re-write the same code while implementing my interface's methods, which I really hate. Anyway!!

Related

Circular dependency when adding reference (Delegates)

Basically I have 2 projects, a form and a user control.
I need both of them to be in different projects but the form need to refer to the user control as it is using the user control. And the user control will need to refer to the form as it is using one of the form class. When I add the second one because it need the , VS will complain circular dependency which is understandable. How do I solve this?
Logically the form should depend on the user control. You could create an interface to replace the form within the user control project, and then have the form implement that interface.
Example user control project;
public interface IForm
{
string MyString { get; }
}
public class MyUserControl : UserControl
{
public IForm Form { get; set; }
private void ShowMyString()
{
String myString = Form.MyString;
...
}
}
Example Form project
public class MyForm : Form, IForm
{
public MYString { get "My String Value"; }
}
I think the root cause of your problem is that you haven't separated your concerns between the form and the control properly.
Since you have a (somewhat generic) control, it shouldn't depend on the form. All of the logic of the control should reside within the control itself. The form should only black-box consume the control: add it, set public fields, call public methods, etc. anything else is a violation of encapsulation.
Sometimes, controls may need to know things about their parent form. In this case, I would suggest something as simple as adding a Parent field to the child control.
if you need something more specific from the form, you can always add an interface; the interface should only list those things that the control needs from the form. For example, if you need the size, you can add:
public interface IControlParent {
int Width { get; }
int Height { get; }
}
This way, you clearly see the dependencies (what the control needs from the parent), and if the parent type/contract changes, you don't need to do as much to change your control class.
You must sepárate your code, its never a good idea to have a reference to an application assembly, if you try to reuse it in the future, the applications exe should go with the control.
So, take the class from the form project and move it to the control project or create a library project, put the class on it and reference it from your control and your app projects.
You should use an event (delegate). Let's assume that inside your form project you created one class: Form1. And inside user control you defined UserControl1.
UserControl1 needs to instantiate and call a method from Form1:
public class Form1
{
public void Execute(string sMessage)
{
Console.WriteLine(sMessage);
Console.ReadLine();
}
}
UserControl1:
public class UserControl
{
public Func<object, object> oDel = null;
public void Execute()
{
oDel?.Invoke("HELLO WORLD!");
}
}
And from the class that instantiate UserControl, let's call it ParentClass:
public class ParentClass
{
public void Execute()
{
UserControl oUserControl = new UserControl();
oUserControl.oDel = Form1Action;
oUserControl.Execute();
}
public object Form1Action(object obj)
{
string sObj = Convert.ToString(obj);
Form1 oForm = new Form1();
oForm.Execute(sObj);
return null;
}
}
This approach gives the responsibility of handling an event to the high level class.

How do I ensure a Class can call a method on another Class, but not other Classes can call that method?

I have two classes that I'd like to keep in separate files.
namespace GridSystem
{
public class Grid
{
public void AddItem(GridItem item)
{
item.InformAddedToGrid();
}
}
}
namespace GridSystem
{
public class GridItem
{
public void InformAddedToGrid()
{
Debug.Log("I've been added to the grid");
}
}
}
How do I ensure no other classes are allowed to call InformAddedToGrid?
I'm trying to emulate Actionscript namespaces, which can be used on a method, in place of public, private, internal, etc. It doesn't exactly protect the method, but forces an extra step of including the namespace before the method can be accessed. Is there an alternative approach to this in C#?
If GridItem itself can be hidden from the outside world as well I would consider putting GridItem inside Grid as a nested class. That way it won't be visible outside of the class
http://www.codeproject.com/Articles/20628/A-Tutorial-on-Nested-Classes-in-C
Not that you should do this, you should do what TGH suggests, have a public interface for GridItem, and have gridItem nested in Grid (then have a factory method on Grid to create Items and use partial Grid class to have them in separate files).
Because there isn't a way of having friend methods ( you can do friend classes through InternalsVisibleToAttribute )
You COULD do this ( but don't... )
public partial class Grid
{
public void AddItem(GridItem item)
{
item.InformAddedToGrid();
}
}
public class GridItem
{
public void InformAddedToGrid()
{
if (new StackTrace().GetFrame(1).GetMethod().DeclaringType !=
typeof(Grid)) throw new Exception("Tantrum!");
Console.WriteLine("Grid called in...");
}
}
then
var g = new Grid();
g.AddItem(new GridItem()); // works
new GridItem().InformAddedToGrid(); // throws a tantrum...
A really ugly answer would be to make it private and use reflection.
Another ugly answer would be to make it throw an exception if the caller is wrong.
Both of these are much slower to execute than a normal call also.
I don't think there's a good answer. C# doesn't have friends.
IMHO the answer is simple: access modifiers are just there to remind the programmer of the intent of how public/private a class should be. Through reflection you can lift those barriers.
The usage you make of a class is all in your hands: if your class is meant to only be used in one place, make it so. If anything, if a class has a special way of being used, document it - put it in the XML comments.
That said, in this specific example I'd believe since the GridItem doesn't add itself to the grid, it's not its job to notify about it (what if "I've not been added to the grid"?). I think InformAddedToGrid belongs somewhere in your Grid class as a private method, where there's a concept of adding an item... assuming that's what AddItem(GridItem) really does.
You can do it as TGH suggested, with nested classes, except the other way around. Nest Grid within GridItem and make InformAddedToGrid private. Here I use a nested base class so the public API can remain the same. Note that no one outside of your assembly can inherit from GridBase because the constructor is internal.
public class GridItem
{
public class GridBase
{
internal GridBase() { }
public void AddItem(GridItem item)
{
item.InformAddedToGrid();
}
}
private void InformAddedToGrid()
{
Debug.Log("I've been added to the grid");
}
}
public class Grid : GridItem.GridBase { }
Another option is to have GridItem explicitly implement an internal interface. This way no one outside of your assembly can use the interface by name and therefore cannot call InformAddedToGrid.
public class Grid
{
public void AddItem(GridItem item)
{
((IGridInformer)item).InformAddedToGrid();
}
}
public class GridItem : IGridInformer
{
void IGridInformer.InformAddedToGrid()
{
Debug.Log("I've been added to the grid");
}
}
internal interface IGridInformer
{
void InformAddedToGrid();
}

Inconsistent Accessibility Error in c# can't call base

I am creating an application with a lot of forms and the visual style required takes a lot time to be applied from the designer, so I created a class called Layout to apply these
property changes to every form on its Load() method.
class Layout : Form
{
public void ApplicarLayout(Form frm)
{
frm.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(30)))), ((int)(((byte)(30)))));
foreach (Control c in frm.Controls)
{
if (c is TextBox)
{
//Apply textBox Formatting
}
//Iterate through the controls in the form and add respective format
}
}
}
So far, so good. My plan was to then inherit this class from every form and just call the base.AplicarLayout() method.However, I get the error:
Inconsistent accessibility: base class 'EntityClub_.Layout' is less accessible than class 'EntityClub_.MainAdminWindow'
Here you can see how I do it.
public partial class MainAdminWindow : Layout
{
public MainAdminWindow()
{
InitializeComponent();
}
public void MainAdminWindow_Load(object sender, EventArgs e)
{
base.ApplicarLayout(this);//ERROR HERE
}
}
Do you know how can I do this using inheritance? I don't want to instance the class and I don't want to pollute each window´s code with the layout method.
Classes without an explicit access modifier (and aren't nested) are implied to be internal.
Therefore:
class Layout : Form
..is internal, whereas:
public partial class MainAdminWindow : Layout
..is public (because you've explicitly said so). Changing Layout's declaration to this solves the issue:
public class Layout : Form

Windows Phone 7 - Access MainPage UI control from Class

I need to access control of MainPage.xaml.cs from another class. How can I access it?
The question is why? There are a couple of approaches, depending on your architecture:
First thing you can do is to make your MainPage singleton. It makes sense because you only have one Main Page in reality too, but I don't like singletons, and it makes your components coupled and your design becomes hard to unit test.
Alternatively, you can pass an interface of your MainPage into your class. If you only pass the interface, you then have the chance to do unit testing without too much trouble. Something like this:
public interface IMainView
{
void MethodOnMainPage();
}
public class MainPage : IMainView
{
}
public class MyClass
{
private IMainView _view;
public MyClass(IMainView view)
{
_view = view;
}
private void SomeEventHappened()
{
_view.MethodOnMainPage();
}
}

C# how to implement databinding without Control?

Is there a simple way to implement databinding when neither of both classes is of type Control?
In my case, I would like to bind a variable to a property of a custom ToolStripButton.
EDIT for clarification: when binding to a Control, I can use Control's DataBindings collection. However, I am searching for a way to bind properties regardless of the source and target Type.
EDIT: using winforms
You can probably do this by using Truss.
Truss provides WPF-style databinding for any class that implements INotifyPropertyChanged. It gives you a bit more flexibility in this, since it doesn't restrict the classes to being derived from a specific base class.
I use this Implemetation of IBindableComponent on the ToolStripButton, found here. The BindableToolStripButton allows you to use Databinding like with a normal Control.
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)]
public class BindableToolStripButton : ToolStripButton, IBindableComponent
{
public BindableToolStripButton()
: base() { }
public BindableToolStripButton(String text)
: base(text) { }
public BindableToolStripButton(System.Drawing.Image image)
: base(image) { }
public BindableToolStripButton(String text, System.Drawing.Image image)
: base(text, image) { }
public BindableToolStripButton(String text, System.Drawing.Image image, EventHandler onClick)
: base(text, image, onClick) { }
public BindableToolStripButton(String text, System.Drawing.Image image, EventHandler onClick, String name)
: base(text, image, onClick, name) { }
#region IBindableComponent Members
private BindingContext bindingContext;
private ControlBindingsCollection dataBindings;
[Browsable(false)]
public BindingContext BindingContext
{
get
{
if (bindingContext == null)
{
bindingContext = new BindingContext();
}
return bindingContext;
}
set
{
bindingContext = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ControlBindingsCollection DataBindings
{
get
{
if (dataBindings == null)
{
dataBindings = new ControlBindingsCollection(this);
}
return dataBindings;
}
}
#endregion
}
Assuming you have a class MyClass implementing INotifyPropertyChanged, use it just like you would when binding to a control property:
bindableToolStripButton1.DataBindings.Add("Enabled", myClass1, "MyBooleanProperty");
Use dependency properties (your property in your ToolStripButton should be) and create a property for your variable in your other class and create a binding and set it to the property of your ToolstripButton.
I guess that's about the easiest way to do it.
EDIT: That's only for WPF...
Else implement INotifyPropertyChanged and when your variable changes, it should automatically change in your ToolStripButton.
For similar behaviour like Controls being bound to object properties, for any Type you can implement the same interfaces.
Based on that thought, you can subclass ToolStripButton (or desired Type to have bindings) and implement IBindableComponent for it. This works for all kinds of source and target Types as long as they're not sealed. For example, your tool strip button:
public class BindableToolStripButton : ToolStripButton, IBindableComponent {
//...
This will cause the BindableToolStripButton to have its own .DataBindings property whereas the base ToolStripButton class doesn't have such a propery.
You would need to follow through on filling out implementation details using examples seen here from Microsoft for ISite, IBindableComponent, IComponent and any inherited interfaces.
Then you would add Binding instances to any instance of BindableToolStripButton.
(Note: I only have fragements so will make my first community wiki post - and we'll see how that goes... )
I written some basic databinding stuff through reflection. It works on any object and doesn't need to implement something special (no INotifyPropertyChanged, it just works) it is part of my editor at http://github.com/filipkunc/opengl-editor-cocoa look at HotChocolate/Bindings (like re-implementation of Cocoa KVC, KVO into .NET) folder. You can see it in action in HotChocolateTest project.
There is another quick and simple solution which consists in creating properties in the Form, and bind them:
public MyForm : Form
{
...
public bool CanDelete
{
get { return deleteToolStripButton.Enabled; }
set { deleteToolStripButton.Enabled = value; }
}
public MyForm()
{
...
this.DataBindings.Add("CanDelete", this.MyModel, "DeleteAllowed",
false, DataSourceUpdateMode.Never);
...
}
}
Assuming that MyModel contains a DeleteAllowed property which notifies its changes.

Categories