Using C# to recursively get a collection of controls from a controlcollection - c#

Currently I am trying to extract a collection of dynamically created controls (checkboxes and dropdownlists) from a recursive control collection (repeater). This is the code I am using.
private void GetControlList<T>(ControlCollection controlCollection, ref List<T> resultCollection)
{
foreach (Control control in controlCollection)
{
if (control.GetType() == typeof(T))
resultCollection.Add((T)control);
if (control.HasControls())
GetControlList(controlCollection, ref resultCollection);
}
}
I am having problems with the following line:
resultCollection.Add((T)control);
I get the error ...
Cannot convert type 'System.Web.UI.Control' to 'T'
Any ideas?

Problem:
Since T can be a reference type or a value type, compiler needs more information.
You can not convert and Integer to Control.
Solution:
To fix this, add where T : Control or where T : class (a more general) constraint to state that T will always be a reference type.
Example:
private void GetControlList<T>(ControlCollection controlCollection, ref List<T> resultCollection)
where T : Control
{
foreach (Control control in controlCollection)
{
//if (control.GetType() == typeof(T))
if (control is T) // This is cleaner
resultCollection.Add((T)control);
if (control.HasControls())
GetControlList(control.Controls, ref resultCollection);
}
}
You also don't need ref keyword. Since, List is a reference type, it's reference will be passed.

Change it to
var c = control as T;
if (c != null)
resultCollection.Add(c);
This will be faster than your cod, since it doesn't call GetType().
Note that it will also add controls that inherit T.
You'll also need to constrain the type parameter by adding where T : Control.

Related

Get a list of forms that doesn't inherit another form

I have a base form (BaseForm) which needs to be inherited from all the forms in the project. Some of them inherit it, others not. How can I get a list of all those forms who currently don't inherit it because we have a lot of forms and will be time consuming to go and check if each one of them inherits this form? Maybe some reflection ?
Use the check Type.IsSubclassOf().
Assembly assembly = Assembly.GetExecutingAssembly();
Type baseType = typeof(BaseForm);
foreach (Type type in assembly.GetTypes().Where(t => (t.FullName.Contains("Form") && (t != baseType))))
{
if (type.IsSubclassOf(typeof(BaseForm)))
Console.WriteLine("{0} is subclass of {1}", type, baseType);
}
Type formType = typeof(Form);
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
if (formType.IsAssignableFrom(type))
{
// print the list of forms
}

Type of params in c# methods

I have a method
public string GetValue(TextBox txt, DropdownList ddl)
{
if(txt != null)
return txt.text;
else
return ddl.SelectedValue;
}
I can only pass either textbox or ddl and null for the other param. How can I resolve this issue?
Dynamically i call that method and at a time either TextBox or DDl exists. So based on that i have to return value from That Control.
I am getting some error message as Method has invalid arguments when i pass Null.
I can only pass either textbox or ddl and null for the other param. How can I resolve this issue?
If this is the business rule you want to enforce, you don't resolve it, you change it. Use method overloading to only accept the type of parameter you actually want to use.
public string GetValue(TextBox tb)
public string GetValue(DropDownList ddl)
I don't think you should be doing a method like this.
From the signature you have some code like
var value = GetValue(txtMyText, null);
which sets value to txtMyText.Text;
OR you have
var value = GetValue(null, ddlMyList);
which sets value to ddl.SelectedValue;
The problem here is that this removes readability. When I'm reading your code I see that you're doing a GetValue() but I'm at a loss for why you're passing in null in some parameters.
It's actually fairly clear, when reading code to just see:
var value = txtMyTextBox.Text;
var dropDownValue = ddlMyList.SelectedValue;
Making this method isn't really all that useful, because you have to handle each and every control type. The classes already have methods to get their value, and you trying to write a utility method that will get a value regardless of class type obscures what is really going on with little value.
Further, if you add more types to this method, you'll end up doing an If/Else until you find the type, and return the value. This causes unnecessary CPU cycles, especially since you will already know the type at design time (since you're passing in null for one parameter.)
if you want to pass many parameters you can use param keyword
public string GetValue(params object[] controls)
{
foreach (var item in controls)
{
if (item != null)
{
if (item is TextBox)
return (item as TextBox).Text;
if (item is CheckBox)
return (item as CheckBox).Checked.ToString();
if (item is DropDownList)
return (item as DropDownList).SelectedValue.ToString();
}
}
return string.Empty;
}
and call the method as this
GetValue(TextBox1);
GetValue(DropDownList1);
GetValue(CheckBox1);
GetValue(null,DropDownList1);
GetValue(TextBox1,DropDownList1);
GetValue(TextBox1,DropDownList1,CheckBox1);
Every Control inherits the class control. So one parameter is enough. You just have to determine the type:
public string GetValue(Control ctl) {
if (ctl != null) {
//Textbox
if (ctl is TextBox) return ctl.Text;
//Combobox
if (ctl is ComboBox) {
ComboBox cb = ctl as ComboBox;
return cb.SelectedText;
}
//...
}
//Default Value (You can also throw an exception
return "";
}

