Design-Time Server Control Relationships

Published 23 April 07 07:34 PM

(originally posted to Absolute Opinion on July 25, 2005)

When I first began building server controls, I ran into a problem with regard to control dependencies. By dependencies, I'm not talking about parent-child or part-whole dependencies, but more relational collaborations, whereby controls could be loosely configured together at design time to send and receive messages to each other at runtime. At the time, I couldn't get it to work, so I resolved myself to configuring controls together in code behind and simply dealing with the frustration of having VS.Net sometimes delete my code (since it was in the InitializeComponent block). However, I stumbled back across that code today, and was determined to solve this problem - and I have an answer!

For a specific example, I have a derived DataGrid, which can be configured to run a specific query to a tabular data source, render the data, sort and page the data - all server-side. However, I wasn't happy with the asthetics of the built in DataGrid paging feature. Specifically, I don't like the fact that it is found in the footer of the grid. Therefore, I built a server control which is a detached, custom page navigator (it looks like the UI element found at the bottom of the MSDN newsgroups UI. I then needed to enable the grid to accept an instance of my custom navigator. The problem here is that if you create a property of type PageNavigator on the grid, the property browser will let you set it, but will not serialize it in either code behind or the page. On the other hand, we can specify the property as a string, but then loose the dropdown support of the property browser. On first glance, creating a type converter doesn't seem right either because the conversion routines seem to be more geared to creating new instances of value types from strings (and vice-versa). However, this in fact not the case, as the type converter also provides methods for giving the property browser a list of selectable values. Therefore, my converter looks as follows:

public class PageNavigatorConverter : StringConverter
{
  public PageNavigatorConverter(){}

  public override TypeConverter.StandardValuesCollection GetStandardValues(
    ITypeDescriptorContext context) {
    if (context == null  context.Container == null)
      return null;
    object[] containerCtrls = this.GetControls(context.Container);
    if (containerCtrls != null)
      return new StandardValuesCollection(containerCtrls);
    return null;
  }

  public override bool GetStandardValuesExclusive(
    ITypeDescriptorContext context) {
    return false;
  }
   
  public override bool GetStandardValuesSupported(
    ITypeDescriptorContext context) {
      return true;
  }
  
  private object[] GetControls(IContainer container) {
    ArrayList selectableIDs = new ArrayList();
    foreach(object o in container.Components) {
      PageNavigator nav = o as PageNavigator;
      if(nav == null  nav.ID == null  nav.ID.Length == 0)
        continue;
      selectableIDs.Add(String.Copy(nav.ID));
    }
    selectableIDs.Sort(Comparer.Default);
    return selectableIDs.ToArray();
  }
}

The method of interest is GetStandardValues. This method is called by the property browser to get a list of selectable values given a context. The context is a standard IContainer object. This interface allows the navigation of a set of components. Therefore, to generate a list of all navigator controls on the page, we simply need to check the type of the enumerated component. If it is a navigator, we add its ID to a list, and return that list - the list will then be displayed as a string on the property browser. From here, it's pretty self-explanatory as to how to deal with the property, as it is just another string. Here's the code inside the grid control.

[Browsable(false)]
public PageNavigator CustomNavigatorInternal {
  get {
    if(this._customNavigator == null) {
      if(this.CustomNavigator != null)
        this._customNavigator = this.Page.FindControl(
      this.CustomNavigator) as PageNavigator;
    }
    return this._customNavigator;
  }
}

[Browsable(true)]
[Category("Paging")]
[Description("A custom paging provider.")]
[TypeConverter(typeof(PageNavigatorConverter))]
public string CustomNavigator {
  get{return this._customNavigatorID;}
  set{this._customNavigatorID = value;}
}

Put in perspective, this is actually much simpler than my frustration made it seems - yet it's always the simple things ....

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# There and Back Again : Design-Time Server Control Relationships / 247 blogging said on May 19, 2008 1:44 PM:

PingBack from http://247blogging.info/story.php?title=There-and-Back-Again-Design-Time-Server-Control-Relationships

# Dating said on May 26, 2008 5:38 PM:

(originally posted to Absolute Opinion on July 25, 2005) When I first began building server controls, I ran into a problem with regard to control dependencies. By dependencies, I'm not talking about parent-child or part-whole dependencies, but more relationa

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

About hdierking

I am currently the Editor-in-Chief for MSDN Magazine. I joined Microsoft in 2006 as a product planner with the certification team at Microsoft Learning. Prior to that, I spent my career as a developer and later as an architect. My main technology passions include pretty much anything on language theory, agile development, and service-oriented architecture.
Page view tracker