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

Reliably adding Controls or Markup before or after an ASP.NET Control?


:P
On this page:

In my last post I was waxing on about the problems with <%= %> markup in pages and content containers and the inability to use Control.Controls.Add() to add controls dynamically at runtime. If you do this you get a nasty error message that 'The Control Collection cannot be modified because the control contains code blocks'.

 

To put this in context, what I'm trying to do is to dynamically generate binding error messages as controls are unbound from databinding, and then display them without requiring to put validators on the page first. The idea is you add a DataBinder to the page, and the DataBinder extends any control on the page. You set binding properties a few possible validation settings (IsRequired) and then call DataBind() to bind the form controls, UnBind()to bind the data from the control back into the binding source. Any errors that occur, are picked up and dumped into a BindingError Collection. The errors also trigger the insertion of binding error messages on the controls. At the moment Controls.Add() is used which has the reliability problem mentioned above.

 

It works great – as long as there are no script <%=  %> markup tags on the page or content container. I mentioned yesterday some workarounds to make this work by using <%# %> Databinding expressions or wrapping the script code/tags into a separate server container tag like <div runat="server"> which effectively removes the script from the parsing container.

 

All that's all fine and good, but I want this to be a generic solution so these workarounds are not quite cutting it.

 

So, I started looking for some other solutions. I figured there would be some way to override the rendering of the control or rather hook the rendering and then inject markup afterwards. It looked like Control.SetRenderMethodDelegate() would work to hook the control's rendering, but as it turns out SetRenderMethodDelegate() is applied only to Container controls, and only fires for the control's inner content. It doesn't fire on typical controls like TextBox and even if it did it wouldn't do me any good to override the content rendering. I want to hook the 'outer' rendering so I can inject markup before or after the Control's Render() method fires.

 

So, there's no way to hook the rendering of a control, from outside of the control itself. This seems to be a pretty big omission in ASP.NET. The combination of the <% %> markup behavior of Control.Controls.Add() and the lack of some hook that allows hooking into the Rendering of a control externally seems really limiting. It makes it difficult to reliably build extender controls that might need to expand on the functionality of existing controls, which is exactly what I'm doing here.

 

So, question: Does anybody see a way to do what I'm trying to do, reliably beyond the hacky workarounds I've described here? Anybody know of a way to create pre or post markup for a control externally (ie. without overriding the control class itself)?


The Voices of Reason


 

Manuel Abadia
May 28, 2006

# re: Reliably adding Controls or Markup before or after an ASP.NET Control?

Have you looked at Control Adapters?

Essentially a control adapter is a class that hooks up in all the events in the ASP.NET life cycle normally used to modify the rendering of a control.

Douglas Hammon
May 28, 2006

# re: Reliably adding Controls or Markup before or after an ASP.NET Control?

Hi,

I’m currently working on my own data binding solution that is similar to yours. I’m also trying to figure out a way to display broken rule messages and icons for controls without having to subclass the controls :-). I have come up with one possible work around. It does not provide the flexibility of adding additional server side controls, but uses Javascript. I have not gotten very far, but the basic Javascript can be found below (I don’t know Javascript very well, so I’m sure there is a better way to do what I’m doing and make it work on different browsers). The important point is that it works! So far :-). I can send you a working example if you would like.

Obvious if someone can come up with a way to add controls to the Control Collection that would be a better solution.


Hope this helps.


<script type="text/javascript" language="javascript">
function DisplayBrokenRules()
{
// PATH TO WARNING IMAGE
var WarningImgPath = "Images/warning.gif";

/* CLASS NAME FOR SPAN TAG THAT WILL ENCLOSE THE CONTROL.
* THIS ALLOWS FOR ADDITIONAL FORMATING I.E. BACKGROUND COLOR, BORDER, ETC. */
var ClassName = "Error";

// LIST OF CONTROL ID'S THAT SHOULD DISPLAY AN ERROR MESSAGE
var controlIdArry = new Array("tbFirstName","tbAge")
var errorMesages = new Array("Please Enter First Name",
"Age must be greater then 0");

// LOOP THROUGH ARRAY OF CONTROLS TO DISPLAY ERROR MESSAGES
var i;
for (i in controlIdArry)
{
// GET CONTROL
var control = document.getElementById(controlIdArry[i]);

// CREATE SPAN TAG
var span = document.createElement('span');
span.className = ClassName;

// CREATE IMAGE TAG TO DISPLAY ERROR ICON
var img = document.createElement('img');
img.src = WarningImgPath;
img.title = errorMesages[i]; // DISPLAY ERROR AS TOOP TIP

// APPEND THE CONTROL INSIDE THE SPAN ELEMENT
span.appendChild(control.cloneNode(false));

// APPEND IMAGE TAG INSIDE SPAN ELEMENT
span.appendChild(img.cloneNode(false));

// REPLACE THE CONTROL WITH THE SPAN ELEMENT
control.parentNode.replaceChild(span, control);
}

}
</script>

Rick Strahl
May 28, 2006

# re: Reliably adding Controls or Markup before or after an ASP.NET Control?

Douglas - I was thinking about that last night, but I'm going to wait this out and see if there isn't a better solution. It would be pretty straight forward to generate a generic routine that could inject a function into the page and then call it with a ControlID and a couple parameters (message, message location) to then generate on the client.

I'm hesitant to rely on client scripting.

As to the Control Collection - that works as long as there's no <% %> markup on the page or Content Panel. I posted the code I use in the previous message.



Rick Strahl
May 28, 2006

# re: Reliably adding Controls or Markup before or after an ASP.NET Control?

Manuel - I hadn't though about Control Adapters. In fact, I haven't really looked at what they do until tonight <g>. I don't think this will help though because they are a generic solution that needs to be implemented for each specific kind of control. They also require configuration which is not workable for a generic solution.

Manuel Abadia
May 29, 2006

# re: Reliably adding Controls or Markup before or after an ASP.NET Control?

Rick, of course implementing an adapter for each kind of control you use is not an option.

I don't know if you can map all the controls to one adapter with a .browser file like:

<adapter controlType="System.Web.UI.Control"
adapterType="MyAdapter" />

so if that works maybe it can help you.

If it doesn't work, another possibility could be to write a control adapter for the page itself and modify how it render its child controls.

Rick Strahl
May 29, 2006

# re: Reliably adding Controls or Markup before or after an ASP.NET Control?

Manuel, I understand, but even if it was a single adapter statement that won't cut it for a generic control. You're just supposed to drop this thing on a form and it's supposed to work.

Mucking around with web.config is not really my idea of 'user friendly' <g>. Then there's also the issue of additional adapters getting loaded that might thwart the whole thing.

Thanks for the thought thought - forced me to at least take a look at control adapters. <sigh> - so much information and things to keep track of. Who can keep up, even in our ASP.NET world alone...

Steve from Pleasant Hill
May 29, 2006

# re: Reliably adding Controls or Markup before or after an ASP.NET Control?

I ran into this a month ago, got tired of fighting it. Trying to come up with a general, user friendly solution and it being this hard usually means the framework (or whatever the situation is) just isn't as far along as one would like.

Hopefully these scenarios get back to MS and the next version of the framework will gain ground.

I tend to avoid delicate balancing acts between elaborate client scripting and .NET.

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