How can I get all the controls in a namespace?

How can I get all the controls in a namespace? For example, I want to get the controls in System.Windows.Forms: TextBox, ComboBox etc.
The notion of a control in a namespace is a bit unclear. You could use reflection to get classes in an assembly in a given namespace which derive from a particular base type. For example:
class Program
{
static void Main()
{
var controlType = typeof(Control);
var controls = controlType
.Assembly
.GetTypes()
.Where(t => controlType.IsAssignableFrom(t) &&
t.Namespace == "System.Windows.Forms"
);
foreach (var control in controls)
{
Console.WriteLine(control);
}
}
}
this will return all classes in a specified namespace :
string #namespace = "System.Windows.Forms";
var items = (from t in Assembly.Load("System.Windows.Forms").GetTypes()
where t.IsClass && t.Namespace == #namespace
&& t.IsAssignableFrom(typeof(Control))
select t).ToList();
Your form object has a Controls member, which is of type ControlCollection. It is essentially a list (with some other interfaces under the hood) of all of the controls.
EDIT: As per your comment, you need to cast the control back into a textbox. First you must identify it as a control.
foreach (var control in controls)
{
if(control is TextBox)
{
(control as TextBox).Text = "Or whatever you need to do";
}
}

get attribute information

My UI is build with lots of user controls that are translatable. Some controls in the usercontrols shouldn't be translated and I want to tag them as such with a [DoNotTranslate]` custom attribute.
in my userControl.designer.cs file
[DoNotTranslate]
private DevExpress.XtraEditors.LabelControl maxLabel;
[DoNotTranslate]
private DevExpress.XtraEditors.LabelControl valueLabel;
//all other controls
The translation function expects a (user)control and then goes through all the child control.Controls to make sure all controls are translated without the need to call the translation function on every single control.
Is it possible to find out if a control has my custom attribute set? The problem is that I don't see how I can get the attribute information in the translation function when i go through all the Controls.
Any advice is greatly appreciated,
Thanks
EDIT: I see now from the code you posted that the attribute is on a property of the control, not on the class defining the control itself. You can try like this:
public IEnumerable<PropertyInfo> GetNonTranslatableProperties(WebControl control)
{
foreach (PropertyInfo property in control.GetType().GetProperties())
{
if(
property
.GetCustomAttributes(true)
.Count(item => item is DoNotTranslateAttribute) > 0)
yield return property;
}
}
otherwise, you can subclass Label to a NonTranslatableLabel, apply the attribute to the class and use it instead of Label in your "father" control.
[NonTranslatable]
public class NonTranslatableLabel : Label
===================
for each of your controls you can do:
myCustomControl.GetType()
.GetCustomAttributes(true)
.Where(item => item is DoNotTranslateAttribute);
for instance you could enumerate all your "non-translatable" controls like this:
public IEnumerable<Control> GetNonTranslatableChildren(Control control)
{
foreach(Control c in control.Controls)
{
if(
c.GetType()
.GetCustomAttributes(true)
.Count(item => item is DoNotTranslateAttribute) > 0)
yield return c;
}
}
You can use GetCustomAttributes method to find out if the attribute has been applied or not. For example,
static readonly Type _DoNotTranslateAttribute = typeof(DoNotTranslate);
.... // other code
var t = control.GetType();
if (t.GetCustomAttributes(_DoNotTranslateAttribute, false).length > 0)
{
// do not translate
}
(disclaimer: untested/uncompiled code- just to give an idea how to use the function)

Finding controls that use a certain interface in ASP.NET

