views:

702

answers:

1

Hello,


A) Question below is based on the assumption that controls are always binded to data source controls in the order they are declared? So in our example SqlDataSource1 will connect to data source prior to SqlDataSource2 and thus lstCities will be populated with values before GridView1, and reason for that being that lstcities was declared before GridView1?!



B) If so, then when exactly does ControlParameter retrieve a value from DropDownList? I assume it’s after *SqlDataSource1_Selected()* event handler and before *SqlDataSource2_Selecting()* event handler, but when precisely?

In .aspx page:

    <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>


thanx

EDIT:

If it is a postback, however, then those parameters will get loaded from a viewstate on the page's OnLoadComplete, again, in the order they are declared.

Q1 - Let’s assume ControlParameter is bound to property C1 of a control C. I would imagine that on postbacks ControlProperty would always be able to get C.C1’s value from ViewState, no matter of what type C is, and even if C has ViewState disabled?!

Q2 - But may I ask why, if a page is created for the first time, can’t a value for ControlParameter also be retrieved from viewstate? Afterall, the moment lstCities retrieves data from data source, lstCities.SelectedValue has its value set?



thanx mate


SECOND EDIT:

I apologize for not replying sooner, but I didn’t realize you’ve replied. And when I did, I've spent good 20 minutes trying to get my 3 braincells to work properly, but I'm not sure if I quite succeeded


A) So ControlParameter evaluates C.C1 and thus retrieves C.C1’s value after C has been bound?!


Q1 - ControlParameter only reads its own state and only to determine if it changed

A) So ControlParameter checks whether its ViewState changed ( in order to fire OnParameterChanged event) before binding takes place --> thus it checks its ViewState during Page.OnLoadComplete. But how will ControlParameter know that its ViewState has changed ( it will know on first postback )? Afterall, from the first time the page is created ControlParameter’s ViewState will always be marked as dirty, so how will, from one postback to another, ControlParameter know whether its value has changed between postbacks?

B) I assume ControlParameter checks whether its Viewstate changed only so that it can fire OnParameterChanged event. But why is handling that event so important?


The first time a property evaluation happens is on Page.OnLoadComplete

By property evaluation you mean ControlParameter checking its own ViewState? Thus you don’t mean ControlParameter evaluating C.C1 ( which I assume happens after C has been bound )


I really appreciate your help


THIRD EDIT:

I’m really sorry for again taking your time.I will do my best to make this my last Edit.


Update() is called both in OnLoadComplete and when data binding takes place. Inside Update() the following sentence is also executed:

this.ViewState["ParameterValue"] = actualValue;

So if Update() is called when data binding takes place, then what that means is that when on next postback UpDate() is called in OnLoadComplete, C.C1 and ControlParameter will already have same values and thus

             if ((actualValue == null && storedValue != null)
             || (actualValue != null && actualValue != storedValue))

will always return false ( when Update() is called in OnLoadComplete ), and so OnParameterChanged event will never get fired?1 If so, the I fail to see the need to call Update() in OnLoadComplete!


much obliged

+1  A: 

Your first assumption is correct.

To your second question, it depends on whether it is a post back or not and/or if you are binding explicitly. If it is not the post back and binding happens automatically then, roughly speaking, the value of the ControlParameter is retrieved when DataSourceView calls Select on DataBind, right before OnSelecting event. The sequence for the gridview (and any given control for that matter) is as follows:

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

So, for each control in a control hierarchy, the framework recursively calls DataBind, which then triggers retrieval of parameters, OnSelecting, data retrieval, and OnSelected.

If it is a postback, however, then those parameters will get loaded from a viewstate on the page's OnLoadComplete, again, in the order they are declared.

Is this what you were looking for?

Edit

Q1 - Let’s assume ControlParameter is bound to property C1 of a control C. I would imagine that on postbacks ControlProperty would always be able to get C.C1’s value from ViewState, no matter of what type C is, and even if C has ViewState disabled?!

