views:

224

answers:

2

Hi everybody

I need to add multiple user controls to a panel for further editing of the contained data. My user control contains some panels, dropdown lists and input elements, which are populated in the user control's Page_Load event.

protected void Page_Load(object sender, EventArgs e)
{
  // populate comparer ddl from enum
  string[] enumNames = Enum.GetNames(typeof (SearchComparision));

  var al = new ArrayList();
  for (int i = 0; i < enumNames.Length; i++)
    al.Add(new {Value = i, Name = enumNames[i]});

  scOperatorSelection.DataValueField = "Value";
  scOperatorSelection.DataTextField = "Name";
  ...

The data to be displayed is added to the user control as a Field, defined above Page_Load. The signature of the events is the following:

public delegate void ControlStateChanged(object sender, SearchCriteriaEventArgs eventArgs);

public event ControlStateChanged ItemUpdated;
public event ControlStateChanged ItemRemoved;
public event ControlStateChanged ItemAdded;

The update button on the user control triggers the following method:

protected void UpdateCriteria(object sender, EventArgs e)
{
  var searchCritCtl = (SearchCriteria) sender;

  var scEArgs = new SearchCriteriaEventArgs
  {
    TargetCriteria = searchCritCtl.CurrentCriteria.CriteriaId,
    SearchComparision = ParseCurrentComparer(searchCritCtl.scOperatorSelection.SelectedValue),
    SearchField = searchCritCtl.scFieldSelection.SelectedValue,
    SearchValue = searchCritCtl.scFilterValue.Text,
    ClickTarget = SearchCriteriaClickTarget.Update
  };
  if (ItemUpdated != null)
    ItemUpdated(this, scEArgs);
}

The rendering page fetches the data objects from a storage backend and displays it in it's Page_Load event. This is the point where it starts getting tricky: i connect to the custom events!

int idIt = 0;
foreach (var item in _currentSearch.Items)
{
  SearchCriteria sc = (SearchCriteria)LoadControl("~/content/controls/SearchCriteria.ascx");
  sc.ID = "scDispCtl_" + idIt;
  sc.ControlMode = SearchCriteriaMode.Display;
  sc.CurrentCriteria = item;
  sc.ItemUpdated += CriteriaUpdated;
  sc.ItemRemoved += CriteriaRemoved;
  pnlDisplayCrit.Controls.Add(sc);
  idIt++;
}

When first rendering the page, everything is displayed fine, i get all my data. When i trigger an update event, the user control event is fired correctly, but all fields and controls of the user control are NULL. After a bit of research, i had to come to the conclusion that the event is fired before the controls are initialized...

Is there any way to prevent such behavior / to override the page lifecycle somehow? I cannot initialize the user controls in the page's Init-event, because i have to access the Session-Store (not initialized in Page_Init).

Any advice is welcome...

EDIT:

Since we hold all criteria informations in the storage backend (including the count of criteria) and that store uses the userid from the session, we cannot use Page_Init... just for clarification

EDIT #2:

I managed to get past some of the problems. Since i'm now using simple types, im able to bind all the data declaratively (using a repeater with a simple ItemTemplate). It is bound to the control, they are rendered in correct fashion. On Postback, all the data is rebound to the user control, data is available in the OnDataBinding and OnLoad events, everything looks fine.

But as soon it enters the real event (bound to the button control of the user control), all field values are lost somehow...

Does anybody know, how the page lifecycle continues to process the request after Databinding/Loading ? I'm going crazy about this issue...

A: 

Dynamic controls are can be a nightmare. The trick is to make sure you rebind everything on the postback.

Mark Holland
A: 

I figured out a solution ;)

If i work with the OnCommand event on the buttons inside the usercontrol, i can pass a CommandArgument. Now im binding the collection identifier to the CommandArgument parameter of the button which enables me to handle all postbacks inside the usercontrol.

<asp:Button ID="scUpdate"
     runat="server"
     Text="Update"
     OnCommand="HandleCommand"
     CommandName="update"
     CommandArgument='<%# CriteriaId %>' />

This declaration preserves the CriteriaId (a Guid) throughout postbacks and enables me to identify the modified entry on the underlying collection (managed on the page). The following code snippet shows how the event to the subscribing page is triggered.

scEArgs = new SearchCriteriaEventArgs
          {
                TargetCriteria = new Guid(e.CommandArgument.ToString()),
                SearchComparision = ParseCurrentComparer(),
                SearchField = scFieldSelection.SelectedValue,
                SearchValue = scFilterValue.Text,
                ClickTarget = SearchCriteriaClickTarget.Update
          };

if (ItemUpdated != null)
  ItemUpdated(this, scEArgs);

Maybe this answer helps somebody so i'll just post it ;)

schaermu