Having a heckuva time with this one, though I feel I'm missing something obvious. I have a control that inherits from System.Web.UI.WebControls.Button, and then implements an interface that I have set up. So think...
public class Button : System.Web.UI.WebControls.Button, IMyButtonInterface { ... }
In the codebehind of a page, I'd like to find all instances of this button from the ASPX. Because I don't really know what the type is going to be, just the interface it implements, that's all I have to go on when looping through the control tree. Thing is, I've never had to determine if an object uses an interface versus just testing its type. How can I loop through the control tree and yank anything that implements IMyButtonInterface in a clean way (Linq would be fine)?
Again, know it's something obvious, but just now started using interfaces heavily and I can't seem to focus my Google results enough to figure it out :)
Edit: GetType() returns the actual class, but doesn't return the interface, so I can't test on that (e.g., it'd return "MyNamespace.Button" instead of "IMyButtonInterface"). In trying to use "as" or "is" in a recursive function, the type parameter doesn't even get recognized within the function! It's rather bizarre. So
if(ctrl.GetType() == typeToFind) //ok
if(ctrl is typeToFind) //typeToFind isn't recognized! eh?
Definitely scratching my head over this one.
Longhorn213 almost has the right answer, but as as Sean Chambers and bdukes say, you should use
ctrl is IInterfaceToFind
instead of
ctrl.GetType() == aTypeVariable
The reason why is that if you use .GetType() you will get the true type of an object, not necessarily what it can also be cast to in its inheritance/Interface implementation chain. Also, .GetType() will never return an abstract type/interface since you can't new up an abstract type or interface. GetType() returns concrete types only.
The reason this doesn't work
if(ctrl is typeToFind)
Is because the type of the variable typeToFind is actually System.RuntimeType, not the type you've set its value to. Example, if you set a string's value to "foo", its type is still string not "foo". I hope that makes sense. It's very easy to get confused when working with types. I'm chronically confused when working with them.
The most import thing to note about longhorn213's answer is that you have to use recursion or you may miss some of the controls on the page.
Although we have a working solution here, I too would love to see if there is a more succinct way to do this with LINQ.
You can just search on the Interface. This also uses recursion if the control has child controls, i.e. the button is in a panel.
private List<Control> FindControlsByType(ControlCollection controls, Type typeToFind)
{
List<Control> foundList = new List<Control>();
foreach (Control ctrl in this.Page.Controls)
{
if (ctrl.GetType() == typeToFind)
{
// Do whatever with interface
foundList.Add(ctrl);
}
// Check if the Control has Child Controls and use Recursion
// to keep checking them
if (ctrl.HasControls())
{
// Call Function to
List<Control> childList = FindControlsByType(ctrl.Controls, typeToFind);
foundList.AddRange(childList);
}
}
return foundList;
}
// Pass it this way
FindControlsByType(Page.Controls, typeof(IYourInterface));
I'd make the following changes to Longhorn213's example to clean this up a bit:
private List<T> FindControlsByType<T>(ControlCollection controls )
{
List<T> foundList = new List<T>();
foreach (Control ctrl in this.Page.Controls)
{
if (ctrl as T != null )
{
// Do whatever with interface
foundList.Add(ctrl as T);
}
// Check if the Control has Child Controls and use Recursion
// to keep checking them
if (ctrl.HasControls())
{
// Call Function to
List<T> childList = FindControlsByType<T>( ctrl.Controls );
foundList.AddRange( childList );
}
}
return foundList;
}
// Pass it this way
FindControlsByType<IYourInterface>( Page.Controls );
This way you get back a list of objects of the desired type that don't require another cast to use. I also made the required change to the "as" operator that the others pointed out.
Interfaces are close enough to types that it should feel about the same. I'd use the as operator.
foreach (Control c in this.Page.Controls) {
IMyButtonInterface myButton = c as IMyButtonInterface;
if (myButton != null) {
// do something
}
}
You can also test using the is operator, depending on your need.
if (c is IMyButtonInterface) {
...
}
Would the "is" operator work?
if (myControl is ISomeInterface)
{
// do something
}
If you're going to do some work on it if it is of that type, then TryCast is what I'd use.
Dim c as IInterface = TryCast(obj, IInterface)
If c IsNot Nothing
'do work
End if
you can always just use the as cast:
c as IMyButtonInterface;
if (c != null)
{
// c is an IMyButtonInterface
}

Categories

Resources