views:

1153

answers:

8

Summary

Hi All,
OK, further into my adventures with custom controls...

In summary, here is that I have learned of three main "classes" of custom controls. Please feel free to correct me if any of this is wrong!

  1. UserControls - Which inherit from UserControl and are contained within an ASCX file. These are pretty limited in what they can do, but are a quick and light way to get some UI commonality with designer support.
  2. Custom Composite Controls - These are controls that inherit from WebControl where you add pre-existing controls to the control within the CreateChildControls method. This provides great flexibility, but lack of designer support without additional coding. They are highly portable though since they can be compiled into a DLL.
  3. Custom Rendered Controls - Similar to Custom Composite Controls, these are added to a Web Control Library project. The rendering of the control is completely controlled by the programmer by overriding the Render method.

My Thoughts..

OK, so while playing with custom composites, I found the following:

  • You have little/no control over the HTML output making it difficult to "debug".
  • The CreateChildControls (and subsequent methods) can get real busy with Controls.Add(myControl) everywhere.
  • I found rendering tables (be it for layout or content) to be considerably awkward.

The Question(s)..

So, I admit, I am new to this so I could be way off-base with some of my points noted above..

  • Do you use Composites?
  • Do you have any neat tricks to control the HTML output?
  • Do you just say "to hell with it" and go ahead and create a custom rendered control?

Its something I am keen to get really firm in my mind since I know how much good control development can cut overall development time.

I look forward to your answers ^_^

+3  A: 

I say go ahead with the custom rendered control. I find that in most cases the composite can be easier done and used in a UserControl, but anything beyond that and you'd need to have a finer degree of control (pun unintended) to merit your own rendering strategy.

There maybe controls that are simple enough to merit a composite (e.g., a textbox combined with a javascript/dhtml based datepicker, for example) but beyond that one example, it looks like custom rendered controls are the way to go.

Jon Limjap
+1  A: 

I often use composite controls. Instead of overriding Render or RenderContents, just assign each Control a CssClass and use stylesheets. For multiple Controls.Add, I use an extension method:

//Controls.Add(c1, c2, c3)
static void Add(this ControlCollection coll, params Control[] controls)
 { foreach(Control control in controls) coll.Add(control);
 }

For quick and dirty rendering, I use something like this:

writer.Render(@"<table>
                   <tr><td>{0}</td></tr>
                   <tr>
                       <td>", Text);
control1.RenderControl(writer);
writer.Render("</td></tr></table>");

For initializing control properties, I use property initializer syntax:

childControl = new Control {  ID="Foo"
                            , CssClass="class1"
                            , CausesValidation=true;
                           };
Mark Cidade
+1  A: 

Using custom composite controls has a point in a situation where you have a large web application and want to reuse large chunks in many places. Then you would only add child controls of the ones you are developing instead of repeating yourself. On a large project I've worked recently what we did is the following:

  • Every composite control has a container. Used as a wrapped for everything inside the control.
  • Every composite control has a template. An ascx file (without the <%Control%> directive) which only contains the markup for the template.
  • The container (being a control in itself) is initialized from the template.
  • The container exposes properties for all other controls in the template.
  • You only use this.Controls.Add([the_container]) in your composite control.

In fact you need a base class that would take care of initializing a container with the specified template and also throw exceptions when a control is not found in the template. Of course this is likely to be an overkill in a small application. If you don't have reused code and markup and only want to write simple controls, you're better off using User Controls.

Slavo
+1  A: 

Slavo, thanks for the reply. I just want to clarify though so I can be sure I have understood your response correctly.

From what I can see, you are kind of mixing the benefits of the two into a sort of hybrid right? Taking the common functionality as a UserControl and then wrapping that, which allows more to be added to the "base" functionality?

My concern with this is that, you are still bound by the lowest common denominator of functionality (which is the UserControl) right? You still have ASCX files floating around that would be need to be maintained in parallell to the code library?

I hope I have understood, if not, please feel free to correct me! :)

Jon Limjap Thank you for your reply also. The advice seems entirely reasonable too (and along the same lines as my thoughts which is nice to know!).

Rob Cooper
+2  A: 

Here's another extension method that I use for custom rendering:

  public static void WriteControls
        (this HtmlTextWriter o, string format, params object[] args)
  { const string delimiter = "<2E01A260-BD39-47d0-8C5E-0DF814FDF9DC>";
    var controls  = new Dictionary<string,Control>();

    for(int i =0; i < args.Length; ++i)
     { var c = args[i] as Control; 
       if (c==null) continue;
       var guid = Guid.NewGuid().ToString();
       controls[guid] = c;
       args[i] = delimiter+guid+delimiter;
     }

    var _strings
       = string.Format(format, args).Split( new string[]{delimiter}
                                           ,StringSplitOptions.None);
    foreach(var s in _strings)
     { if (controls.ContainsKey(s)) controls[s].RenderControl(o);
       else o.Write(s);
     }
  }

Then, to render a custom composite in the RenderContents() method I write this:

protected override void RenderContents(HtmlTextWriter o)
  { o.WriteControls
         (@"<table>
               <tr>
                    <td>{0}</td>
                    <td><{1}></td>
               </tr>
             </table>"
            ,Text
            ,control1
           );
  }
Mark Cidade
A: 

marxidad, thanks for your replies. Sadly, we are not working in .NET 3.x so I am unable to use extension methods, but I do like the way you have used the format placeholder syntax do drop your own controls in.

Nice method :) +1's on both!

Rob Cooper
+2  A: 

Rob, you are right. The approach I mentioned is kind of a hybrid. The advantage of having ascx files around is that on every project I've seen, designers would feel most comfortable with editing actual markup and with the ascx you and a designer can work separately. If you don't plan on actual CSS/markup/design changes on the controls themselves later, you can go with a custom rendered control. As I said, my approach is only relevant for more complicated scenarios (and these are probably where you need a designer :))

Slavo
A: 

You might be able to make use of this technique to make design-time easier:

http://aspadvice.com/blogs/ssmith/archive/2007/10/19/Render-User-Control-as-String-Template.aspx

Basically you create an instance of a user control at runtime using the LoadControl method, then hand it a statebag of some kind, then attach it to the control tree. So your composite control would actually function like more of a controller, and the .ascx file would be like a view.

This would save you the trouble of having to instantiate the entire control tree and style the control in C#!

Andrew Myhre