3


A) 下面的问题是基于这样的假设:控件总是按照声明的顺序绑定到数据源控件?因此,在我们的示例中,SqlDataSource1 将在 SqlDataSource2 之前连接到数据源,因此lstCities将在GridView1之前填充值,原因是lstcitiesGridView1之前声明?!



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" …&gt;
    </asp:GridView>


谢谢

编辑:

但是,如果它是回发,那么这些参数将从页面的 OnLoadComplete 上的视图状态加载,再次按照声明它们的顺序。

Q1 - 假设 ControlParameter 绑定到控件 C 的属性 C1。我想在回发时 ControlProperty 总是能够从 ViewState 获取 C.C1 的值,无论 C 是什么类型,即使 C 禁用了 ViewState ?!

Q2 - 但是我可以问为什么,如果第一次创建页面,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 被绑定之后发生)


我真的很感谢你的帮助


第三次编辑:

真的很抱歉再次占用您的时间。我会尽我最大的努力使这成为我的最后一次编辑。


Update() 在 OnLoadComplete 和数据绑定发生时都被调用。在 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() 的必要性!


非常感谢

4

1 回答 1

2

你的第一个假设是正确的。

对于您的第二个问题,这取决于它是否是回发和/或您是否明确绑定。如果不是回发并且绑定自动发生,那么粗略地说,当 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 将被触发。顺便说一句,至少还有十个其他地方会触发此事件。它在数据绑定和属性检索过程中没有发挥重要作用(如果有的话)。

于 2009-04-08T18:34:53.800 回答