I don't want you to think that I started working on ASP.Net MVC, but this article about a 36 year trek through backward compatibility hell was really funny and needed to be linked. What is so funny, I guess, is that it is factual and the humour is in the situation more than in the article itself.

On a separate note, ASP.Net 4.0 fixed this issue with a web.config setting.

A while ago I was writing about the annoying The Controls collection cannot be modified because the control contains code blocks (i.e. <% ... %>). error and how to fix it by using a PlaceHolder or another container to hold your code blocks you can resolve this.

But what if the ASPX code is not yours and you are only building the control? How can you get around the dreaded code blocks? First, let's try to understand the mechanism that renders the code blocks. When the markup code is read, a CodeDomTreeGenerator class is used to parse it. All DOM tree generators inherit from BaseTemplateCodeDomTreeGenerator which does the following: if the block read is a control, create the control and add it to the control collection, if it is a code block, generate a dynamic render method and use that. From that moment on, you can't change the control collection because (stupid, if you ask me) the render method has already been generated and it only knows about the controls it had then.

You can test if the ControlCollection object cannot be changed via the IsReadOnly property. If it is read only, code blocks have been added. Indeed, in the ControlCollection class, a private field is used to hold an error message and, if not null, it will be used as the exception message when trying to add or remove controls from the collection.

Are you in the mood for some insanity? Ok, let's unset the string via reflection, see what happens! Well, first of all, no error! You can manipulate the control collection at your leisure. The problem is that the render method is still the generated one. If you change the control collection weird stuff will happen, like not getting your control rendered or, if inserted or deleted, seeing controls rendered instead of others or being pushed out of the "rendering queue". So, what is we remove the render method as well? Then the normal Render mechanism will be used. That means that the code blocks will be completely ignored!

So, if you are a mean son of a bitch like myself, instead of begging junior programmers to never use code blocks or to encapsulate them at least, screw the controls so that they ignore that bad code. Not very smart, but oh, so mean :)

Here is a bit of code to remove the "readonlyness" of control collections:



public static class MSOAB
{
private static readonly FieldInfo _readOnlyErrorMsgFieldInfo =
typeof(ControlCollection).GetField("_readOnlyErrorMsg",
BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly PropertyInfo _rareFieldsEnsuredPropertyInfo =
typeof(Control).GetProperty("RareFieldsEnsured",
BindingFlags.Instance | BindingFlags.NonPublic);

private static FieldInfo _renderMethodFieldInfo;

public static void FixReadOnlyControlCollection(Control control)
{
if (control.Controls.IsReadOnly)
{

_readOnlyErrorMsgFieldInfo.SetValue(control.Controls, null);
var rareFieldsEnsured = _rareFieldsEnsuredPropertyInfo.GetValue(control, new object[] { });
if (_renderMethodFieldInfo == null)
{
_renderMethodFieldInfo = rareFieldsEnsured.GetType().GetField("RenderMethod");
}
_renderMethodFieldInfo.SetValue(rareFieldsEnsured, null);
}
}
}


This isn't really tested except the basic functionality and I haven't used it in a production environment, but it was fun as it was. I hope you enjoyed it as well.

Coincidence made it that this week people have asked me twice about how to preserve focus during postbacks, especially during auto postbacks. I didn't really know much about how ASP.Net does this other than building your own script, so I started investigating.

When you post back, there is a postback event reference that is used. The Page.ClientScript property, of the type ClientScriptManager, has a bunch of methods called GetPostBackEventReference which return different script strings for different options. The options are encapsulated into a PostBackOptions object which has, interestingly enough, a property called TrackFocus. Wow! Exactly what I wanted.

The problem comes when digging in the System.Web sources and seeing that no one actually sets this property. I did a bunch of code to add the script with the TrackFocus property and it seems it works. Not only does it preserve the focus before the postback, but also the scroll bars position. I tested in Internet Explorer, FireFox and Chrome and it worked on all.

So what is going on? Why does this feature, which I imagine is quite useful, seem only half done? And that since the era of .Net 2.0? I have no idea. I will post this method, applicable on TextBox controls, although I guess it could be applied on most if not all AutoPostback controls, that replaces the script for a normal AutoPostback with one that also preserves focus:

private void fixAutoPostBack(TextBox tb)
{
if (!tb.AutoPostBack)
return;
tb.AutoPostBack = false;
PostBackOptions options
= new PostBackOptions(tb, string.Empty)
{
TrackFocus = true,
AutoPostBack = true
};
if (tb.CausesValidation)
{
options.PerformValidation = true;
options.ValidationGroup = tb.ValidationGroup;
}
string onchange = string.Empty;
if (tb.HasAttributes)
{
onchange = tb.Attributes["onchange"];
if (!string.IsNullOrEmpty(onchange))
{
onchange = onchange.TrimEnd(';') + ";";
}
}
onchange += ClientScript
.GetPostBackEventReference(options, true);
tb.Attributes["onchange"] = onchange;
}

As you can see, the only thing I do differently from the normal AutoPostback code is to set TrackFocus to true.

This post should have been titled Have your cake and eat it, too, but people need to find it in Google.

First of all, a bit of theory.

Web controls in ASP.Net can persist their properties in markup in two ways: attributes and nested tags. The problem with attributes is that the type of the property may be complex and iself have properties.

ASP.Net does have a mechanism for specifying subproperties by using a hyphen to separate properties and their subproperties. For example the BackColor property of a row of a GridView can be specified as both BackColor="Red" in the RowStyle tag or as RowStyle-BackColor="Red" in the GridView tag. However, that way is pretty unreadable and cumbersome that is why using inner tags is sometimes necessary (and implemented by decorating the properties with [PersistenceMode(PersistenceMode.InnerProperty)]).

But using inner tags is not always possible! Imagine a control like Panel. Its contents are interpreted as controls that are supposed to be rendered, so an inner tag would just be rendered to the page and not interpreted. There is no way of making such a control (decorated in its class declaration with [ParseChildren(false)]) have inner properties, but one can use something like an ITemplate property so that the controls can be placed in a tag of their own.

Also, consider controls like the DropDownList, in which ListItems are placed directly in the control tag and it is assumed automatically that they are children of the Items collection. That is because the Items collection is marked as [PersistenceMode(PersistenceMode.InnerDefaultProperty)] which means all the children of the control are considered elements in the property. Adding an inner tag would raise an error, as it would not be a ListItem object.

Yet, there is a way! And finding it was like discovering the holly grail. I have only tested it on a ListControl, but it should work on templated controls as well.

Let's start with a class that inherits from ListControl, therefore it has the Items collection in it and adding a complex property to it. I will create a special class called InnerTagClass that has a Text property. The control that inherits from ListControl and which I will call BaseClass will have an InnerTag property of type InnerTagClass. The code would look like this:

public class BaseClass:ListControl
{
private InnerTagClass mInnerTag;

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[NotifyParentProperty(true)]
public virtual InnerTagClass InnerTag
{
get
{
if (mInnerTag==null)
{
mInnerTag=new InnerTagClass();
}
return mInnerTag;
}
set
{
mInnerTag = value;
}
}
}
Now go into the designer and modify the Text property of InnerTag. You will notice that it will be saved as a hyphen attribute, while adding items to the Items collection would render the list items as children in the BaseClass tag:

<local:BaseClass ID="BaseClass1" runat=server InnerTag-Text="Test">
<asp:ListItem>Test</asp:ListItem>
</local:BaseClass>
Now, an InnerDefaultProperty is still an InnerProperty. You can put the ListItem inside an Items tag and it will still work, and also you can add an InnerTag tag with a Text attribute and it will work as well! However, if you go into the designer and you change the Text for the InnerTag property you get the changed property as an attribute and the previous InnerTag tag, with the old value. That is not acceptable.

However, you can inherit from the BaseClass, override the Items and the InnerTag properties and change their persistence mode to InnerTag! Here is the code:

public class InheritedClass : BaseClass
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public override System.Web.UI.WebControls.ListItemCollection Items
{
get
{
return base.Items;
}
}

[PersistenceMode(PersistenceMode.InnerProperty)]
public override InnerTagClass InnerTag
{
get
{
return base.InnerTag;
}
set
{
base.InnerTag = value;
}
}
}
Now editing in the designer will create the Items tag and add the list items to it and an InnerTag tag with a Text attribute! But when you type it in source view, you get intellisense for both the Items tag and the ListItem tag! Adding only list items to the control works just like an inner default property.

There is a catch, though, as adding list items as direct children of the InheritedClass tag will not allow you to change the values of Items or InnerTag from the designer. It will save the current items in an Items tag, create the InnerTag, but fail to remove the previous items and thus throw a designer error. Still, fixing that is as easy as switching to source view and removing the old items.

Another possible question would be if one could add both InnerDefaultProperty and InnerProperty as decoration to a property in a class, but the answer is no. An error of Duplicate 'PersistenceMode' attribute is thrown.

Also, it might be possible to be able to fix that via a specialised editor, but I think it would be too complicated by far.

So there you have it: a way to satisfy both the styles of source people (like myself) and designer people by using inheritance and persisting differently the base property and its override.

There are all these attributes that can be used to decorate properties and classes so that the Property Grid control knows to handle them. One of these attributes is PersistenceModeAttribute, which specifies how to persist the information in ASP.Net markup. More often than not, you need to persist a complex object with subproperties as an inner tag. In that case one uses [PersistenceMode(PersistenceMode.InnerProperty)]. For the possible values of the constructor parameter, just follow the link if interested.

The problem comes when you want to persist the properties as attributes, in the ASP.Net specific "flattened" syntax, using a hyphen to separate property from subproperty. (<Style Color="red"/> as an inner tag is the same as Style-Color="red" as an attribute). You want this because some controls interpret their inner content as controls to be placed in them, such as the Panel control. You cannot add a Style tag in it because it just gets rendered in the page. The solution is to not use PersistenceMode! Of course, you might say, that's obvious. But it is not when your property already has the PersistenceModeAttribute decorating it and you are desperately looking for a parameter that does what you want. There is no such parameter. Not decorating the property is a separate option and it is the default behavior.

There are cases when pages need to use the same session, even if they are started from different contexts. One example is when trying to open a new window from within a WebBrowser control, or maybe issues with the ReportViewer control, or even some browsers who choose to open frames and new windows on different threads, like FireFox did for me a while ago. One might even imagine a situation where two different browsers open the same site and you want to use the same session. You have a SessionID, you are on the same server, so you should be able to use the session you want!

Here is how you do it:

