Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Watch out for Collection property Setters in ASP.NET Controls


:P
On this page:

If you've ever run into the dreaded ASP.NET control error - Error Creating Control: control could not be set on <some Collection Property> in the ASP.NET designer that looks like this:

ControlError

you've probably run your head against the wall after a while of frustration.

Oddly, even though the designer bombs, the page will run just fine if you execute it at runtime. The control above actually has a control designer to handle rendering so unlike the common problem of the rendering code causing an error due to design time differences, here the problem is more low level.

In the example above the markup looks like this:

<ww:wwScriptContainer ID="scripts" runat="server">
    <Scripts>                                        
        <ww:Script Src="~/Scripts/jquery.js"  RenderMode="Header" AllowMinScript="true" />
        <ww:Script Src="~/scripts/wwScriptLibrary.js" RenderMode="Header" AllowMinScript="true" />
    </Scripts>
</ww:wwScriptContainer>

which is correct for this particular control. There are no errors, and as I said the control works just fine at runtime and the page renders fine in the browser with the script code properly embedded into the page. There's also a control designer that does nothing more than inherit from the generic ControlDesigner() class with no custom rendering code that could blow up in the designer.

The designer however chokes as soon as a script items is added to the control, either in the markup or via the property sheet collection editor.

So why the problem here? The issue is an insidious one because it's not debuggable. My original property declaration - the one that doesn't work - for the Script property in the control is as follows:

/// <summary>
/// Class that provides scripting embedding services that work with VS Intellisense

/// and provide various services (ResolveUrl syntax and compressed script service)
/// </summary>
[NonVisualControl, Designer(typeof(wwScriptContainerDesigner))]    
[ParseChildren(true, "Scripts")]
[PersistChildren(false)]
[ToolboxData("<{0}:wwScriptContainer runat=\"server\">\r\n\t<Scripts>\r\n\t</Scripts></{0}:wwScriptContainer>")]
public class wwScriptContainer : Control
{
    /// <summary>
    /// Collection of ScriptItems
    /// </summary>
    [Description("Collection of ScriptItems")]
    [Category("Script"),DefaultValue("")]        
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public List<HtmlGenericControl> Scripts
    {
      get { return _ScriptItems; }
      set { _ScriptItems = value; }  
    }
    private List<HtmlGenericControl> _ScriptItems = new List<HtmlGenericControl>();

...

}

Looks perfectly fine right? But it won't work in the designer. Worse, if I try to debug this control by using the control project as a startup project and using DevEnv.exe as my debuggable target, you only get a non-traceable error regarding an MDA violation:

LoaderLockError

There's obviously a problem but Visual Studio isn't of much help with this error - when this error occurs there's no call stack and no breakpoint to look at. MDA errors are apparently caused by managed code firing too early as part of unmanaged DLL initialization which results in this LoaderLock error. What this means in terms of ASP.NET Control development is that the control loading onto the page couldn't initialize properly and that is in fact what's causing the failure. Not much help that.

I have run into this problem before a long time ago when I built my wwDataBinder control and even longer ago when I built a Tab control all of which use collection properties. At the time I figured out the problem, but because it's so obscure promptly forgot it again only to have it bite me yet again on this control.

Collection Properties: Make sure they don't have a Setter!

So what was the problem? I eventually figured it out by looking at those older controls and noticing the following:

A collection property that is automatically serialized/deserialized by the ASP.NET designer CANNOT HAVE A SETTER property! Yup. You can't have a set {} method on a property or else the designer will cause the Cannot Create error. Remove the setter and voila the control works as expected:

    /// <summary>
    /// Collection of ScriptItems
    /// </summary>
    [Description("Collection of ScriptItems")]
    [Category("Script"),DefaultValue("")]        
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public List<HtmlGenericControl> Scripts
    {
      get { return _ScriptItems; }
      // set { _ScriptItems = value; }  // *** IMPORTANT: If set method exists, error occurs during designer loading.

    }
    private List<HtmlGenericControl> _ScriptItems = new List<HtmlGenericControl>();

Aargh. This is one of those little diddies that drives me crazy, because the error message gives really no indication of what's happening. Well, maybe with lots of imagination you could construe that wwScriptContainer could not be set on the Scripts property. I on the other hand spent an hour fucking around with various versions of the collection item class (originally a custom script class, then HtmlGenericControl) and the designer attributes thinking that something like a missing constructor was the problem. But no - the setter is the issue. I suspect what's happening is that the setter is getting fired before the control is initialized, as the control is deserializing from the markup and this is causing some inconsistent state in the class. It's hard to tell, since I can't debug into it, but I suppose it doesn't matter either - the solution is simple enough once you know to leave out the setter.

The question is how the heck do you figure out this issue  for the first time? Pure chance?

One more note: If you think you've fixed this particular problem and it's still not working in your Visual Studio instance, make sure you test in a new instance of Visual Studio. When I was writing up this code for this post I noticed that after I reintroduced the setter to capture the screen and show the code, the code started failing again. I then fixed the code and - it still kept failing in the designer. I was just about to pull the post from the Web when I tried debugging the control one more time and saw that the debugged instance worked just fine, while my active VS instance still kept bombing. I restarted my active instance and the control worked as expected. When seeing 'weird' behavior in designer scenarios it's often a good idea to restart Visual Studio from time to time just to be sure - it may simply be that some of the designer assemblies are not fully unloading. This even if you use the designers "Refresh" option or completely unloading and reloading a page! Don't be a fart - restart!

There you have it. Hopefully this will save somebody some time and by writing this up I might remember NEXT time... Fat chance, eh?

Posted in ASP.NET  

The Voices of Reason


 

Speednet
March 10, 2008

# re: Watch out for Collection property Setters in ASP.NET Controls

Thanks for the informative post Rick.

On your last issue -- that VS seems to keep old code running -- I have found that happens when using the built-in web server, rather than IIS. It exists as a small taskbar icon by the clock. If you click that icon and stop the built-in server, the next time you debug it will start a new instance and old code will be flushed. When you restarted VS, it shut down the web server, so next time try just shutting down the web server instead of the entire environment.

Rick Strahl
March 10, 2008

# re: Watch out for Collection property Setters in ASP.NET Controls

@Speednet - In this case I'm not using the built in Web Server. It shouldn't make a difference anyway though since that server runs external to Visual Studio. While VS debugs that process it attaches and detaches. It also should have no effect on design time behavior which is what I was referring above.

Nate
March 10, 2008

# re: Watch out for Collection property Setters in ASP.NET Controls

Rick,

Thanks as always for taking the time to write this stuff down.

Dave
March 21, 2008

# re: Watch out for Collection property Setters in ASP.NET Controls

Thanks, Rick. I was just struggling with this problem myself, and the setter was exactly it.

Omar
January 26, 2009

# re: Watch out for Collection property Setters in ASP.NET Controls

thanks,Rick for two weeks i was facing same problem , hop this tip will solve my problem

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024