简体   繁体   中英

Using a user control in a base page class

I have a base page class that inherits from Page. We'll call it SpiffyPage.

I also have a user control called SpiffyControl.

I then have an aspx page, ViewSpiffyStuff.aspx, that inherits from SpiffyPage.

On page load, the SpiffyPage class is supposed to inject a SpiffyControl into the aspx page.

The problem is that SpiffyControl is a user control, and to instantiate that in the code behind one has to access the ASP namespace, sort of like:

ASP.controls_spiffycontrol_aspx MyControl;

But SpiffyPage isn't an aspx page, it's just a base page, and as such I can't access the ASP namespace, and I therefore can't instantiate a SpiffyControl in order to inject it.

How can I achieve my goal?

Edit:

One important point is that I must have a reference to the actual control type so I can assign values to certain custom Properties. That's why a function like LoadControl, which returns type Control, won't work in this case.

Add a baseclass to your SpiffyControl, like:

public class SpiffyBase : UserControl
{
    public void DoIt()
    {
        Response.Write("DoIt");
    }
}

and then you make your SpiffyControl inherit that baseclass, like:

public partial class SpiffyControl : SpiffyBase

then you should be able to do this from a Basepage class:

public class BasePage : Page
{
    protected override void OnLoad(EventArgs e)
    {
        var c = Page.LoadControl("SpiffyControl.ascx") as SpiffyBase;

        Page.Controls.Add(c);

        c.DoIt();
    }
}

I would implement SpiffyControl in a separate ASP.NET Server Control project, and reference that project from the web application. That way the control will not reside in the ASP namespace, but in the namespace of your choice and behave like any other server control.

Update: a nice side effect of putting the user controls into a separate project is that they become more decoupled from the web application as such, which in turn tends to make them more reusable and, depending on their design, also more testable.

Are you guys kidding me, I know that ASP.NET webforms was a painful experience but has nobody heard of the ClassName attribute, ie

<%@ Control ClassName="SpiffyControl" Language="C#" AutoEventWireup="true" CodeFile="SpiffyControl.ascx.cs" Inherits="Controls_SpiffyControl"  %>

Thus, the original example:

SpiffyControl spiffy = (SpiffyControl)LoadControl("~spiffy.ascx");

I am not sure if this will help you but there is a method called LoadControl that can load a user control dynamically using the usercontrol filename or type of the usercontrol. The method is in the System.Web.UI.TemplateControl class.

Control userControl= LoadControl("usercontrol.ascx");
panelControl.Controls.Add(userControl);

You can move your SpiffyControl into a library - this will give it a more precise namespace and also allow you to use it in other projects. This is a little complex, though - you have a UserControl already made, but it won't work in a library. UserControls use the codebehind model - they are composed of two files, one markup and one code. Libraries don't seem to support the codebehind model, so you can't use a UserControl. You need to convert that user control to a web control.

Create a new class library project. Open up the project's properties and set the AssemblyName and Default Namespace to SpiffyLibrary.

Now we need to convert your UserControl. First, create a class in your new library. Call it SpiffyControl, just like your existing user control, but make it inherit from System.Web.UI.WebControls.CompositeControl.

Second, open up the code for your existing user control and copy everything inside the class. Then go back to the new class and paste it all inside. If you're using any of the standard control events, you may need to rename those functions to override the appropriate events. For example,

protected void Page_Load(object sender, EventArgs e)
{
    ...
}

... should become ...

protected override void OnLoad(EventArgs e)
{
    ...

    base.OnLoad(e);
}

Third (this is the complex part) you need to reproduce all the markup you had in SpiffyControl's markup file. You can't just copy it - instead, you need to override the CreateChildControls() function which is inherited from CompositeControl. Every tag you had in the markup needs to be instantiated like a variable and added to the class's Controls collection. So for example, if your existing markup file looked like this:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="SpiffyControl.ascx.cs" Inherits="Controls_SpiffyControl" %>

<div id="Div1" runat="server">
    <asp:Label ID="TextOutput" runat="server" />
</div>

... then your new CreateChildControls function should look like this:

HtmlGenericControl Div1;
Label TextLabel;

