A (less) simple include for ASP.NET

(c) 2003 Bertrand Le Roy In yesterday’s post, I published the code for a simple include method for ASP.NET that I’ve been using in a couple of places, only to realize that it was fine for what I was doing but probably not very useful beyond that.

So I spent some time today broadening its scope. It now supports nested includes (I modified the original post to reflect that change) and also setting properties on the control.

You can still do plain includes:

<% this.Include("contents.ascx"); %>

But now you can also set properties on the included user control using an anonymous object:

<% this.Include("contents.ascx", new {number=2, what="A"}); %>

The code for the helper considerably grew along the way but it’s still reasonable:

using System;
using System.Linq;
using System.Reflection;
using System.Web.UI;

public static class IncludeHelper {
    public static void Include(this TemplateControl host,
string virtualPath) { Include(host, virtualPath, null); } public static void Include(this TemplateControl host,
string virtualPath,
object properties) { var resolvedPath = host.ResolveUrl(virtualPath); var include = host.LoadControl(resolvedPath); if (properties != null) { var includeType = include.GetType(); foreach (var fieldInfo in
properties.GetType().GetProperties()) { var targetInfo = includeType.GetMember(
fieldInfo.Name, MemberTypes.Field |
MemberTypes.Property, BindingFlags.IgnoreCase |
BindingFlags.Instance |
BindingFlags.Public) .FirstOrDefault(); var targetPropertyInfo = targetInfo as PropertyInfo; if (targetPropertyInfo != null) { targetPropertyInfo.SetValue(
include,
fieldInfo.GetValue(properties, null),
null); } else { var targetFieldInfo = targetInfo as FieldInfo; if (targetFieldInfo != null) { targetFieldInfo.SetValue(
include,
fieldInfo.GetValue(properties, null)); } else { throw new ArgumentException( "There is no property of field with " +
"that name on the target control."
, fieldInfo.Name); } } } } using (var writer =
new HtmlTextWriter(host.Page.Response.Output)) { include.RenderControl(writer); } } }

I hope this helps. Again, if you are OK with the regular syntax to include a user controls, you probably shouldn’t use this and stick to what you know. I built this because I’m not entirely satisfied with that syntax and because I have a clear use case where it makes more sense. Make your own opinion and follow it :)

Note: The control is still not going into the control tree, which breaks lots of stuff. I don't really care that much about it but it may be important to you so I thought I'd point it out.

10 Comments

  • Cool, this is looking a lot like Rails' partial views. It would have a lot less Reflection cruft if you used C# 4.0 dynamics ... or if you wrote it in VB. ;)

  • Why is it that people don't prefer to Register their ascx file but persistently use include instead?

  • This is really interesting. I don't see a need for it right now, but I'm sure it's an awesome thing to know.

  • I have done this for a while now as i deal with a graphic designer who doesn't want me futzing too much with the aspx files...

    1. Add a UserControl that just has this:

    public string Path { get; set; }
    protected void Page_Load(object sender, EventArgs e)
    {
    this.Controls.Add(LoadControl(Path));
    }

    2. In web.config








    3. On any aspx page - regardless of the existence of the @Page directive or control registration etc....all you do is add the glabally registered control and enter the path of the control ... - postback etc. works as normal

    4. ???

    5. Profit.

  • @Brett: yes, I'm preparing the next post along those lines, except you'll also be able to set properties for the included control. :) And maybe something else after that...

  • I don't know: if you use this much it looks like an awful lot of extra reflection code to go through at runtime. Could be a real performance hog for not a lot of syntax savings. I'd like to see some load testing.

  • Just replace this as the UserControl in my comments above...

    System.Web.UI.Control control=LoadControl(Path);
    foreach (string key in this.Attributes.Keys)
    {
    PropertyInfo pinfo = control.GetType().GetProperty(key);
    pinfo.SetValue(control, Convert.ChangeType(this.Attributes[key], pinfo.PropertyType), null);
    }
    this.Controls.Add(control);

    then you can add properties like


    Good for standard types.

  • @Brett: yep. Still not getting IntelliSense on those properties. Working on an interesting solution, going almost full-circle to where we started...

  • Interesting read - I stumbled on using code blocks in Web user controls when I wanted to use one inside another, then the code blocks syntax was not evaluated. Perhaps this will do the trick for me.

    This didnt work:

    UserControl1.Ascx:
    <uc1:UserControl2 ID="uc2" runat="server" Text=""/>


    UserControl2.Ascx: (has public property "Text")

  • @Jonas: well the reason why your code doesn't work is fairly simple: to set-up a server control's properties, you need to use databinding syntax ( instead of ) and you need to call databind on the control.

Comments have been disabled for this content.