That's not entirely how it happens... On post back (and on initial request for that matter), ControlParemeter's view state is evaluated only to see if it changed so that the OnParameterChanged event could get fired. The actual value of the ControlParameter is evaluated against the control it points to (via reflection). In your case it'd be "C.C1". Now, when it reads C.C1, its value is most likely read from a view state. But at no point does the ControlParameter read C's view state directly.

Q2 - But may I ask why, if a page is created for the first time, can’t a value for ControlParameter also be retrieved from viewstate? Afterall, the moment lstCities retrieves data from data source, lstCities.SelectedValue has its value set?

That's the thing, at that point (the first time page loads) the lstCities did not retrieve any data yet. The first time a property evaluation happens is on Page.OnLoadComplete, but before any DataBind (which happens shortly after, when Page.PreRenderRecursiveInternal gets fired).

In the crude form, trying to place it in a life cycle of a page:

...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

Second Edit

So ControlParameter evaluates C.C1 and thus retrieves C.C1’s value after C has been bound?!

The ControlParameter retrieves values whenever it is asked, which in this scenario happens (indirectly) in two places: OnLoadComplete and DataBind (triggered by PreRenderRecursiveInternal). On OnLoadComplete, the C is not bound. On PreRenderRecursiveInternal, after DataBind, the C is bound. Both times ControlParameter is asked to read C.C1. Maybe following will help...

Here are classes and methods of interest in a nutshell. Place them in perspective of the page cycle and hopefully it will be clear.

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) So ControlParameter checks whether its ViewState changed...

Refer to the UpdateValue method above to see how it uses ViewState.

B) I assume ControlParameter checks whether its Viewstate changed only so that it can fire OnParameterChanged event. But why is handling that event so important?

I don't know that it is important. I suppose, like any other event, it allows you to track changes in parameter's properties and act accordingly to your needs. It gets fired in many places, but I don't see where anyone subscribes to it. So...

By property evaluation you mean ControlParameter checking its own ViewState? Thus you don’t mean ControlParameter evaluating C.C1 ( which I assume happens after C has been bound )

It means that the ControlParameter.UpdateValue gets called, which checks ViewState for stated reasons, then calls ControlParameter.Evalue, which then finds a control and retrieves data using reflection (Eval). See above.

Third Edit

I presume that by Update you mean UpdateValue.

So if Update() is called when data binding takes place, then what that means is that when on next postback UpDate() is called in OnLoadComplete, C.C1 and ControlParameter will already have same values...

Not necessary. You are forgetting that the view state is loaded on LoadAllState and between it and the OnLoadComplete there are six more steps (see page life-cycle above). Each of those may modify the source control's (C.C1) value.

Say you have C.C1 = "x" and did a post back. Now, the view state for all controls is loaded (LoadAllState). If C.C1 stored it's value in the view state, it will load "x". On Page_Load (LoadRecursive) you decide to set C.C1 = "y". Here C.C1 may decide to store "y" in its view state or not -- it's irrelevant. Then other events follow. Next comes OnLoadComplete. Since SqlDataSource subscribes to this event, it will evaluate all associated parameters (LoadCompleteEventHandler) and, since you did change C.C1 but ControlParameter's view state did not, the

if ((actualValue == null && storedValue != null)
 || (actualValue != null && actualValue != storedValue))
 this.OnParameterChanged();

will return true and OnParameterChanged will be fired. By the way, there is at least ten other places where this event gets triggered. It doesn't play big role (if any) in data binding and property retrieval process.

Ruslan
I've updated my questions, in case you're still willing to help
SourceC
Sorry for bothering you again,but I did another edit,in case you find the time.Anyhow,I suppose this is the right time to also ask if notifying people(the way I notify you)via comments about the edits I made to my thread is accepted behavior here, or is that considered pesting?I guess this is also
SourceC
me indirectly asking if I’m pesting you :)
SourceC
np. You are asking in an open forum. I don't see it as "you pesting me" as I'm not obligated to answer. In fact I don't really see it as you communicating with me. More like both of us debating/writing an article addressing specific problem of interest. So long that it all makes sense in the end.
Ruslan
thanx for helping me mate - I appreciate it alot
SourceC