protected override void CreateChildControls()
{
    Div1 = new HtmlGenericControl("div");
    this.Controls.Add(Div1);

    TextLabel = new Label();
    Div1.Controls.Add(TextLabel);

    base.CreateChildControls();
}

Note that the controls are declared as class fields; that way, they're visible to all the class methods.

Once you've converted your markup, compile the library project and add it to the references for your website project. Then open your website's web.config file and find the system.web / pages / controls section. This section should already have a tag like this:

<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

That tag is what lets you use controls from the ASP core libraries. So we're going to add one just like it, for our library:

<add tagPrefix="spiffy" namespace="SpiffyLibrary" assembly="SpiffyLibrary" />

Now anywhere in your site, you can put in markup like this:

<spiffy:SpiffyControl ID="SpiffyInstance" runat="server" />

... and that will load your web control from your custom library.

If I read this correctly, the original poster had the problem in which he cannot access the Type of the base class from which the parent page derived in his UserControl, not the other way around. It just so happens I had the same problem.

In my case all the pages in my project inherit from the same base, so I'm not worrying about the fact that my user control has knowledge of the base class of the page. Apologies to architects.

However, I solved the problem with a combination of 1) using a base class for my user control, and 2) employing a common namespace. This enabled the visibility I needed, since for some reason the base class of the user control still couldn't see the base class type of the pages' base class.

Anyway, I just thought I'd share my results in case they help someone else in the future.

HTH.

Even though your base isn't an ASPX page, you can have it inherit from the same framework class as if it was:

public abstract class MyBasePage : System.Web.UI.Page
{

}

Assuming SpiffyControl and SpiffyPage are in the same namespace:

 Control spiffyControl = LoadControl("SpiffyControl.ascx");
 (control as SpiffyControl).SpiffyControlProp1 = true;
 (control as SpiffyControl).SpiffyControlProp2 = "Hello, World!";

You could also just define the namespace of your SpiffyControl manually. To do this, you need to change both the codebehind and the markup.

In the codebehind, wrap your class in a namespace block. So if your class was

public partial class Controls_SpiffyControl : System.Web.UI.UserControl
{
    ...
}

... then change it to ...

namespace Spiffy
{
    public partial class Controls_SpiffyControl : System.Web.UI.UserControl
    {
        ...
    }
}

Then in the markup, adjust your @Control header. So if your @Control header was

<%@ Control
    Language="C#"
    AutoEventWireup="true"
    CodeFile="SpiffyControl.ascx.cs"
    Inherits="Controls_SpiffyControl" %>

... then change it to ...

<%@ Control
    Language="C#"
    AutoEventWireup="true"
    CodeFile="SpiffyControl.ascx.cs"
    Inherits="Spiffy.SpiffyControl" %>

Assuming that you will be running in an environment that supports reflection you could use these methods.

private void SetValue(Control control, string propertyName, object value)
{
    Type type = control.GetType();
    PropertyInfo property = type.GetProperty(propertyName);
    property.SetValue(control, value, null);
}
private object GetValue(Control control, string propertyName)
{
    Type type = control.GetType();
    PropertyInfo property = type.GetProperty(propertyName);
    return property.GetValue(control, null);
}

And call it like this:

     Control control = this.LoadControl("~/SpiffyControl.ascx");
    string propertyName = "Custom1";
    object value = "Value";

    SetValue(control, propertyName, value);

If it gets called a lot you might want to cache the Type information.

You could also consider creating an ISpiffyControl interface in the assembly SpiffyPage is in, then have your user control implement it.

It's been touched on, but an interface should work. Make ISpiffyControl with whatever members/methods, implement it in the SpiffyControl.ascx.vb and do this in the SpiffyPage:

Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    MyBase.OnLoad(e)
    Dim page As New Page
    Dim path As String = request.ApplicationPath
    Dim control as UI.Control = page.LoadControl(String.Format _
        ("{0}/pathToUserControl/{1}.ascx", path, controlName))
    Dim sc As ISpiffyControl = CType(control, ISpiffyControl)
    sc.DoSomethingSpecial()
    Me.Controls.Add(sc)
End Sub

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM