Cómo encontrar un control o una página en la que está incrustado un control

I've written a web user control which I want to be able to drop into the markup for either aspx pages or other web user controls.

I need my user control to be able to easily and efficiently work out if its inside another user control or an aspx page. My initial idea is to do it recursively with checks on the Parent property - continue looking up the nesting hierarchy until I find either a web form or a user control - but I'm not sure this the best way of going about this.

Can you suggest an easier way? Thanks.

preguntado el 09 de enero de 11 a las 00:01

What spawns the need for your control to be "aware" of its parent? If it is simply because you need custom event handling based on events of either the page or other controls, than perhaps delegates are the way to go... It is possible to have a more graceful solution... -

It's for something that I'm working on so I'd rather not say yet ;-) I wanted to make something simple for developers to be able to easily drop into their code so its deliberately kept as self-contained as possible with a requiremment being zero-config, hence the need to make the thing 'aware' of where it is without having to do additional hooking-up. -

3 Respuestas

Recursively check the type of your Parent hasta Parent.GetType() es cualquiera typeof(UserControl) or type(Page)

private bool IsAncestorTypeOf(Control c, params Type[] typesToCheck)
{
   var parent = c.Parent;

   if (parent == null) return false;    
   if (typesToCheck.Contains(parent.GetType())) return true;

   return IsAncestorTypeOf(parent, typesToCheck);
}

Or the same without recursion

private bool IsAncestorTypeOf(Control c, params Type[] typesToCheck)
{
   var parent = c.Parent;

   while (true)
   {
       if (parent == null) return false;    
       if (typesToCheck.Contains(parent.GetType())) return true;

       parent = parent.Parent;
   }
}

Llámalo como

var isAncestorPageOrUserControl = IsAncestorTypeOf(this, typeof(Page), typeof(UserControl));

or

var isAncestorPage = IsAncestorTypeOf(this, typeof(Page));
var isAncestorUserControl = IsAncestorTypeOf(this, typeof(UserControl));

Respondido el 11 de enero de 12 a las 04:01

This is exactly the sort of thing I was thinking of, although I was probably going to do it with some LINQ-type goodness ;-). I do worry a little about performance in the scenario of a Page that hosts multiple user controls containing my widget all making this recursive call at runtime. Any thoughts? - Indra

well, you can run a loop instead of recursion. guess it will theoretically perform better because you're not filling up the stack... but one way or the other you have to crawl through every parent until you find the types you're interested in. - Pauli Østerø

Generally, components should be unaware of their arbitrary containers, although the containers must know their components (unless it's a strong dependency situation like list items are always in a list type and you can make a strong two way relationship). However it sounds like you are reaching out into the general surroundings. You might find many cases to code for doing this and accidentally miss others.


By making the user control aware of its surroundings and the larger world you may be introducing dependencies that make your control less reusable and harder to maintain.

If something the control needs is outside of itself, you might move toward composición by forcing the developer to provide a reference to the needed thing on a property of your user control. This is the way, for example, that validation controls in ASP.NET do it, to reference an external control to validate by id.

Of course what I specified is practical only some of the time. Is there a specific reason or edge case why you need to make your user control look around itself, or can you get away with providing instructions to the developer about where the control should be used?

Respondido el 09 de enero de 11 a las 07:01

all depending of the specific scenario i really don't see it as to root of all evil. There are a lot of built-in asp.net controls that to exactly the same. Try for instance to drop an <asp:button> or <asp:textbox> into your page without a <form runat="server"> and you'll see them complain loudly! - Pauli Østerø

Indeed Microsoft's control reliance on the form is a very special and important edge case. You're right, not the root of all evil, just another evil among many, that we all but too often participate in. - John K

Esto debería funcionar:

C#

bool inPage = (this.NamingContainer == this.Page);

VB.NET

Dim inPage as Boolean = Me.NamingContainer is Me.Page

Editar: it seems to be not as simple as i hoped. If the usercontrol resists in a control like a GridViewRow, the NamingControl of it would be the Row and not the Page.

This takes it into account:

C#

public static bool isControlInPageOruserControl(Control uc)
{
 bool inPage = uc.NamingContainer is Page;
 if (inPage) {
  return true;
 } else if (uc.NamingContainer is UserControl) {
  return false;
 } else {
  return isControlInPageOruserControl(uc.NamingContainer);
 }
}

VB.NET:

Public Shared Function isControlInPageOruserControl(ByVal uc As Control) As Boolean
    Dim inPage As Boolean = TypeOf uc.NamingContainer Is Page
    If inPage Then
        Return True
    ElseIf TypeOf uc.NamingContainer Is UserControl Then
        Return False
    Else
        Return isControlInPageOruserControl(uc.NamingContainer)
    End If
End Function

Respondido el 09 de enero de 11 a las 22:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.