Control.UniqueId 是什么时候创建的?

4
有人知道控件的UniqueId何时被分配吗?
我现在在我的Page_Init中编写了一些基于UniqueId的代码。 但是,根据某些业务逻辑,我可能需要在此之前重新排列页面的控件层次结构。
因此,我的主要问题是,UniqueId何时被分配? 我是否可以在Page_PreInit()中重新排列层次结构,以便当我的代码在Page_Init()中运行时,我将拥有正确的UniqueId?

你为什么要依赖于 UniqueId - Tag 不是更好吗? - Daniel A. White
3个回答

6
为了回答这个问题,我编写了一个小的代码后台,记录了每个事件中控件唯一ID属性的值:
  • PreInit
  • Init
  • InitComplete
  • PreLoad
  • Load
  • LoadComplete
  • PreRender
  • PreRenderComplete
例如,最后一个事件处理程序如下:
protected void Page_PreRenderComplete(object sender, EventArgs e)
{
    _uniqueIdValues.Add(
        new Tuple<string, string>("PreRenderComplete", MyControl.UniqueID));
}

然后,我在卸载事件中设置了一个断点,并使用Visual Studio的Immediate窗口打印出记录的值:

_uniqueIdValues.ToArray()
{System.Tuple<string,string>[8]}
    [0]: {(PreInit, MyControl)}
    [1]: {(Init, MyControl)}
    [2]: {(InitComplete, MyControl)}
    [3]: {(PreLoad, MyControl)}
    [4]: {(Load, MyControl)}
    [5]: {(LoadComplete, MyControl)}
    [6]: {(PreRender, MyControl)}
    [7]: {(PreRenderComplete, MyControl)}

看起来,在每个事件中,UniqueID都设置为字符串"MyControl"(实际上,这是我在ASPX标记中为控件指定的ID属性)。似乎@Rewinder在MSDN上的回答是正确的。这些在任何ASP.NET页面级别事件被触发之前都会被设置。
编辑:
如果我们查看.NET 3.5参考源(http://referencesource.microsoft.com/)中的System.Web.UI.Control,我们可以看到当访问该属性时,UniqueID的返回值会被计算。UniqueID属性的代码如下:
public virtual string UniqueID { 
    get { 
        if (_cachedUniqueID != null) {
            return _cachedUniqueID; 
        }

        Control namingContainer = NamingContainer;
        if (namingContainer != null) { 
            // if the ID is null at this point, we need to have one created and the control added to the
            // naming container. 
            if (_id == null) { 
                GenerateAutomaticID();
            } 

            if (Page == namingContainer) {
                _cachedUniqueID = _id;
            } 
            else {
                string uniqueIDPrefix = namingContainer.GetUniqueIDPrefix(); 
                if (uniqueIDPrefix.Length == 0) { 
                    // In this case, it is probably a naming container that is not sited, so we don't want to cache it
                    return _id; 
                }
                else {
                    _cachedUniqueID = uniqueIDPrefix + _id;
                } 
            }

            return _cachedUniqueID; 
        }
        else { 
            // no naming container
            return _id;
        }
    } 
}

当命名容器发生变化时,会调用以下方法。ClearCachedUniqueIDRecursive方法会重置_cachedUniqueID字段的值,以便在下一次调用UniqueID属性时重新生成它。

private void UpdateNamingContainer(Control namingContainer) {
    // Remove the cached uniqueID if the control already had a namingcontainer
    // and the namingcontainer is changed. 
    if (_namingContainer != null && _namingContainer != namingContainer) {
        ClearCachedUniqueIDRecursive(); 
    } 

    _namingContainer = namingContainer; 
}

有趣的是,如果我检查控件的UniqueID值,然后更改该控件的父容器,当我再次检查UniqueID时,它会更新为新的层次结构。 - Jim B
请查看.NET参考源(http://referencesource.microsoft.com/)中的System.Web.UI.Control。当访问UniqueID属性时,返回值会被计算。如果NamingContainer发生更改,则UniqueID的内部缓存值将被重置,以便在下次访问该属性时重新计算。 - Matthew Rodatus
我更新了我的答案,包括一些来自.NET 3.5参考源代码的代码。 - Matthew Rodatus

1

我能否在Page_PreInit()中重新排列层次结构,以便在Page_Init()中触发我的代码时,我将有正确的UniqueId分配?

我无法确定您要做什么,但听起来微软不鼓励这样做:

通常,您不需要使用UniqueId属性。例如,您不应编写引用控件的代码,使用生成的UniqueId属性的预测值。您可以读取并将UniqueId属性的值传递给其他进程,但不应依赖它具有特定的结构。


0

来自 MSDN:

当处理页面请求时,此标识符将自动生成。

所以,在 PreInit 期间您可以访问 uniqueId

编辑

好的,我想我有点模糊。`Page.ProcessRequest` 调用 `FrameworkInitialize()` 方法,该方法将构建控件树。这发生在 PreInit 之前,因此控件的 UniqueId 可用。


2
在请求期间会发生大量的事情,我建议发帖者知道这一点,并且正在询问在请求过程中特定地给出属性值的位置是什么? - Kieren Johnstone
3
你的回答就像将生产形容为“婴儿从母亲的身体中出来一样”,虽然是正确的,但��太过模糊,信息没有用处。 - FreeAsInBeer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接