Today I had a hell of a time trying to debug a problem with an ASP.Net server control. The Control was working just fine at runtime, but I noticed that in the VS.Net designer it always showed as an error button (red text name of the control rather than the rendered control). In fact, it was a specific interface I was creating and implementing on a set of controls and all controls with this interface were failing in the designer.
Figuring out problems of this sort are tricky at best because if your control performs any sort of sophisticated app or framework specific logic its quite likely that it won't properly Render in design time. It helps to understand that at DesignTime the designer instantiates the control, calls the constructor assigns any property values, fires OnInit and then fires Render() (there may be more but those are the ones I noticed - note that other event hooks like OnLoad and OnPreRender() don't fire at design time).
As you might expect this can be problematic as you essentially get a control that's firing only on half of its cylinders, right? <g>
One very helpful thing is to be able to detect desingmode which I wrote about last month.You basically can check HttpContext.Current being null to see whether you are in DesignMode. I tend to set a property called designmode in the constructor:
this.DesignMode = (HttpContext.Current == null);
With that you can appropriately block code from firing that's application specific and requires objects that might not exist without an actual application running.
As mentioned the key things to watch for are:
- OnInit Code
- Render Code
- Any Property values that fire code and get assigned from the designer
OnInit code is pretty straight forward - if there's anything non-self contained just bracket it out with the DesignMode property.
Render is a good one to provide your control with a decent design time display. If you have complex controls that have child controls or collections etc. it might be nice to actually have to control render all the child items etc. This works fine as long as the control is mostly self contained. However, I've found for many controls especially in a framework it's often a good idea to just render somesort of default display.
For example, I've been working on a generic translation switching control that ties into a framework. Because the translation code relies on a lot of different pieces of information that are based on the framework to render completely I opted for simply rendering out a string that displays a label and a single entry listbox.
protected override void Render(HtmlTextWriter writer)
{
if (this.DesignMode)
{
// *** Just render an approximation of what it will look like
writer.Write("Please choose a language: <select style='width:100px'><option>English</option></select>");
}
base.Render (writer);
The runtime code (which fires in OnLoad() and thus doesn't even fire at design time) on the other hand would create a label control (so it's localizable) and fills the list with values of all available languages. For UI positioning services however the above code is completely sufficient to dump the control.
Ok, all of this is somewhat logical, but here comes the part where I got hung up on yesterday for a couple of hours. If there is any code in the methods you are dealing with that call into components that cannot be loaded or accessed at designtime - even if the code doesn't run directly, the code will blow up.
What's happening is that I had some code that I knew wouldn't work at designtime in a Property Set. So I had the part that was a problem wrappered with the DesignMode flag to keep the code out of it. The control still would not render at designtime. Now if I went in and used a constant false value for the If statement it worked!
Well, it turns out that the Property Set was calling a Property Get of another property which was referencing another component in an external assembly that isn't necessarily available. The compiler can't load the other assembly, and voila our error occurs where the control cannot be loaded in the designer.
It's frustrating as hell to have a problem like this though because first off the problem is 4 or 5 levels removed from the actual control I was building. But most frustrating is the fact that there is no error message that gives you any sort of indication what's wrong. Remotely debugging a control in the designer is a hit or miss situation too - I've had it work on several occasions, but couldn't get any breakpoints to hit yesterday. Which makes sense in hindsight - the error was a compiler error so the control never bothered to even instantiate probably. But tell me that WHILE I'm trying to debug the thing <g>...
Other Posts you might also like