2011-08-05 12 views
3

是否有办法强制只能在页面生命周期中的特定事件期间访问方法。例如,我有一个System.Web.UI.Page的扩展,它增加了一个PrependTitle方法。在事件期间运行强制方法

我也有一个嵌入另一个母版页的母版页。第一个母版页设置基本标题(Google),下一个母版页预先标题(日历),并且页面也预先标题(2011年5月21日)。

结果应该是:

21 May 2011 :: Calendar :: Google

这是当PrependTitlePage_Init活动期间运行的情况。然而,当该方法在Page_Load运行以下结果:

Google

所以,这使我想到了一个问题:怎样才可以执行该方法只能在指定的生命周期事件访问?

// The Method Mentioned 
public static class PageExtensions 
{ 
    public static void PrependTitle(this Page page, string newTitle) 
    { 
     page.Title = newTitle + " " + Global.TITLE_DELIMITER + " " + page.Title; 
    } 
} 

回答

1

如果您想蛮力确保从Init调用该方法,则可以检查调用堆栈。事情是这样的:

public static bool CalledFromInit() 
{ 
    //Grab the current Stack Trace and loop through each frame 
    foreach(var callFrame in new StackTrace().GetFrames()) 
    { 
     //Get the method in which the frame is executing 
     var method = callFrame.GetMethod(); 

     //Check if the method is Control.OnInit (or any other method you want to test for) 
     if(method.DeclaringType == typeof(Control) && method.Name == "OnInit") 
      //If so, return right away 
      return true; 
    } 

    //Otherwise, we didn't find the method in the callstack 
    return false; 
} 

那么你可以使用它像:

public static void PrependTitle(this Page page, string newTitle) 
{ 
    //If we aren't called from Init, do something 
    if (!CalledFromInit()) 
    { 
     //We could either return to silently ignore the problem 
     return; 

     //Or we could throw an exception to let the developer know they 
     // did something wrong 
     throw new ApplicationException("Invalid call to PrependTitle"); 
    } 

    //Do the normally processing 
    page.Title = newTitle + " " + Global.TITLE_DELIMITER + " " + page.Title; 
} 

不过,我会告诫说,堆栈跟踪是不是最可靠的东西。在发行版中,可以对代码进行优化,使Control.OnInit方法内联,以便您的代码无法在调用堆栈中看到它。您可以将此支票包装在#if DEBUG区块中,以便它仅在开发期间执行。根据您的使用情况,在DEBUG中可能会遇到这个问题,而不会在RELEASE中进行检查。但这取决于你。

另一种选择...建立在Tommy Hinrichs的回答上,如果你所有的页面都从一个基类继承,你将能够更可靠地做到这一点。我建议是这样的:

public abstract class BasePage : Page 
{ 
    private bool _executingInit; 
    protected internal override void OnPreInit(EventArgs e) 
    { 
     _executingInit = true; 
     base.OnPreInit(e); 
    } 

    protected internal override void OnInitComplete(EventArgs e) 
    { 
     base.OnInitComplete(e); 
     _executingInit = true; 
    } 

    public void PrependTitle(string newTitle) 
    { 
     if (!_executingInit) 
      throw new ApplicationException("Invalid call to PrependTitle."); 

     Title = newTitle + " " + Global.TITLE_DELIMITER + " " + Title; 
    } 
} 

这样,PrependTitle会抛出异常,除非它被称为PreInit和InitComplete(这听起来像你想要什么)之间。

作为最后一个选项,您可能会偷偷摸摸地使用反射来访问Control.ControlState属性(这是一个令人困惑的名称,因为它与控件状态无关 - 类似于视图状态)。该属性会跟踪控件,因为它会抛出其生命周期 - 它具有以下值:

internal enum ControlState 
{ 
    Constructed, 
    FrameworkInitialized, 
    ChildrenInitialized, 
    Initialized, 
    ViewStateLoaded, 
    Loaded, 
    PreRendered 
} 

您会注意到Enum是内部的。 Control.ControlState属性也是如此。但是使用反射,你可以使用它 - 你甚至可以从页面外部的扩展方法中使用它。

希望这些方法之一将为您工作!

+0

这是很棒的信息,并按我的意图解决了这个问题。谢谢。 – roydukkey

0

您最好的选择可能是使用Handles关键字的方法连接到事件。

您可能必须创建System.Web.UI.Page的子类以确保强制执行该子类。

0

看来问题出在prependTitle方法中,它应该将文本附加到页面标题上而不是替换它。

只需调用每个mashterpage和页面的page_load中的PrependTitle方法,并将该文本附加到标题。

+0

我已经添加了该方法。我不认为这是问题,但我可能是错的。 – roydukkey

2

我认为这可以做到类似于以下内容。总的想法是申报方法为私有,声明为密封

class AppsBasePage : Page 
{ 
    abstract void PrependTitle(string title); 
} 
class PageWithTitlePrepended : AppsBasePage 
{ 
    private void PrependTitle(string title) 
    { 
     Title = String.Format("{0} {1} {2}", newTitle, Global.TITLE_DELIMITER, Title); 
    } 
    protected sealed override void Page_Init(object sender, EventArgs e)  
    { 
     PrependTitle("This is a title") 
    } 
} 
class ActualPageInApp: PageWithTitlePrepended 
{ 
    override void Page_Load(object s, EventArgs e) 
    { 
     // can't access PrependTitle here 
    } 
} 

这可以解决你的问题用粗体应该可以访问它的那些,但我不认为这种情况是什么原因造成的问题, PrependTitle具体。我认为需要更多的代码/上下文来解决您的实际问题

+0

Humm ...我很惊讶这是唯一的解决方案。这不是我所期待的。另外,我不相信PrependTitle方法有问题。这是我理解Page_Init从主人到页面的运行,以及Page_Load从页面到主人的触发。我想唯一的其他选择是从PrependTitle内部确定当前的事件并相应地进行跟踪。 – roydukkey