Thursday, April 23, 2009

Using Generics To Perform a Dynamic Cast in .NET WinForms

By Andrew D Green

UPDATE: I modified the article after tweaking the code some to make it more efficient. Instead of passing the entire parent control, I simply pass the control collection associated to the parent.

This is a solution I came up with to handle the issue of dynamic casting of controls. In this case I had a situation where I needed to be able to specify a control or form parent control collection and the name of the control I wanted to emulate and access the properties of. I wanted to be able to use a dynamic object to access the properties of an object embedded within a form without having to hard code any specific values associated to identifying that control.

Here's the actual class that implements the generic type that I will use to cast a located control from. The generic variable is itemType. I loop through the parent collection of controls and locate the control I seek to emulate as a Control type. Once found, perform a DirectCast to cast from Control type to the generic itemType. The function then returns the itemType specified in the object instantiation :

VB.NET
Public Class NewGenericControl(Of itemType As {Control, New})

Public Sub New()

End Sub


Public Function GetControlByName(ByVal controls As Control.ControlCollection, ByVal ctlName As String) As itemType
Dim newCtl As New itemType
' Loop trhough the controls on the parent to locate the desired control to manage
For Each ctl As Control In controls
If (ctl.Name.StartsWith(ctlName)) Then
newCtl = DirectCast(ctl, itemType)
Return newCtl
End If
Next ctl

Return newCtl

End Function


End Class


C#
public class NewGenericControl<itemType> where itemType : Control, new() 
{

public NewGenericControl()
{

}


public itemType GetControlByName(Control.ControlCollection controls, string ctlName)
{
itemType newCtl = new itemType();
// Loop trhough the controls on the parent to locate the desired control to manage
foreach (Control ctl in controls) {
if ((ctl.Name.StartsWith(ctlName))) {
newCtl = (itemType)ctl;
return newCtl;
}
}


return newCtl;
}


}


Here's the code that accesses the class by specifying a control type. It's only two lines of code. In this case I am emulating a PictureBox that I used to act as a button. As you can see, I'm accessing the Enabled property of the control itself through the generic instance of the control:

VB.NET
Dim splitContainer As New NewGenericControl(Of ucQuestionManager)

splitContainer.GetControlByName(Me.TabPage2.Controls, "ucQuestionManager").JumpToQuestion(questionId)


C#

NewGenericControl<ucQuestionManager> splitContainer = new NewGenericControl<ucQuestionManager>();

splitContainer.GetControlByName(this.TabPage2.Controls, "ucQuestionManager").JumpToQuestion(questionId);


Here's another example with a TreeView control that was embedded in a SplitContainer panel control inside the form:

VB.NET
Dim splitContainer As New NewGenericControl(Of SplitContainer)

Dim newTree As New NewGenericControl(Of TreeView)

newTree.GetControlByName(DirectCast(splitContainer.GetControlByName(m_parent.ParentForm.Controls, "SplitContainer1").Panel1, Control).Controls, "treeAnswerList").Nodes(Me.QuestionId).Nodes.Add(1, m_answerValue)


C#
NewGenericControl<SplitContainer> splitContainer = new NewGenericControl<SplitContainer>(); 

NewGenericControl<TreeView> newTree = new NewGenericControl<TreeView>();

newTree.GetControlByName(((Control)splitContainer.GetControlByName(m_parent.ParentForm.Controls, "SplitContainer1").Panel1).Controls, "treeAnswerList").Nodes(this.QuestionId).Nodes.Add(1, m_answerValue);



As you can see you can directly access embedded controls even by passing in an emulated instance of the parent container control! Sweet...

1 comment:

Pancit said...

What a freaking nerd! :-P