是否有办法强制方法只能在页面生命周期中的某些事件期间访问。例如,我有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;
}
}
答案 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方法,然后将文本追加到标题中。