Working with Custom Provider Controls
page 6 of 9
by Brian Mains
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 36348/ 103

Newsletter Administration

The next control is something like the GridView control, where you can delete a newsletter from the list, as well as create new newsletters.  This control inherits from CompositeDataBoundControl, and supports the more advanced binding through a data source control or another enumerable data source.

Because the underlying data source comes from the provider, this control doesn't make use of the DataSource or DataSourceID properties at all.  Instead, it makes use of the provider internally, and so when being bound to, it uses the provider data source instead, as we shall soon see.

There is a scheme with these controls that you need to understand.  With these types of tabular data controls, there is a usually a header, a series of items, and the footer below it.  The header, in this case, is non-existent because it isn't needed.  The items will be the list of each of the newsletters, as well as a link to delete the newsletter.  In the footer, there are two textboxes for the newsletter name and description, and a link to add this newly found newsletter.

The CompositeDataBoundControl class uses a different CreateChildControls overloaded method for creating the inner data-bound controls.  The following is the definition for this overload in the newsletter administration control:

Listing 10

protected override int CreateChildControls(System.Collections.IEnumerable
  dataSource, bool dataBinding)
{
  int itemCount = 0;
 
  if (dataBinding)
    dataSource = Newsletter.GetAllNewsletters();
 
  Table table = new Table();
  this.Controls.Add(table);
  this.Items.Clear();
 
  foreach (object o in dataSource)
    this.Items.Add(this.CreateItem(table, o, itemCount++, dataBinding));
 
  this.CreateFooter(table);
  return itemCount;
}

This method takes an IEnumerable data source that is resolved from the manually bound data source (bound through the DataSource property) or a data source control bound through the DataSourceID property.  This isn't a concern for two reasons; the CompositeDataBoundControl class takes care of the differences through the backend architecture, and the second I will talk about in a moment.

For now, the dataBinding parameter is the key to determine whether we are in data binding mode, meaning that a data source is bound.  The data source is provided from the Newsletter.GetAllNewsletters() provider method, and assigned to the data.  The next time around, provided it isn't bound to, is the ViewState being bound, which means the data source is an empty object array.

When using the provider, the DataBinding property is the key because DataSource will be not be used no matter what.  Unfortunately, the DataSource and DataSourceID properties do not help in this situation, because the data source is provided within the control instead of on the outside.

Listing 11

new private object DataSource
{
  get
  {
    return null;
  }
  set
  {
    throw new NotImplementedException();
  }
}
 
new private string DataSourceID
{
  get
  {
    return string.Empty;
  }
  set
  {
    throw new NotImplementedException();
  }
}

The table is the structure that has the header, footer, and items, and the CreateChildControls method above regulates when that all happens.  The CreateItem method is responsible for the creation of the actual items.

Listing 12

protected virtual DataboundItem CreateItem(Table table, object dataItem, int
  index, bool dataBinding)
{
  DataboundItem item = new DataboundItem(dataItem, index);
  table.Rows.Add(item);
 
  TableCell newsletterCell = new TableCell();
  item.Cells.Add(newsletterCell);
  TableCell buttonCell = new TableCell();
  item.Cells.Add(buttonCell);
  this.OnItemCreated(new DataEventArgs < DataboundItem > (item));
 
  if (dataBinding)
  {
    newsletterCell.Text = (string)dataItem;
    this.OnItemDatabound(new DataEventArgs < DataboundItem > (item));
  }
 
  LinkButton button = new LinkButton();
  button.Text = this.DeleteButtonText;
  button.CommandName = "delete";
  button.CommandArgument = this.Items.Count.ToString();
  buttonCell.Controls.Add(button);
  return item;
}

Notice that there is added logic that runs when data binding.  When in data binding mode, the cell is assigned the text value of the newsletter passed from the string array of newsletters.  Any other fields that would be assigned something would also be assigned here, but since there is one field, only one field is needed.  Lastly, when binding, the ItemDataBound event fires for the row.

The control overrides the OnBubbleEvent to capture any events that were raised, and process them accordingly.  This method could be used to raise a RowCommand event, similar to the GridView's event that fires for any outstanding commands, if so desired.  We will get to the HandleEvent method in a moment.

Listing 13

protected override bool OnBubbleEvent(object source, EventArgs args)
{
  CommandEventArgs commandArgs = args as CommandEventArgs;
  if (commandArgs != null)
    return this.HandleEvent(commandArgs);
  return false;
}

This control implements IPostBackEventHandler; any button control that makes use of the GetPostBackClientHyperlink method raises the click event to this method.  It is here that the command name/argument are passed in as the event argument, parsed, and passed to the HandleEvent method.

Listing 14

public void RaisePostBackEvent(string eventArgument)
{
  if (eventArgument.Contains("$"))
  {
    string[]parts = eventArgument.Split('$');
    CommandEventArgs args = new CommandEventArgs(parts[0], parts[1]);
    this.HandleEvent(args);
  }
}

The HandleEvent method received the command argument raised.  If inserting, the name/description is retrieved and the newsletter is added through the provider.  If deleting, the index of the row is used to receive the name of the newsletter from the cell in which it is rendered.  So if row zero's button is clicked, row zero's newsletter name stored in the cell is retrieved.

Listing 15

private bool HandleEvent(CommandEventArgs args)
{
  if (args.CommandName == "insert")
  {
    Page.Validate("NewsletterAdministration_Add");
    if (Page.IsValid)
    {
      string name = _newsletterName.Text;
      string description = _newsletterDescription.Text;
 
      Newsletter.AddNewsletter(name, description);
    }
  }
  else if (args.CommandName == "delete")
    Newsletter.RemoveNewsletter(this.Items[int.Parse
      (args.CommandArgument.ToString())].Cells[0].Text);
  else
    return false;
 
  base.RequiresDataBinding = true;
  this.DataBind();
  return true;
}

After each is run, the control is rebound. RequiresDataBinding ensures that the data binding occurs.


View Entire Article

User Comments

No comments posted yet.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2021 ASPAlliance.com  |  Page Processed at 2021-12-04 8:59:37 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search