views:

396

answers:

3

Hi all

I found this extremely useful article on creating instances of ASP.NET UserControls in .ASHX Handlers by Scott Guthrie.

Unfortunately I now have a requirement now where I need to create an instance of a UserControl which has a dependency on the parent page. This being the case, I can't use the same method to create the UserControl.

Can anyone suggest how I might go about this? What is involved in creating an instance of the Parent page and then getting the contents of the UserControl it contains?

Just for reference & to be specific on the type of depenendency I'm dealing with, the UserControl is a panel that exists on a main Factsheet page. Depending on the user's configuration preferences, different controls are dynamically generated in the Factsheet's base class FactsheetBuilderPage, and included on the page.

The UserControl has properties, such as:

public DateTime EffectiveDate
{
    get
    {
        return ((FactsheetBuilderPage)this.Page).EffectiveDate;
    }
}

public Site ConfiguredSite
{
    get { return ((FactsheetBuilderPage)this.Page).SiteConfiguration; }
}

public ConfiguredFund Fund
{
    get
    {
        return ((FactsheetBuilderPage)this.Page).Fund;
    }
}

which reference the FactsheetBuilderPage class, which means I need to create a FactsheetBuilderPage class for it to reference.

Ideally I'd be able to resolve this issue without having to modify the existing code base, because refactoring this will be a real pain!!

Thanks guys

Dave

A: 

You can create and execute a page by a couple of methods. One is to use the BuildManager class to instantiate a page, like so:

Page page = BuildManager.CreateInstanceFromVirtualPath("/my/app/page.aspx", typeof(MyPageClass) ) as Page;

But if you need your page to execute in the current request context, try it this way:

Page page = new MyPageClass();  
page.AppRelativeVirtualPath = 
         context.Request.AppRelativeCurrentExecutionFilePath; 
page.ProcessRequest(context);

This is of course courtesy of Rick Strahl.

womp
@womp - thanks for the reply. I'm giving this method a go and it's all going pretty good so far, except in my parent page's Page_Load event, it's trying to refer to the body tag (<body id="bodyTag" runat="server">) like this: this.bodyTag.Attributes.Add(..), and it's throwing a NullReferenceException (object not set to an instance). Any ideas why body isn't instantiated??
DaveDev
I'm not sure, I've never played around with this very much. Like Gabriel though, I really have to question the dependencies you're generating here - why would a child control depend on the parent's body tag? Can you not just inject the dependencies into the usercontrols?
womp
@womp - I've explained in the update to the original question.
DaveDev
@womp - Rick goes on to explain: "the code above referencing SchedulerTest is actually referencing the CodeBehind class (in WAP) NOT the actual ASPX page which means you don't get markup rendering. This means executing this page only executes the CodeBehind, and not any of the page markup and controls."
DaveDev
+2  A: 

You walked into this one, I'm afraid. If you're going to make a user control that is dependent on its page, then why is it separate? A user control is used to create modularity. They are not supposed to be dependent on the page.

That said, what is the dependence? Since you obviously don't want the page, then maybe you can provide what is needed without the page.

Gabriel McAdams
Gabriel, I agree with you completely, except I didn't walk into it, I got dropped in. I pushed for this project to be done correctly in MVC. If it was, everything everything would be independent. Unfortunately, I got landed into the middle of it all before I could have any input in the archetecture. .. and so now moving on, I have another MVC app that needs that panel. That's why I have a handler in the classic.net working as a service.
DaveDev
@Dave: What is the dependence on the page? I think (if not to remove the dependence) the best thing is to see if we can fulfill the dependency without creating the page.
Gabriel McAdams
@Gabliel, I'll edit the question to clarify..
DaveDev
I updated the example again. - there's more than EffectiveDate, there's objects that are integral to the system that this UserControl needs. Is there a way that I'm not seeing of accessing that data other than having to create the page they exist in?
DaveDev
My advice is to remove the dependency on the page. It'll be better in the long run if you fix badly designed code instead of trying to work around it.
Gabriel McAdams
A: 

Ok, so the solution turned out to be changing the UserControl's properties to be automatic, instead of just having a getter that referrs to the parent page.

public ConfiguredFund Fund { get; set; }

public DateTime EffectiveDate { get; set; }

public Site ConfiguredSite { get; set; }

Now it means that I have to explicitely assign values to these properties when the user control is being instantiated. This allows me to create an instance of this UserControl anywhere without the need for the parent page, as long as I can supply its required data.

then in my Handler, create an instance of the UserControl and assign the properties it needs:

private MyUserControl GetUserControl(int id)
{
    MyUserControl myUc = GetUserControl("Controls/MyUserControl.ascx");

    myUc.Fund = new ConfiguredFund(id);
    myUc.EffectiveDate = DateTime.Now;
    myUc.ConfiguredSite = siteManager.GetSiteById(2);

    return myUc;
}

then to get the rendered html, I use a slightly modified version of Scott Guthrie's code:

public static string RenderView(MyUserControl viewControl, object data)
{
    Page pageHolder = new Page();

    if (data != null)
    {
        Type viewControlType = viewControl.GetType();
        FieldInfo field = viewControlType.GetField("Data");

        if (field != null)
        {
            field.SetValue(viewControl, data);
        }
        else
        {
            throw new Exception("View file doesn't have a public Data property");
        }
    }

    pageHolder.Controls.Add(viewControl);

    StringWriter output = new StringWriter();
    HttpContext.Current.Server.Execute(pageHolder, output, false);

    return output.ToString();
}

Thanks Guys

Dave

DaveDev
It appears you took my advice. If it helped, I would appreciate it if you could accept my answer.
Gabriel McAdams
I'll give you a check if you give me a +1 for the result! :-)
DaveDev