在事件期间运行的强制方法

时间:2011-08-05 18:34:31

标签: asp.net webforms

是否有办法强制方法只能在页面生命周期中的某些事件期间访问。例如,我有System.Web.UI.Page的扩展名,可以添加PrependTitle方法。

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

结果应为:

21 May 2011 :: Calendar :: Google

PrependTitle事件期间运行Page_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;
    }
}

4 个答案:

答案 0 :(得分: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问题的原因。我认为需要更多的代码/背景来解决您的实际问题

答案 1 :(得分: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属性也是如此。但是使用Reflection,你可以使用它 - 你甚至可以从页面外部的扩展方法中使用它。

希望其中一种方式适合你!

答案 2 :(得分:0)

您最好的选择可能是使用Handles关键字将该方法附加到该事件中。

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

答案 3 :(得分:0)

似乎问题出现在prependTitle方法中,它应该将文本追加到页面标题而不是替换它。

只需在每个mashterpage和page的page_load中调用PrependTitle方法,然后将文本追加到标题中。