你的第一个假设是正确的。
对于您的第二个问题,这取决于它是否是回发和/或您是否明确绑定。如果不是回发并且绑定自动发生,那么粗略地说,当 DataSourceView 在 DataBind 上调用 Select 时,就在 OnSelecting 事件之前检索 ControlParameter 的值。网格视图(以及任何给定的控件)的顺序如下:
Page.ProcessRequest
Page.PreRenderRecursiveInternal
...
GridView.EnsureChildControls
GridView.CreateChildControls
GridView.DataBind
GridView.PerformSelect
DataSourceView.Select //comes from either SQLDataSource or LinqDataSource
DataSourceView.ExecuteSelect
//for Linq:
LinqDataSourceView.GetParameterValues(WhereParameters)
//for SQL:
SqlDataSourceView.InitializeParameters(SelectParameters)
Parameters.GetValues
Parameters.UpdateValues //this is where values get retrieved using reflection
DataSourceView.OnSelecting //follows almost immediately
...get data...
DataSourceView.OnSelected
因此,对于控件层次结构中的每个控件,框架会递归调用 DataBind,然后触发参数检索、OnSelecting、数据检索和 OnSelected。
但是,如果它是回发,那么这些参数将从页面的 OnLoadComplete 上的视图状态加载,再次按照声明它们的顺序。
这是你要找的吗?
编辑
Q1 - 假设 ControlParameter 绑定到控件 C 的属性 C1。我想在回发时 ControlProperty 总是能够从 ViewState 获取 C.C1 的值,无论 C 是什么类型,即使 C 禁用了 ViewState ?!
这并不完全是如何发生的......在回发(以及针对该问题的初始请求)时,仅评估 ControlParemeter 的视图状态以查看它是否已更改,以便可以触发 OnParameterChanged 事件。ControlParameter 的实际值根据它指向的控件(通过反射)进行评估。在您的情况下,它将是“C.C1”。现在,当它读取 C.C1 时,它的值很可能是从视图状态中读取的。但是 ControlParameter 决不会直接读取 C 的视图状态。
Q2 - 但是我可以问为什么,如果第一次创建页面,ControlParameter 的值也不能从视图状态中检索?毕竟,在 lstCities 从数据源检索数据的那一刻, lstCities.SelectedValue 是否设置了它的值?
就是这样,那时(第一次加载页面) lstCities 还没有检索到任何数据。第一次发生属性评估是在 Page.OnLoadComplete 上,但在任何 DataBind 之前(发生在 Page.PreRenderRecursiveInternal 被触发后不久)。
以粗略的形式,尝试将其置于页面的生命周期中:
...request...
PerformPreInit
InitRecursive //SqlDataSource subscribes to Page.LoadComplete
OnInitComplete
if PostBack
LoadAllState //the view state gets loaded
ProcessPostData
OnPreLoad
LoadRecursive
if PostBack
ProcessPostData
RaiseChangedEvents
RaisePostBackEvents //you handle your events
//notice that following sections assume that you did not do any data
//binding inside your events
OnLoadComplete //This is where parameters (SelectParemeters/WhereParemeters)
//get updated. At this point none of them are data bound yet.
//And if it the first time, there are no values
//as the ViewState is empty for them.
PreRenderRecursiveInternal //calls the DataBind (if you haven't already),
//then DataSourceView.Select; parameters evaluate their controls.
//The control C would be bound at this point.
PerformPreRenderComplete
SaveAllState
OnSaveStateComplete
RenderControl
第二次编辑
所以 ControlParameter 评估 C.C1 并因此在 C 绑定后检索 C.C1 的值?!
每当询问时,ControlParameter 都会检索值,在这种情况下(间接)在两个地方发生:OnLoadComplete 和 DataBind(由 PreRenderRecursiveInternal 触发)。OnLoadComplete 时,C 未绑定。在 PreRenderRecursiveInternal 上,在 DataBind 之后,C 被绑定。两次 ControlParameter 都被要求读取 C.C1。也许以下会有所帮助......
简而言之,这里是感兴趣的类和方法。将它们放在页面周期的角度,希望它会很清楚。
public class ControlParameter : Parameter
{
public string ControlID { get; set; } //stored in ViewState
public string PropertyName { get; set; } //stored in ViewState
protected override object Evaluate(HttpContext context, Control owner)
{
Control sourceControl = DataBoundControlHelper.FindControl(owner, this.ControlID);
//evaluate C.C1 using reflection
return DataBinder.Eval(sourceControl, this.PropertyName);
}
internal void UpdateValue(HttpContext context, Control owner)
{
//PostBack or not, read stored value (on initial load it is empty)
object storedValue = this.ViewState["ParameterValue"];
//Get the actual value for this parameter from C.C1
object actualValue = this.Evaluate(context, owner);
//Store received value
this.ViewState["ParameterValue"] = actualValue;
//Fire a change event if necessary
if ((actualValue == null && storedValue != null)
|| (actualValue != null && actualValue != storedValue))
this.OnParameterChanged();
}
}
public class SqlDataSource : DataSourceControl
{
//fired by OnLoadComplete
private void LoadCompleteEventHandler(object sender, EventArgs e)
{
//UpdateValues simply calls the UpdateValue for each parameter
this.SelectParameters.UpdateValues(this.Context, this);
this.FilterParameters.UpdateValues(this.Context, this);
}
}
public class SqlDataSourceView : DataSourceView, IStateManager
{
private SqlDataSource _owner;
//this method gets called by DataBind (including on PreRenderRecursiveInternal)
protected internal override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
{
DbConnection connection = this._owner.CreateConnection(this._owner.ConnectionString);
DbCommand command = this._owner.CreateCommand(this.SelectCommand, connection);
//This is where ControlParameter will read C.C1 values again.
//Except this time, C.C1 will be already populated by its own DataBind
this.InitializeParameters(command, this.SelectParameters, null);
command.CommandType = GetCommandType(this.SelectCommandType);
SqlDataSourceSelectingEventArgs e = new SqlDataSourceSelectingEventArgs(command, arguments);
this.OnSelecting(e);
if (e.Cancel)
return null;
//...get data from DB
this.OnSelected(new SqlDataSourceStatusEventArgs(command, affectedRows, null));
//return data (IEnumerable or DataView)
}
private void InitializeParameters(DbCommand command, ParameterCollection parameters, IDictionary exclusionList)
{
//build exlusions list
//...
//Retrieve parameter values (i.e. from C.C1 for the ControlParameter)
IOrderedDictionary values = parameters.GetValues(this._context, this._owner);
//build command's Parameters collection using commandParameters and retrieved values
//...
}
}
A) 所以 ControlParameter 检查它的 ViewState 是否改变了......
请参阅上面的 UpdateValue 方法,了解它如何使用 ViewState。
B)我假设 ControlParameter 检查其 Viewstate 是否仅更改,以便它可以触发 OnParameterChanged 事件。但为什么处理该事件如此重要?
我不知道这很重要。我想,就像任何其他事件一样,它允许您跟踪参数属性的变化并根据您的需要采取行动。它在很多地方都被解雇了,但我看不到有人在哪里订阅它。所以...
通过属性评估,您的意思是 ControlParameter 检查自己的 ViewState?因此,您并不是说 ControlParameter 评估 C.C1 (我假设在 C 被绑定之后发生)
这意味着 ControlParameter.UpdateValue 被调用,它检查 ViewState 以说明原因,然后调用 ControlParameter.Evalue,然后找到一个控件并使用反射 (Eval) 检索数据。往上看。
第三次编辑
我认为更新是指 UpdateValue。
因此,如果在发生数据绑定时调用 Update(),那么这意味着当在 OnLoadComplete 中调用下一次回发 UpDate() 时,C.C1 和 ControlParameter 将已经具有相同的值......
没有必要。您忘记了视图状态是在 LoadAllState 上加载的,并且在它和 OnLoadComplete 之间还有六个步骤(参见上面的页面生命周期)。其中每一个都可以修改源代码管理的 (C.C1) 值。
假设您有 C.C1 = "x" 并回帖。现在,所有控件的视图状态都已加载 (LoadAllState)。如果 C.C1 将其值存储在视图状态中,它将加载“x”。在 Page_Load (LoadRecursive) 上,您决定设置 C.C1 = "y"。在这里,C.C1 可能决定是否在其视图状态中存储“y”——这无关紧要。然后其他事件随之而来。接下来是 OnLoadComplete。由于 SqlDataSource 订阅此事件,它将评估所有关联参数 (LoadCompleteEventHandler),并且由于您确实更改了 C.C1 但 ControlParameter 的视图状态没有,所以
if ((actualValue == null && storedValue != null)
|| (actualValue != null && actualValue != storedValue))
this.OnParameterChanged();
将返回 true 并且 OnParameterChanged 将被触发。顺便说一句,至少还有十个其他地方会触发此事件。它在数据绑定和属性检索过程中没有发挥重要作用(如果有的话)。