A)以下问题基于这样的假设:控件始终按照声明的顺序绑定到数据源控件?因此,在我们的示例中,SqlDataSource1将在SqlDataSource2之前连接到数据源,因此lstCities将在GridView1之前填充值,原因是lstcities是在GridView1之前声明的!
B)如果是这样,那么ControlParameter何时确切从DropDownList检索值?我假设它在SqlDataSource1_Selected()事件处理程序之后和SqlDataSource2_Selecting()事件处理程序之前,但是究竟是什么时候?
在.aspx页中:
<asp:SqlDataSource ID="SqlDataSource1" ... >
</asp:SqlDataSource>
<asp:DropDownList ID="lstCities" DataSourceID="SqlDataSource1"
DataTextField="City" runat="server"></asp:DropDownList>
<asp:SqlDataSource ID="SqlDataSource2" ... >
<SelectParameters>
<asp:ControlParameter ControlID="lstCities" Name="City"
PropertyName="SelectedValue" />
</SelectParameters>
</asp:SqlDataSource>
<asp:GridView DataSourceID="SqlDataSource2" runat="server" …>
</asp:GridView>
谢谢
编辑:
但是,如果是回发,则将按照声明的顺序从页面的OnLoadComplete上的视图状态中加载这些参数。
Q1-假设ControlParameter绑定到控件C的属性C1。我可以想象在回发时,ControlProperty始终能够从ViewState获取C.C1的值,无论C是什么类型,即使C禁用了ViewState ?!
问题2-但是我想问一下,如果是第一次创建页面,为什么还不能从viewstate中检索ControlParameter的值?毕竟,当lstCities从数据源lstCities.SelectedValue检索数据时,是否已设置其值?
伴侣
第二编辑:
抱歉,我们没有尽快回复您,但我没有意识到您已回复。当我这样做时,我花了20分钟的时间尝试让我的3个脑细胞正常工作,但是我不确定我是否成功
A)因此,ControlParameter评估C.C1,从而在绑定C之后获取C.C1的值?
Q1-ControlParameter仅读取自己的状态,并仅确定其是否已更改
A)因此,ControlParameter在绑定发生之前检查其ViewState是否已更改(以触发OnParameterChanged事件)->因此,它在Page.OnLoadComplete期间检查其ViewState。
但是ControlParameter如何知道它的ViewState已更改(它将在第一次回发时知道)?毕竟,从第一次创建页面开始,ControlParameter的ViewState始终会被标记为脏,因此从一个回发到另一个回发,ControlParameter如何知道其值在两次回发之间是否已更改?
B)我假设ControlParameter检查它的Viewstate是否仅改变了,以便它可以触发OnParameterChanged事件。但是,为什么处理该事件如此重要?
第一次进行属性评估是在Page.OnLoadComplete上
通过属性评估,您是说ControlParameter检查自己的ViewState?因此,您的意思不是ControlParameter评估C.C1(我认为这是在C绑定之后发生的)
非常感谢您的帮助
第三编辑:
非常抱歉再次抽出宝贵的时间。我将尽我最大努力使这成为我的上一次修改。
在OnLoadComplete中和发生数据绑定时都将调用Update()。在Update()内部,还执行以下语句:
this.ViewState["ParameterValue"] = actualValue;
因此,如果在数据绑定发生时调用Update(),则意味着
在OnLoadComplete中调用下一次回发UpDate()时,C.C1和ControlParameter将已经具有相同的值,因此
if ((actualValue == null && storedValue != null)
|| (actualValue != null && actualValue != storedValue))
将始终返回false(在OnLoadComplete中调用Update()时),因此OnParameterChanged事件将永远不会被触发?1如果是这样,我看不到需要在OnLoadComplete中调用Update()!
多谢
最佳答案
您的第一个假设是正确的。
对于第二个问题,这取决于是否是回发和/或是否明确绑定。如果不是这样,则回发并自动进行绑定,粗略地说,当DataSourceView在OnSelecting事件之前调用DataSourceView上的Select时,将检索ControlParameter的值。 gridview的顺序(以及与此相关的任何给定控件)如下:
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的视图状态。
问题2-但是我想问一下,如果是第一次创建页面,为什么还不能从viewstate中检索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没有绑定。在DataRind之后的PreRenderRecursiveInternal上,绑定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。顺便说一下,至少还有其他十个地方触发了此事件。它在数据绑定和属性检索过程中不起作用(如果有)。