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。顺便说一下,至少还有其他十个地方触发了此事件。它在数据绑定和属性检索过程中不起作用(如果有)。

09-26 11:56