Question

Anyone know how I can dynamically load a control inside of a shared/static function? The function itself is inside of a mustinherit/abstract class. (It's an ASP.NET project in VB) I want to do something like this:
VB:

    Public Shared Function GetWidget(ByVal name As WidgetName) As Control
        Select Case name
            Case WidgetName.Name1
                Return LoadControl("~/Control1.ascx")
            Case WidgetName.Name2
                Return LoadControl("~/Control2.ascx")
            Case WidgetName.Name3
                Return LoadControl("~/Control3.ascx")
        End Select
    End Function

my C# is a little rusty, so this might have some syntax errors:

Public Static Control GetWidget(WidgetName name)  
{  
    switch (name)  
    {  
        Case WidgetName.Name1:  
            return LoadControl("~/Control1.ascx");  
            break;  
        Case WidgetName.Name2:  
            return LoadControl("~/Control2.ascx");  
            break;  
        Case WidgetName.Name3:  
            return LoadControl("~/Control3.ascx");  
            break;  
    }  
}  

(Where WidgetName is an enumerator.)

I'm getting "Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class.", but I don't understand this error. I understand what it means, I just don't understand why calling LoadControl isn't seen by the compiler as being an explicit instance of the class. What's not explicit about using LoadControl to create a new control from a file? I tried creating a new user control and initializing it, then setting it to a different control with LoadControl to no avail. I also don't want to do a DirectCast because I'm trying to put this in a shared, mustinheret (abstract) class, which therefore doesn't have an .aspx file to write in a <%@ Reference Control="~/SomeControlPath.ascx" %>, so the class name is unavailable.

What I'm trying to do is write a static function that takes some value and returns a control based only on that control's source file location. The end result is a user-modifiable list of controls. They get a column of controls that they freely add, remove, or reorder based on a static list of available child controls that I specify. I'm not married to this approach; it might be really wrong in more ways than one.

yeah, I know the static strings being there is code smell, it doesn't actually look like that; it's a simplification for the sake of asking the question.

C#, VB, or plain English explanations all welcome.

Was it helpful?

Solution

Is it because LoadControl can't be returned from your class? Can you try this instead...

    Protected Static string GetWidget(WidgetName name)  
    {  
        switch (name)  
        {  
            Case WidgetName.Name1:  
                return "~/Control1.ascx";  
                break;  
            Case WidgetName.Name2:  
                return "~/Control2.ascx";  
                break;  
            Case WidgetName.Name3:  
                return"~/Control3.ascx";  
                break;  
      }  
 } 

and call the method like

... = LoadControl(GetWidget(name));

I suspect that the other alternative is to cast the control

Control c;
...
Case ...
   c = (ControlName)LoadControl("~/Control1/.ascx");
   break;
...
return c;

However the calling code would still need to cast it back to its type...

OTHER TIPS

Actually, you can do it like this (it works):

UserControl tmp0 = new UserControl();
Control ctl = tmp0.LoadControl("MyControl.ascx");

LoadControl is an instance method on the TemplateControl class which the Page class inherits from, and you do not have an instance of the Page class inside your static method (there is no this object because it is a static method).

All good information here, but I'm surprised that no one has used it to make the leap to an actual solution to the original question:

Public Shared Function GetWidget(ByVal name As WidgetName,
                                 ByVal onTemplate As TemplateControl) As Control
    Select Case name
        Case WidgetName.Name1
            Return onTemplate.LoadControl("~/Control1.ascx")
        Case WidgetName.Name2
            Return onTemplate.LoadControl("~/Control2.ascx")
        Case WidgetName.Name3
            Return onTemplate.LoadControl("~/Control3.ascx")
    End Select
End Function

I tested this solution and it woks.

I like it better than Rippo's solution because it prevents the caller from having to worry about the LoadControl details. And this is definitely a better solution if there is more work to be done to the resulting control other than simply returning it.

I would not have reached this solution without CSharpAtl's answer, though, because I (stupidly) did not realize that LoadControl is a method on TemplateControl. Like the original poster, I was very confused about why I was getting the same error. I could not understand why it would not be OK to load a control in a shared method, and in fact, it is OK if you know how to call LoadControl in this context!

Brian

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top