Generic All Controls Method

Generic All Controls Method

The problem is that Concat would want an IEnumerable<T> as well - not an IEnumerable<Control>. This should work though:

public static IEnumerable<T> GetAll<T>(this Control control) where T : class
{
var controls = control.Controls.Cast<Control>();

return controls.SelectMany(ctrl => GetAll<T>(ctrl))
.Concat(controls.OfType<T>()));
}

Generic method to get all controls of type T

The "magic" is simply declaring another method that returns the List<T> rather than void.

List<T> GetAllControlsOfType<T>(Control parent) where T : class {
List<T> list = new List<T>();
GetAllControlsoFType<T>(list, parent); // Invoke your existing method
return list;
}

Because you're using recursion, you can't simply modify your existing method to return List<T>, since doing so would make it impossible for you to keep track of that list and build on it.

A few other minor points:

  • You have:

    if (parent.GetType() == typeof(T))

    But it would be more clear to write it as:

    if (parent is T)

    Unless, of course, you truly want your method to fail when used with subclasses of T.

  • You may want to consider declaring the new method as an extension method by making parent declared as this Control parent (provided it's declared in a static class)

    This would allow you to invoke the method as this.GetAllControlsOfType<WebControl>()

Method with generic and multiple parameters

If we remember that the Form class derives from Control (indirectly, by deriving from ContainerControl which derives from ScrollableControl, which derives from Control), and the Enabled property belongs to the Control class, we can write a method that will enable any control's children (including the Form or TableLayoutPanel controls), since the Controls collection also belongs to the Control class:

public static void EnableChildren(Control control, bool enabled = true)
{
foreach (Control child in control.Controls)
{
child.Enabled = enabled;
}
}

And then if we also want to be able to use this with a collection of controls (as in your example), we can write an overload that takes a collection:

public static void EnableChildren(IEnumerable<Control> controls = null, 
bool enabled = true)
{
if (controls == null) return;

foreach (var control in controls)
{
EnableChildren(control, enabled);
}
}

Now we can use this with a Form or a collection of TableLayoutPanel controls (or any control that has controls in it's Controls collection).

Examples of usage:

var myForm = new Form1();

EnableChildren(this); // 'this' is the current form
EnableChildren(myForm); // a separate instance of a form control
EnableChildren(tableLayoutPanel1, false); // A single TableLayoutPanel control

var tableLayoutPanels = new [] {tableLayoutPanel1, tableLayoutPanel2, tableLayoutPanel3};
EnableChildren(tableLayoutPanels); // An array of tableLayoutPanel controls

Generic method to find all TextBox controls in Silverlight

It sounds like you need a recursive routine like GetTextBoxes below:

void Page_Loaded(object sender, RoutedEventArgs e)
{
// Instantiate a list of TextBoxes
List<TextBox> textBoxList = new List<TextBox>();

// Call GetTextBoxes function, passing in the root element,
// and the empty list of textboxes (LayoutRoot in this example)
GetTextBoxes(this.LayoutRoot, textBoxList);

// Now textBoxList contains a list of all the text boxes on your page.
// Find all the non empty textboxes, and put them into a list.
var nonEmptyTextBoxList = textBoxList.Where(txt => txt.Text != string.Empty).ToList();

// Do something with each non empty textbox.
nonEmptyTextBoxList.ForEach(txt => Debug.WriteLine(txt.Text));
}

private void GetTextBoxes(UIElement uiElement, List<TextBox> textBoxList)
{
TextBox textBox = uiElement as TextBox;
if (textBox != null)
{
// If the UIElement is a Textbox, add it to the list.
textBoxList.Add(textBox);
}
else
{
Panel panel = uiElement as Panel;
if (panel != null)
{
// If the UIElement is a panel, then loop through it's children
foreach (UIElement child in panel.Children)
{
GetTextBoxes(child, textBoxList);
}
}
}
}

Instantiate an empty list of TextBoxes. Call GetTextBoxes, passing in the root control on your page (in my case, that's this.LayoutRoot), and GetTextBoxes should recursively loop through every UI element that is a descendant of that control, testing to see if it's either a TextBox (add it to the list), or a panel, that might have descendants of it's own to recurse through.

Hope that helps. :)

Generic method to set Control values

Be careful what you wish for...

No error control whatsoever, but here goes the generics version of your 2nd code block.

static class ControlAssign
{

public static void Assign(Control target, object source, PropertyInfo prop)
{
Setters[prop.PropertyType](prop, source, target);
}

static ControlAssign()
{
Setters[typeof(string)] = (prop, src, target) =>
{
((TextBox)target).Text =
(string)prop.GetValue(src, null);
};

Setters[typeof(bool?)] = (prop, src, target) =>
{
((CheckBox)target).Checked =
(bool)prop.GetValue(src, null);
};

Setters[typeof(bool)] = (prop, src, target) =>
{
((CheckBox)target).Checked =
(bool)prop.GetValue(src, null);
};
}

public delegate void Action<T, U, V>(T t, U u, V v);

readonly static Dictionary<Type, Action<PropertyInfo, object, Control>> Setters = new Dictionary<Type, Action<PropertyInfo, object, Control>>();
}

How can I write a generic method to add any form to a tab control and set properties on those forms?

You can use either of these options:

public void AddFormAsTab<T>() where T : Form, new()
{
var f = new T();
f.TopLevel = false;
//...
}

Usage:

AddFormAsTab<Form1>();

Or

public void AddFormAsTab(Form f)
{
f.TopLevel = false;
//...
}

Usage:

AddFormAsTab(new Form1());

Generic control to have inherit methods in WinForm

The inheritance and designer in Windows Forms is a problem.

I have a Form with an splitter, two listboxes and some other controls. That form is used to translate (map) some items. You select one item at left, one at right and click button to match. They are the same item in different providers.

I have another provider that require some extra controls to do the translation. May be 90% or more of the code is the same, but I need some extra for this provider.

The options that I saw:

  • Add these extra controls (protected or public) to the Form, hidden by default and without use. In Form derived class, you use them. You haven't the designer in derived Form, but you don't need because controls are in base Form. The problem with this approach is that the designer part of inheritance of derived Form is in base Form. It's a nonsense. I don't recomend this option.

  • Don't use the designer in derived Form. Starting in the previous point, copy the designer code added for your derived Form into your derived Form and leave your base Form as at first stage, without nothing of derived Form. You don't use the designer but you can use it temporary, copy/paste and have a good inheritance... without the designer in derived Form. It's a good option if your derived Forms has few changes, few maintenance in the designer part.

  • You can "Add" some logic to your base Form to allow extensions. For example, below of the ListBox, I can add a Panel (hidden by default) and some methods like ShowLeftPanel/ShowRightPanel. By default, these panels aren't used, but in derived class I can add an UserControl in left panel and show it. And that UserControl show the properties that I need to show in the special provider. Add some virtual methods for listbox selection changed, to update the UserControl. In this way, your UserControl has designer and also the base Form. You only need add some "extension points" in your form (a Panel, a Splitter...) and give some methods to interact with this parts of the base Form. And this is ok with inheritance because is something generic, like Tag property in controls.

UPDATE

Check this solution and tell me about it. Make your Forms like this:

public partial class MyForm1 : UserControl, IMyUserControl
{
private readonly MyOwnClass myClass;

public MyForm1(MyOwnClass myClass, MyMiddleClass myMiddle)
{
InitializeComponent();
this.myClass = myClass;
this.MyMiddle = myMiddle;
}

public MyMiddleClass MyMiddle { get; }
}

In this way, all your panel's forms are IMyUserControl:

public class MyUserControl : IMyUserControl
{
public MyMiddleClass MyMiddle { get; }
}

So, having any of your panel's form, you can cast to IMyUserControl and get the related MyMiddleClass having access to methods like MethodForAllChild:

public class MyMiddleClass
{
public void MethodForAllChild()
{
}
}

In your main form, you may have some property or method that give you access to your UserControl. Create a method that give you the middle instance of the current UserControl:

private MyMiddleClass GetMyMiddle()
{
UserControl userControl = GetYourMainFormCurrentUserControl();
IMyUserControl myUserControl = userControl as IMyUserControl;
return myUserControl?.MyMiddle;
}

And use it in your main form when you need:

MyMiddleClass myMiddle = GetMyMiddle();
if (myMiddle != null)
{
myMiddle.MethodForAllChild();
}

In this way, you only need implement the interface and add a property in your forms/usercontrols. In the main form you can get this middleclass and the code to reuse is only in that class and shared in all places. You don't need copy/paste if you add or change something in the middle class.

UPDATE 2

I'm going to explain in other form how it works because the code is written above. The goal is having the code only in one place, without duplicate it.

You define an interface in a very similar way as a class but without implementation (this is not really true in lastest C# versions but we can suppose that is without code). C# don't allow multiple inheritance but you can derive from a class and implement as many interfaces as you want.

When we define IMyUserControl we are telling that every class that implements IMyUserControl, has a property MyMiddle. When MyForm1 implements IMyUserControl, if you don't add the MyMiddle property, you get a compiler error. The key with this solution is that add and implement this interface in each form is very easy: add IMyUserControl, the property and a parameter in the constructor to set the property.

So, all your forms implements now IMyUserControl. I don't know where are your forms but it's sure that you have a way to get access to your UserControl. Maybe a variable or an array in which you add your user controls. You are working with them, so you can access to your user controls. Well, if you have an UserControl instance, and you know that your UserControl implements IMyUserControl, you can cast your UserControl to IMyUserControl and after the cast, you have access to the interface, in this case, to the MyMiddle property.

And we put in MyMiddle all the code that you want to share.

If you add some code of your main form, where you work with your forms, I can help you with the code. I haven't more code than existing in my answer.

C# Generic Method, create controls with arguments

You could do it by passing in an Action delegate, like this:

Create<Label>(label => label.Text = "Test");

public T Create<T>(Action<T> setup)
where T : Control, new()
{
T control = new T();
setup(control);
return control;
}

If you had multiple properties, the syntax would be:

Create<Label>(label => {
label.Text = "Test";
label.Color = Colors.Black;
});

Alternately, you could have multiple parameters:

Create<Label>(label => label.Text = "Test", label => label.Color = Colors.Black);

public T Create<T>(params Action<T>[] actions)
where T : Control, new()
{
T control = new T();
if (actions != null)
foreach (var action in actions)
action(control);
return control;
}

Can you use generic methods in a controller?

"Sort of" is the answer here. With generics, you must eventually define the underlying type somewhere and at some point or else it's all just theoretical. How would Web API or MVC route the data in your request (which is just QueryString GET or FormData POST key-value pairs) to a generic type and automatically infer the intended type? It cannot. What you could do is make a private generic method on the controller, but have the Action Methods of the controller resolve to concrete types which are passed to the private generic method.

You could also take a look at this SO answer as an alternative, convention-based approach. It should work pretty well for you assuming that you are specifying the concrete type of your generic controller as part of the URL, QueryString, or FormData.



Related Topics



Leave a reply



Submit