var sessionID=(string)Request.QueryString["SessionIdentifier"];
if (Request.Cookies["ASP.NET_SessionId"] == null
&& sessionID != null)
{
Request.Cookies.Add(new HttpCookie("ASP.NET_SessionId", sessionID);
}

This piece of code must be added in the Global.asax.cs file (create a Global.asax file for your web site if you don't have one) in the void Global_PostMapRequestHandler(object sender, EventArgs e) handler and the sessionID must be given in the URL parameter SessionIdentifier.

Unfortunately you can't do it anywhere else. I've seen attempts to abandon the session in page load or page init and then do this, but it doesn't work. Basically, this post describes a horrible hack that replaces the default cookie where ASP.Net saves the SessionID value just before it is read.

As it can be a security risk, you should also add some validation logic so that the session hijacking is done only on certain pages that are likely to be opened in different application threads.

I was working on a small ASP.Net control that inherited from Button, so nothing special. I went into Visual Studio in design mode and I noticed that the control did not display any resize handles so I could only move the control, not resize it using the mouse.

After intensive Googling, trying custom ControlDesigner classes with AllowResize set to true and all other kinds of strange things, I realised that in the Render method I would write a style tag while in DesignMode before rendering the button. The method was described in a previous blog entry to fix the problem with CSS files with WebResource entries inside and PerformSubstitution.

It appears that Visual Studio, by default, only resizes the controls that render an HTML element that has size. A style tag does not! The solution was to add a div around the control (and the style block) while in DesignMode. The only problem was that the div is a block element, while the input of type button is an inline element. That means that two div elements would place themselves one under the other, not next to the other like two buttons would. So I fixed the problem by also adding a float style attribute and setting it to left.

Here is a small code sample:

protected override void Render(HtmlTextWriter writer)
{
if (DesignMode)
{
writer.AddStyleAttribute("float", "left");
writer.RenderBeginTag("div");
// this renders an embedded CSS file as a style block
writer.Write(this.RenderDesignTimeCss("MyButton.css"));
}
base.Render(writer);
if (DesignMode)
{
writer.RenderEndTag();
}
}

I was trying to make a web control that would be completely styled by CSS rules. That was hard enough with the browsers all thinking differently about what I was saying, but then I had to also make it look decent in the Visual Studio 2008 designer.

You see, in order to use in the CSS rules (or in Javascript resources, for that matter) files that have been stored as embedded resources in the control assembly, a special construct that looks like <%=WebResource("complete.resource.name")%> is required.

The complete resource name is obtained by appending the path of the embedded resource inside the project to the namespace of the assembly. So if you have a namespace like MyNamespace.Controls and you have an image called image.gif in the Resources/Images folder in your project, the rule for a background image using it is background-image:url(<%=WebResource("MyNamespace.Controls.Resources.Images.Image.gif")%>);.

Also, in order to access the CSS file you need something like [assembly:
WebResource("MyNamespace.Controls.Resources.MyControl.css",
"text/css", PerformSubstitution = true)]
somewhere outside a class (I usually put them in AssemblyInfo.cs). Take notice of the PerformSubstitution = true part, that looks for WebResource constructs and interprets them.

Great! Only that PerformSubstitution is ignored in design mode in Visual Studio!!. The only solution to render the control correctly is to render the CSS file yourself and handle the WebResource tokens yourself. Here is a method to do just that:

private static readonly Regex sRegexWebResource =
new Regex(@"\<%\s*=\s*WebResource\(""(?<resourceName>.+?)""\)\s*%\>",
RegexOptions.ExplicitCapture | RegexOptions.Compiled);

public static string RenderDesignTimeCss2(Control control, string cssResource)
{
var type = control.GetType();
/*or a type in the assembly containing the CSS file*/


Assembly executingAssembly = type.Assembly;
var stream=executingAssembly.GetManifestResourceStream(cssResource);

string content;
using (stream)
{
StreamReader reader = new StreamReader(stream);
using (reader)
{
content = reader.ReadToEnd();
}
}
content = sRegexWebResource
.Replace(content,
new MatchEvaluator(m =>
{
return
control.GetWebResourceUrl(
m.Groups["resourceName"].Value);
}));
return string.Format("<style>{0}</style>", content);
}

All you have to do is then override the Render method of the control and add:

if (DesignMode) {
writer.Write(
RenderDesignTimeCss2(
this,"MyNamespace.Controls.Resources.MyControl.css"
);
}


Update:
It seems that rendering a style block before the control disables the resizing of the control in the Visual Studio designer. The problem and the solution are described here.

I have this ASP.Net web control that has a property that is persisted as an inner property and sets some custom style attributes. Then there is another control that inherits from the first one and overloads this custom style property with another having the same name and returning a different type. All this is done via the new operator

Now, while this is perfectly acceptable from the standpoint of Object Oriented Programming and it compiles without any issues, I get a compile error when I actually use the property and save it in the aspx code. That is a limitation of ASP.Net and I could find no way to circumventing other than changing the name of the property. This would also be the case if you have two public properties that have the same name only differently cased.

While this second situation I understand, the first is really stupid. I am specifying the type of the control so there should be no issues with an overloaded property. I guess it has something to do with the persistence mechanism, but still... Annoying.

I have been working on a class that has some properties of type Color?, a nullable Color. I noticed that, in Visual Studio 2008, when I go to edit the properties in question in the property grid I get a dropdown of possible colors whereas if I go to edit a color on a WebControl, something like BackColor, I get a nice dialog window with the title 'More Colors' with colored hexagons from which to choose a color. Researching on the net I found out that if you want to use the ColorEditor in the property grid control you should decorate the property with [Editor(typeof(ColorEditor), typeof(UITypeEditor))]. I did that and, indeed, the editor was changed. However, it was not the one for BackColor, with its hexagonal design.

Reflecting the WebControl class I noticed that it didn't use any Editor decoration, but rather [TypeConverter(typeof(WebColorConverter))], so I added that and removed the editor on my property. Now the only editing option for my property was a simple textbox. Using both TypeConverter with the Editor option didn't have any visible effect either, it just used the normal editor.

As a last test I changed the type of the property to Color and decorated it only with the TypeConverter attribute and there it was, the 'More colors' editor. My only conclusion is that the editor is chosen by the property grid itself on Color properties decorated with the WebColorConverter. It must be hardcoded and thus it will never work on other types. Just stick with the ColorEditor option for nullable values.

Needless to say, I spent quite a lot of time compiling, closing and opening the designer on a test page and so on, so I hope this helps other people with a similar problem.

Whenever you build a more complex ASP.Net control you get to build templates. Stuff like collapsing panels, headered controls or custom repeaters are all usually working with templates, by having properties of the ITemplate type which then are instantiated into a simple control like a Placeholder or Panel. There are many tutorials on how to do this, so I won't linger on the subject.

What I find very difficult to achieve was to have a templated control that would just accept dropped controls and place them into a template, without having to enter in "template mode". First I will copy the entire code of a very simple control, then I will explain.

using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;

namespace Web.Controls
{
[Designer(typeof (TestControlDesigner), typeof (IDesigner))]
[ParseChildren(true)]
[PersistChildren(false)]
public class TestControl : WebControl
{
#region Properties

[EditorBrowsable(EditorBrowsableState.Never)]
[Browsable(false)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[DefaultValue(typeof (ITemplate), "")]
[TemplateInstance(TemplateInstance.Single)]
public ITemplate MainTemplate
{
get;
set;
}

#endregion

#region Private Methods

protected override void CreateChildControls()
{
base.CreateChildControls();
if (MainTemplate != null)
{
MainTemplate.InstantiateIn(this);
}
}

#endregion
}

public class TestControlDesigner : ControlDesigner
{
#region Member data

private TestControl mTestControl;

#endregion

#region Public Methods

public override void Initialize(IComponent component)
{
base.Initialize(component);
SetViewFlags(ViewFlags.TemplateEditing, true);
mTestControl = (TestControl) component;
}

public override string GetDesignTimeHtml()
{
mTestControl.Attributes[DesignerRegion.DesignerRegionAttributeName] = "0";
return base.GetDesignTimeHtml();
}

public override string GetDesignTimeHtml(DesignerRegionCollection regions)
{
var region = new EditableDesignerRegion(this, "MainTemplate");
region.Properties[typeof (Control)] = mTestControl;
region.EnsureSize = true;
regions.Add(region);
string html = GetDesignTimeHtml();
return html;
}


public override string GetEditableDesignerRegionContent(
EditableDesignerRegion region)
{
if (region.Name == "MainTemplate")
{
var service = (IDesignerHost) Component.Site.GetService(typeof (IDesignerHost));
if (service != null)
{
ITemplate template = mTestControl.MainTemplate;
if (template != null)
{
return ControlPersister.PersistTemplate(template, service);
}
}
}
return string.Empty;
}


public override void SetEditableDesignerRegionContent(
EditableDesignerRegion region,string content)
{
if (region.Name == "MainTemplate")
{
if (string.IsNullOrEmpty(content))
{
mTestControl.MainTemplate = null;
}
else
{
var service = (IDesignerHost)
Component.Site.GetService(typeof (IDesignerHost));
if (service != null)
{
ITemplate template = ControlParser.ParseTemplate(service, content);
if (template != null)
{
mTestControl.MainTemplate = template;
}
}
}
}
}

#endregion

#region Properties

public override TemplateGroupCollection TemplateGroups
{
get
{
var collection = new TemplateGroupCollection();

var group = new TemplateGroup("MainTemplate");
var template = new TemplateDefinition(this,
"MainTemplate", mTestControl, "MainTemplate", true);
group.AddTemplateDefinition(template);
collection.Add(group);
return collection;
}
}

#endregion
}
}


The above code is a pretty common way of making a templated control. We have a Control (TestControl) with one or more ITemplate properties (MainTemplate) and a ControlDesigner (TestControlDesigner) that decorates the TestControl class via the Designer attribute. The trick for the drag and drop to work with this scenario is in the last two methods.

First look at the GetDesignTimeHtml() override. There I did a little hack in order to set for the target control an attribute with the name DesignerRegion.DesignerRegionAttributeName and with the value of "0". This actually declares the entire control in the Visual Studio designer as the first (and only) "editable region". The region is registered in the GetDesignTimeHtml(DesignerRegionCollection regions) override and named MainTemplate.

Secondly, look at the TemplateGroups property override. This is in order for the "Edit Templates" action to appear in design mode. Since drag and drop would work very nicely, technically we don't need it. However, I noticed that if one tries to drag a control out of the templated one, it doesn't really work :). We still need Template Editing mode, but only in unlikely scenarios. Even if the template group is called "MainTemplate" it has no connection with the editing region.

Thirdly, look at the lots of attributes decorating the TestControl class and the MainTemplate property. An important one is [TemplateInstance(TemplateInstance.Single)], which says that there will be only one instance of the template, in other words, the controls can be treated as in a Panel and accessible from the code as protected members.

Finally, let's look at the methods that actually permit the drag and drop operation: GetEditableDesignerRegionContent, which has a region as a parameter, and SetEditableDesignerRegionContent, which also has a region, but also a string content parameter.

The trick lies in the conversion from this unwieldy string variable to a control tree and viceversa. The string is necessary because it is the XML/HTML as interpreted by the Visual Studio designer.

As you can see, in the get method the static class ControlPersister is used to transform the control tree into a string, while in the set method the static ControlParser class is used to do the reverse.

Hope this helps someone.

The thing to do is use .Net 2.0 web parts and override CreateEditorParts or implement IWebEditable on generic controls that are not web parts.

A good link on how to do that (once you know what to look for, duh!) is How to get EditorParts working in your WebParts.

Another link that summarises different solutions for several issues with web parts is WSS 3.0 webparts development. A quote that is relevant to this blog entry:

Toolpanes can be customized in a number of ways. A web part by itself can declare custom toolparts which show up in the toolpane by overriding the GetToolParts method (WSS WebPart) or CreateEditorParts (ASP.NET WebPart).

A developer can also customize the toolpane generically by supporting the ICustomizeToolpane interface (which allows you to redefine the structure of the toolpane UI) or supporting IAddToGallery (which allows you to add new gallery "tabs").


I am still working on clarifying that out as the documentation from MSDN is almost non existent on the subject. It appears that using ICustomizeToolPane is only supported for Sharepoint web parts and not for ASP.Net web parts. I yet know of no other way of changing the toolpane except by using some javascript from the web part editor (which would suck) and it might not even be possible.

Update: The rest of this post is not longer relevant. I believe it is the way it was done in the old Sharepoint days, when Sharepoint web parts were not based on ASP.Net 2.0.

Click to show anyway

Ok, I know I promised you some cool posts and I really was going to write them. However, I am in my own personal crisis and all because of Infragistics controls. Ok, I know that some people like to complain and blame others for their mistakes. I am tempted to do that all the time and I am not always succeeding in abstaining, but this time it is not a pointless blame shift, this is totally real.

Let me put it in no uncertain terms: Infragistics controls suck shitty balls! They are poorly made, badly documented, there is no architecture that one can talk about (except in funny anecdotes at parties) and they are a complete pain in the ass. After giving some time estimates of how long it would take me to finish up some tasks I ended up repeatedly doubling that time because of the stupidity of Infragistics controls. Have I mentioned enough the shitty balls? And the sucking? Because I don't feel completely satisfied.

A small example: class A is inherited by class B. The functionality of class B is implemented in class A as "if I am B, then...". Then other classes using the A class sometimes have checks to see if it is not B, so that they would behave in a completely different way. I am not a brilliant software architect or something, but this is simply insane!

What about the grid columns and rows that are NOT web controls, nor do they implement some events or overridable methods that control their rendering. Instead, the grid is asking questions like "is this a template column? Then add this to the rendered string" (so not even a Controls collection that one can manipulate). And if this weren't enough, I get to have to fix errors in the javascript from Infragistics by replacing whole functions, because there is no other way to get through. One step forward and two back, just like bloody Sisyphus!

I had expected this to take 5 days and it took 3 weeks and I am still not done. My self esteem is plummeting, I am losing my will to live, I feel like the only satisfaction I could have is to go to the Infragistics team and tear them limb by limb, laughing all the way. I sincerely wish all of their managers, sales people, developers and remote assholes that finished tasks without consideration for the other developers they were working for a slow and painful death.

The fixes and solutions I have found are so unavoidably ugly that I don't dare to publish them on the blog, so my advice for you if you use Infragistics controls is to throw them all away and start from scratch. It will take you longer, but in the end you get a good thing, satisfaction for a job well done and a maintainable code. Don't worry, you can be a completely useless developer and you will still do a better job.

On a brighter note, I may start some attempts to code in Mono or at least create stuff in a different domain that what I have worked so far, so good things will happen on the blog, only not in the immediate future.

It started with a simple request for a custom control to perform some validation. Seems easy enough, but it is not. Sure, there is an easy solution and that is to create a composite control, containing the original control and a normal validator, but that means you don't inherit from either control and you have to copy all the properties and methods you are interested in. If, as I did, you do not like this solution, you will soon find out that there are a lots of obstacles to be conquered if you want all the features of a validator, the first of which being that a control cannot easily change the Controls collection of its parent. It would be bad design. Therefore simply adding a Validator from inside the control does not work.

Let's start with what others did: Self Validating ASP.NET Text Box. This CodeProject article shows how you can build a control that is validated only on the server side and implements the IValidator interface. However, you will notice that it did not implement the ValidationGroup behaviour. And that is due to a simple fact: there is a method of the Page class called GetValidators(string validationGroup) that is used all around ASP.Net. It looks kind of like this:

public ValidatorCollection GetValidators(string validationGroup)
{
if (validationGroup == null)
{
validationGroup = string.Empty;
}
ValidatorCollection validators = new ValidatorCollection();
if (this._validators != null)
{
for (int i = 0; i < this.Validators.Count; i++)
{
BaseValidator validator = this.Validators[i] as BaseValidator;
if (validator != null)
{
if (string.Compare(validator.ValidationGroup,
validationGroup, StringComparison.Ordinal) == 0)
{
validators.Add(validator);
}
}
else if (validationGroup.Length == 0)
{
validators.Add(this.Validators[i]);
}
}
}
return validators;
}
Notice anything? There is no mention of IValidator, instead only BaseValidators are sought and the reason the IValidator approach works at all is the line before last where a validator is added to the list if the validationGroup parameter is empty. That is, it will work with IValidators but only on Buttons or other postback controls that have an empty ValidationGroup.

So let's start from the idea of the original article: an IValidator TextBox control with server validation only. For simplicity I will compare the value of the control with a constant string and fail the validation if the comparison fails.

Click to expand


You notice that the entire logic of this method is that the validators in the Page.Validators collection will be validated when need arises. In order to implement the ValidationGroup behaviour, we need to either fool the GetValidators method or to actually add a BaseValidator to the collection, not the IValidator control. I would have preferred the first method, but GetValidators is executed not only in Page.Validate but, being public, can also be executed in other situations (and in fact it is called by every control that wants to cause a postback).

My first attempt was to simply add a dummy validator to the collection, with the proper ValidationGroup set. It worked for the client side (we'll get to that) but then it failed miserably when Page.Validate tried to loop through the collection. I was forced to create my own validator control that inherits from BaseValidator. Do not worry, though, it is very lightweight and does not need to be added to the Controls collection. Here is the source:
Click to expand

public class DummyValidator:BaseValidator
{
private readonly IValidator _realValidator;

public DummyValidator(IValidator realValidator)
{
_realValidator = realValidator;
}

protected override bool EvaluateIsValid()
{
_realValidator.Validate();
return _realValidator.IsValid;
}

protected override bool ControlPropertiesValid()
{
return true;
}
}


Note: the following code has a property called ValidationGroup. The TextBox already has it, for its own AutoPostBack behaviour. I left it there, though, because you might want to use this on a control that doesn't have it natively.

Now the code for the ValidatedTextBox class looks like this:
Click to expand

private DummyValidator _dummyValidator;

protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (Page != null)
{
_dummyValidator = new DummyValidator(this){ValidationGroup = ValidationGroup};
Page.Validators.Add(_dummyValidator);
//Page.Validators.Add(this);
}
}

protected override void OnUnload(EventArgs e)
{
if (Page != null)
{
//Page.Validators.Remove(this);
Page.Validators.Remove(_dummyValidator);
}
base.OnUnload(e);
}

private string _validationGroup;

public string ValidationGroup
{
get
{
if (_validationGroup == null)
{
if (ViewState["ValidationGroup"] == null)
ValidationGroup = "";
else
ValidationGroup = (string) ViewState["ValidationGroup"];
}
return _validationGroup;
}
set
{
_validationGroup = value;
ViewState["ValidationGroup"] = value;
}
}


So the ValidationGroup is now working on the server side since there is an actual BaseValidator in the Validators collection calling the Validate method of our control. Since the DummyValidator class doesn't implement any specific behaviour, it can be reused for any type of validated control.

Now for the client side part. The client validation scripts work with a javascript array called Page_Validators. The validators are rendered as span elements (where the error message is displayed) and the array holds all these elements, with some extra properties like isValid, errorMessage, evaluationfunction, controltovalidate, validationGroup, focusOnError, enabled and display. The first three are like the IValidator members, only in javascript, controltovalidate is the client id of the control to be validated while focusOnError is used to scroll the page/container in order to see the invalid control without looking all over the page to find it.

All the ASP.Net validation functions are in a javascript file called WebUIValidation.js and embedded in the System.Web.dll library. We need to load it in order for validation to work. Then we need to render a span element and set its attributes. Lastly, but most important of all, we must create a function that will evaluate the validity of the control.

I will just post the entire source, since all the client code is practically copied from the BaseValidator code.

Click to expand


There are several things you need to take care of.
One of them is the client display of the validators. Normally, a validator's span element is rendered with the ErrorMessage as inner html. The ASP.Net framework then changes the visibility of the span depending on the validity of the control. I have chosen to not render the error message, as I wanted a different behaviour (in this case, a red background). The span element is still shown and hidden, but being empty, there is no visible effect.
Another thing is when you would use validation summaries. The error message is being rendered as a property of the span. It WILL be used in the summary, even if it is not normally displayed.

This code will work with almost no change for any control, so you could encapsulate it into a ValidatorHelper class or something. The only variants are the Validate method and evaluatefunction function.

Also note that having inherited from BaseValidator, I bypassed the normal functionality of the default validator controls. If you want to inherit from one of those, like RegularExpressionValidator, you might encounter problems, especially for controls that are not normally validated and for which the normal validator controls try to get their value. Take a look at the ValidationProperty attribute that you might need to use to decorate your control in order to work with default validators. Also, the javascript behaviour is to look for a control that has a value attribute. If not found it searches deeper into the children. So if you want to use a default validator, you might want to add script to keep a value attribute updated on the validated control HTML element.

And that was all.

The Infragistics web controls have a way of saving design time settings into XML files and then loading them. You can do this either by loading the presets in the designer or dynamically from code, using the LoadPreset method. It accepts Stream, TextReader and string parameters and it is pretty easy to use.

The problem is that it doesn't work in inherited controls! The explanation is that a piece of its code is searching for the root element of the XML file by getting the assembly defined TagPrefix and then adding to it colon and the name of the object.

In other words, I had a control that inherited from UltraWebGrid, and in the ASPX it looked like omega:OmegaGrid, when styling it with the designer and then saving the preset, it created an XML that has its root element . Since my assembly had no assembly TagPrefix defined, it was looking for ":OmegaGrid" and failing.

The solution was to add the TagPrefix assembly attribute: [assembly: TagPrefix("Super.Desktop.GUIControls.Web", "omega")] in the AssemblyInfo.cs file.