How to access the owner Page from within a component

Discussion in 'ASP .Net Building Controls' started by Ingvar Nilsen, Dec 22, 2004.

  1. I have a component (System.ComponentModel.Component)
    and want to access the Page that "owns" it, during runtime.

    Yes, I can write a public property and set that property in the
    designer, and thereby get access to it at runtime, but I want to be
    smarter than that.

    Somehow the component has to "know" the Page it sits on.
    I want access to that Page object early in the initialization process,
    when the component is created.
    If I have that Page object, I can for example add certain events to the
    Page.

    Any ideas?
     
    Ingvar Nilsen, Dec 22, 2004
    #1
    1. Advertisements

  2. Having the component add events to the page is backwards. The component
    should expose events which the Page can listen to if it wants to. What
    exactly do you mean, "add events to the page"?

    John Saunders
     
    John Saunders, Dec 22, 2004
    #2
    1. Advertisements

  3. Perhaps, but before we discuss that, how do I get access to the page?
    I am sure there is a way, but how?

    Yes, that is the normal case. And it also does.
    Mostly to add an event handler for Page_Load and for Page_Unload.
    I do this already, when the published property "Page" I use today is
    initialized, because this happens before Page_Load. And it works great.

    What I wanted to do is to avoid having to explicit set the Page
    property at design time. The Page is there, at runtime, it has to be,
    it "owns" the component, I just can't get hold of it because I do not
    know how to, as simple as that.

    BTW, this question comes up now and then (Google), no proper answer
    found so far, strangely enough.
     
    Ingvar Nilsen, Dec 22, 2004
    #3
  4. There is no way in general.
    Ok, it sounded like you wanted to add event handlers _to_ the page, which I
    couldn't see how you could do.
    If you take a look at InitializeComponent in your code behind you'll see
    that the page does not "own" the component. If the component has a
    "component constructor", then the "components" variable on the page contains
    the components, but there's no link from "components" to the page.

    John Saunders
     
    John Saunders, Dec 22, 2004
    #4
  5. Fair enough, the expression "own" is more figuratively than technically
    correct. But what is "this" in this context, if not the page?

    this.components = new System.ComponentModel.Container();
    this.MyComponent1 = new MyComponent(this.components);

    I still believe there must be a way.
    When I can get access to the Response object ar runtime, I should get
    access to the page too.
    I am not familiar with this architecture.
    I am used to components always knowing who contains/created them, and
    the creator in turn about its creator again and so on.
    By using the "Parent" or "Owner" property which here seems to not exist.
     
    Ingvar Nilsen, Dec 22, 2004
    #5
  6. Yes, "this" is the page. But notice that it's not "this" that's being passed
    to the component, but "this.components".
    You can get access to the response object because the HttpContext class
    thoughtfully provides a static Current property. There is no Page.Current.

    Ahah! I just realized that your component should be able to find "the
    running page" assuming that there is one. Try HttpContext.Current.Handler.
    If HttpContext.Current is non-null, and .Handler is non-null, and if it is a
    page, then it's the currently-running page.

    Said page may not be available in the constructor of your component, BTW.

    John Saunders
     
    John Saunders, Dec 22, 2004
    #6
  7. Well, I've got your solution right here!

    Or, more accurately, Chris Sells does. I've adapted his code from "Windows
    Forms Progeaming in C#", ISBN 0-321-11620-8, Chapter 9, "Design-Time
    Integration", page 314-315. The basic trick is to define a public property.
    The property getter runs at design time, where it can get a reference to the
    page through the designer infrastructure. This puts a reference to the page
    into this property at design time.

    When the designer serialzes the component to the code, it will emit code to
    store "this" into the property, so you'll have it at run time:

    private IComponent _page;
    [Browsable(false)]
    public IComponent ParentPage
    {
    get
    {
    if (_page == null && this.DesignMode)
    {
    System.ComponentModel.Design.IDesignerHost designer =
    this.GetService(typeof(System.ComponentModel.Design.IDesignerHost))
    as System.ComponentModel.Design.IDesignerHost;
    if (designer != null)
    {
    _page = designer.RootComponent;
    }
    }
    return _page;
    }
    set
    {
    if (!this.DesignMode)
    {
    if ((_page != null) && (_page != value))
    {
    throw new InvalidOperationException("Can't change
    ParentPage at run time");
    }
    }
    else
    {
    _page = value;
    }
    }
    }

    With this in place, the designer serializes the component as follows:

    this.myComponent2 = new MyComponent(this.components);
    //
    // myComponent2
    //
    this.myComponent2.ParentPage = this;


    Success!

    John Saunders
     
    John Saunders, Dec 22, 2004
    #7
  8. Yes, I have. It takes time to getting used to. I find it strange that
    the component constructor doesn't have an argument for the instance
    creating it. And C# doesn't have virtual constructors, AFAIK, hm...

    Yes, have seen that.
    In hindsight, I really had plans to look at it but currently my help
    system is wobbly for some reason, so I skipped this property.
    It is not!
    Oh yeah, indeed it is!!!!

    Thanks a lot, John, with combined efforts we once again proved that in
    computing everything is possible if you look and try hard enough!.

    Now it works like magic, I can just drop a component on the web form,
    and this component itself installs event handlers for the Page's
    events, without writing a single line of code in the Page's code behind
    file.

    Exactly what I was after.
     
    Ingvar Nilsen, Dec 22, 2004
    #8
  9. John, thanks a lot, I really appreciate your efforts!
    I have saved this solution, too, it may come in handy, but as you see
    form my other post, I achieved it with a couple of lines because the
    Current.Handler actually is precisely what I am after: The Page.

    Thanks again,
     
    Ingvar Nilsen, Dec 22, 2004
    #9
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.