There is an article here that explains it better than I would. Basically, the new Visual Studio is finally released in beta and is supposed to be the IDE for .NET and Vista.
The .NET Framework 3.5 (that is 2.85 in normal versioning? :D ) is also to be released here.

A previous post of mine detailed the list of ASP.Net controls that cannot be used with UpdatePanel and ASP.Net Ajax. Since I provided a fix for the validators earlier on, I've decided to try to fix the Menu, as well. And I did! At least for my problem which involved using a two level dynamic menu inside an UpdatePanel.
Here is the code:
<script>
function FixMenu() {
if (typeof(IsMenuFixed)!='undefined') return;
if (!window.Menu_HideItems) return;
window.OldMenu_HideItems=window.Menu_HideItems;
window.Menu_HideItems=function(items) {
try {
OldMenu_HideItems(items);
} catch(ex)
{
if (items && items.id) {
PopOut_Hide(items.id);
}
}
}
IsMenuFixed=true;
}
</script>

Now all you have to do is load it at every page load:
ScriptManager.RegisterStartupScript(this,GetType(),"FixMenu","FixMenu();",true);


Explanation: the error I got was something like "0.cells is null or not an object", so I looked a little in the javascript code, where there was something like " for(i = 0; i < rows[0].cells.length; i++) {" and rows[0] was null. All this in a function called Menu_HideItems.

Solution 1: Copy the entire function (pretty big) and add an extra check for rows[0]==null.

Solution 2: Hijack the function, put it in a Try/Catch block and put the bit of the original function that appeared after the error in the catch. That I did.

Did you get a "0.cells is null or not an object" Javascript error while trying to use UpdatePanel and something like Menu or TreeView? The reason is that some controls are incompatible with UpdatePanel! Most amazingly, the newest controls seem to be the least compatible.

You just copied a directory with an ASP.Net web site in your wwwroot directory, you created an ASP.Net application from IIS/Web Sites, yet you get an error, no matter what page you try to access: The web application you are attempting to access on this web server is currently unavailable.
It's an access issue. Give access to the directory to the ASPNET user.

Update 2nd of July 2008:
The solution below doesn't always work. For a validator to not work you need to have .NET 2.0 installed without installing Service Pack 1 for it and you also need to add the validator dynamically (so it is not there when the page is loaded, but it appears there during an async postback).

The problem lies in the BaseValidator class itself, after the postback, the validator is not in the Page_Validators array. I've tried rehooking the validators, even enumerating all validators in the page and manually entering them in the Page_Validators array. It does not work. Mainly because the html spans of the validators are not the same thing as the validators and stuff like the id of the control to validate and other details are never rendered.

So you absolutely need to install the .Net 2.0 SP1 in order to use UpdatePanels with validators.
=====

Today I've encountered a strange error, where the validation did work inside an UpdatePanel, but the validator message would not appear. This in a project where I used validation in GridViews inside their own UpdatePanel and they worked!

So I guess part of the reason the error occurs could be any one of these:
  • validated control and validator are both in a UserControl
  • validator is loaded at start of the page, it does not appear during Ajax calls, but it is replaced during Ajax calls
  • validated control is a ListBox, worse, an object inherited from a ListBox


Anyway, the true reason why the validators behaved in this fashion was that they were different from the html node of the validators. In other words, the Javascript array Page_Validators was filled with the validators correctly, did have the evaluation function, did return isvalid=false, but then, when the style.display attribute was changed to inline, the validator was not part of the html page DOM, as it was changed by Ajax.

Solution: a Javascript function that enumerates the validators, gets the validator DOM node by way of document.getElementById, stores the validator properties into this node and then replaces the element in the Page_Validators array.
Problem: where should one load it? Possible solutions include submit button onclick events, form onsubmit events and in the Page_Load method. I would not use onclick, since I should have added onclick events on all possible submit buttons. I would not use RegisterOnSubmitStatement, since it ran the code after checking if the validators are valid or not (even if the validation itself took place afterward; now that is weird). The only solution is to use Page_Load.

There are various ways of doing that, too, since you could use MasterPages, custom made Page objects and even HttpHandlers or HttpModules. You also could have custom controls or objects that don't have a reference to the Ajax ScriptManager object or even to the Ajax library itself. Yes, I know, I am very smart. In my project, all the pages were inherited from a custom Page object, also in the project. So it was relatively easy.

Here is the code:

// in Page_Load
string script=@"
if (typeof(Page_Validators)!='undefined')
for (var i=0; i<Page_Validators.length; i++)
{
// get DOM node
var vld=document.getElementById(Page_Validators[i].id);
if (vld) {
for (var key in Page_Validators[i])
// check if the Page_Validators element has extra attributes
// and add them to the node
if ((vld[key]==null)&&(Page_Validators[i][key]!==null))
vld[key]=Page_Validators[i][key];
// replace the Page_Validators element with the reconstructed validator
Page_Validators[i]=vld;
}
}
"
;
// get current ScriptManager for this page
ScriptManager sm=ScriptManager.GetCurrent(this);
if ((sm!=null)&&(sm.IsInAsyncPostback)) {
// if we did an Ajax postback, fix validators
ScriptManager.RegisterStartupScript(Page, GetType(),"FixValidators",script,true);
}

In order to access an object even after postbacks, you need to put it either in the Session or the ViewState. The ViewState is preserved only between postbacks, not between different pages and it is a Page property, so it is more efficient to use it. The problem with this method is that every object you put in the ViewState must be serializable.
So, the quick and dirty path: if you don't have strange custom serializing to do, all you have to do it to decorate the object with the [Serializable] flag, like this:
[Serializable]
public class MyDictionary StateDict:Dictionary<int,bool> {
}

Say you wouldn't have done this, you would have probably met with the "Class is not marked as Serializable". Duh!. However, in this situation above I have inherited from an object that implements ISerializable. I will get an error "The constructor to deserialize an object of type ... was not found". What that means is that the object must have a constructor that accepts two parameters, a SerializationInfo and a StreamingContext object. So we must add it to the object, like this:
[Serializable]
public class MyDictionary StateDict:Dictionary<int,bool> {

public MyDictionary(SerializationInfo info, StreamingContext context) : base(info, context) { }
public MyDictionary() {}

}

I added the second constructor because when adding a parametrized constructor, the default empty one is no longer inherited. So no more new MyDictionary() unless one adds it.

That does it! Please do check out the entire ISerializable interface documentation, since it requires, besides the constructor, a GetObjectData method, with the same parameters as the constructor, which controls the custom serialization of the object.

Update November 2014: Sift4 is here!! Check out the new improved version of Sift here: Super Fast and Accurate string distance algorithm: Sift4

Update October 6 2014: New stuff, compare Levenstein vs Sift here:

Algorithm:      Levenstein      Sift
String 1:   String 2:  

Result:



Update June 25th 2013: I've decided to play a little with the suggestions in the comments and check for validity. This was spurned by the realization that a lot of people use my algorithm. So, in order to celebrate this, here is the "3B" version of the Sift3 algorithm:
It is made in Javascript, this time, as it was easier to test and has the following extra features:

  • a maxDistance value that tells the algorithm to stop if the strings are already too different.
  • two pointers c1 and c2, rather than a single pointer c and two offsets
  • Instead of dividing to 2 the total length of the strings compared, now I divide it with 1.5. Why? Because this way the value is closer to the Levenshtein distance computed per random strings


Happy usage!
The variant I posted was totally buggy. I removed it. Just use sift3Distance.


Update: the Sift algorithm is now on Github.

A while ago I wrote an entry here about Sift2, an improvement of Sift, the original and silly string distance algorithm. Now I am publishing Sift3, which is way more accurate and even simpler as an algorithm.

I found out that my algorithm is part of a class of algorithms that solve the Longest Common Substring problem, therefore I calculated the LCS, not the distance, then the distance from the LCS. The result is way more robust, easy to understand and closer to the Levenshtein algorithm both on random strings and user databases. Not to mention that there is no goto in this one.

BTW, if you are looking for an algorithm that detects switched words, this is not it :) This just looks for typos and small regional differences between the strings. I mean, you could normalize the strings, so that words are ordered by some mechanism, then it would work because the words wouldn't be switched :)

I promise to work on a word switching algorithm, but not in the near future.
Without further ado, here is the code:

The C# code is a method in an object that has a private member maxOffset. As in Sift2 maxOffset should be around 5 and it represents the range in which to try to find a missing character.

public float Distance(string s1, string s2, int maxOffset)
{
if (String.IsNullOrEmpty(s1))
{
return String.IsNullOrEmpty(s2)
? 0
: s2.Length;
}
if (String.IsNullOrEmpty(s2))
{
return s1.Length;
}
int c = 0;
int offset1 = 0;
int offset2 = 0;
int lcs = 0;
while ((c + offset1 < s1.Length)
&&
(c + offset2 < s2.Length))
{
if (s1[c + offset1] ==
s2[c + offset2])
lcs++;
else
{
offset1 = 0;
offset2 = 0;
for (int i = 0;
i < maxOffset;
i++)
{
if ((c + i < s1.Length)
&&
(s1[c + i] == s2[c]))
{
offset1 = i;
break;
}
if ((c + i < s2.Length)
&&
(s1[c] == s2[c + i]))
{
offset2 = i;
break;
}
}
}
c++;
}
return (s1.Length + s2.Length)/2 - lcs;
}



And here is the T-Sql code. This version is actually an improvement of my original source, gracefully provided by Todd Wolf:

CREATE FUNCTION [DBO].[Sift3distance2]
(
@s1 NVARCHAR(3999),@s2 NVARCHAR(3999),@maxOffset INT
)
RETURNS FLOAT
AS
BEGIN
DECLARE @s1LEN INT,@s2LEN INT

SELECT @s1LEN=Len(Isnull(@s1,'')),@s2LEN=Len(Isnull(@s2,''))

IF @s1LEN=0 RETURN @s2LEN
ELSE
IF @s2LEN=0 RETURN @s1LEN

IF Isnull(@maxOffset,0)=0 SET @maxOffset=5

DECLARE @currPos INT,@matchCnt INT,@wrkPos INT,@s1Offset INT,@s1Char VARCHAR,@s1Pos INT,@s1Dist INT,@s2Offset INT,@s2Char VARCHAR,@s2Pos INT,@s2Dist INT

SELECT @s1Offset=0,@s2Offset=0,@matchCnt=0,@currPos=0

WHILE(@currPos+@s1Offset<@s1LEN AND @currPos+@s2Offset<@s2LEN)
BEGIN
SET @wrkPos=@currPos+1

IF(Substring(@s1,@wrkPos+@s1Offset,1)=Substring(@s2,@wrkPos+@s2Offset,1)) SET @matchCnt=@matchCnt+1
ELSE
BEGIN
SET @s1Offset=0

SET @s2Offset=0

SELECT @s1Char=Substring(@s1,@wrkPos,1),@s2Char=Substring(@s2,@wrkPos,1)

SELECT @s1Pos=Charindex(@s2Char,@s1,@wrkPos)-1,@s2Pos=Charindex(@s1Char,@s2,@wrkPos)-1

SELECT @s1Dist=@s1Pos-@currPos,@s2Dist=@s2Pos-@currPos

IF(@s1Pos>0 AND (@s1Dist<=@s2Dist OR @s2Pos<1) AND @s1Dist<@maxOffset) SET @s1Offset=(@s1Pos-@wrkPos)+1
ELSE
IF(@s2Pos>0 AND (@s2Dist<@s1Dist OR @s1Pos<1) AND @s2Dist<@maxOffset) SET @s2Offset=(@s2Pos-@wrkPos)+1
END

SET @currPos=@currPos+1
END

RETURN(@s1LEN+@s2LEN)/2.0-@matchCnt
END


It doesn't give the same exact results as my own code, yet the result is close enough and the speed is about 20% higher.

And thanks to Diogo Nechtan, the version in PHP:

function sift3Plus($s1, $s2, $maxOffset) {
$s1Length = strlen($s1);
$s2Length = strlen($s2);
if (empty($s1)) {
return (empty($s2) ? 0 : $s2Length);
}
if (empty($s2)) {
return $s1Length;
}
$c1 = $c2 = $lcs = 0;

while (($c1 < $s1Length) && ($c2 < $s2Length)) {
if (($d = $s1{$c1}) == $s2{$c2}) {
$lcs++;
} else {
for ($i = 1; $i < $maxOffset; $i++) {
if (($c1 + $i < $s1Length) && (($d = $s1{$c1 + $i}) == $s2{$c2})) {
$c1 += $i;
break;
}
if (($c2 + $i < $s2Length) && (($d = $s1{$c1}) == $s2{$c2 + $i})) {
$c2 += $i;
break;
}
}
}
$c1++;
$c2++;
}
return (($s1Length + $s2Length) / 2 - $lcs);
}


And thanks to Fernando Jorge Mota, the version in Python:



Also, here is the Javascript version, used in Mailcheck, by Derrick Ko and Wei Lu.

function sift3Distance(s1, s2) {
if (s1 == null || s1.length === 0) {
if (s2 == null || s2.length === 0) {
return 0;
} else {
return s2.length;
}
}

if (s2 == null || s2.length === 0) {
return s1.length;
}

var c = 0;
var offset1 = 0;
var offset2 = 0;
var lcs = 0;
var maxOffset = 5;

while ((c + offset1 < s1.length) && (c + offset2 < s2.length)) {
if (s1.charAt(c + offset1) == s2.charAt(c + offset2)) {
lcs++;
} else {
offset1 = 0;
offset2 = 0;
for (var i = 0; i < maxOffset; i++) {
if ((c + i < s1.length) && (s1.charAt(c + i) == s2.charAt(c))) {
offset1 = i;
break;
}
if ((c + i < s2.length) && (s1.charAt(c) == s2.charAt(c + i))) {
offset2 = i;
break;
}
}
}
c++;
}
return (s1.length + s2.length) / 2 - lcs;
}


Another implementation, this time in Java, by Eclesia:


You might also be interested in a customised version in AutoKey, by Toralf:



Thanks all for your comments and I look forward to more. Just tell me it worked or not and, most important, why. Good luck!

Update 2020 - most of the links here are dead, the things they referred to long forgotten. So much for "once you put it on the Internet it never disappears".

Having reached the 200th entry, I really wanted to write something cool, something interesting, something that sticks (and it ain't shit).

I thought of blogging Kartoo, a very nice - albeit slow - visual search engine that shows not only relevant links, but also the context items that link different pages.

But Kartoo is not personal enough, so I switched to YouTube, thought about blogging (yet another) female vocalist nu-metal with goth overtones band like the Italian band Exilia. Or something else, like the Turkish band maNga, or the Spanish Dead Stoned or Demiurgo. But this is a blog, not a video/music site.

Then I thought about programming; there must be something in the three projects I am working on worth blogging about, or at least something important like Don't use the .NET Random class when concerned about security. But then again, the blog is full of (I hope) interesting programming hints.

What else is there? Ranting about bycicle lanes the city hall is building on the sidewalk and on which old people are happy to walk (slowly) without losing themselves;
interesting conceptual games like BoomShine, Straight Dice or Stickman Fight and how they can be improved;
the BBC Baghdad Navigator, to show you the distribution and timeline of Baghdad bombings;
the Lilium song for the anime Elfen Lied;
the Coma article on Wikipedia (I didn't write it);
coming improvements in the Sift3 algorithm;
InuYasha manga reaching chapter 500;
the new Google/Kartoo/Wikipedia searches for any selected text in the blog;
how I am reading Il Nome de la Rosa and The Name of the Rose in the same time, trying to grasp more of the Italian language;
Gwoemul, a very nice South Korean film...

No, there is too much to choose and I can't decide. I think I will skip entry 200 entirely.

and has 0 comments
I've found this interesting article by John Cronan about using the Abstract Factory pattern to access files, no matter if they are on FTP, HTTP or the local or networked file system. Basically he uses WebRequest.Create rather than any *Stream* class.

Interesting enough, he seems to be the only one providing a solution to the problem of accessing local file system resources when the default access rights do not allow you to, even if the logged on credentials would normally give you the access, thus solving an issue of the FileWebRequest class. Unfortunately he uses P/Invoke, which kind of contradicts the whole "more flexible than thou" approach of the article.

Overall an interesting read which gives you flexibility of file support, while taking away some of the specific advantages like seeking or appending. It's a definitely better approach than StreamReader and the ugly "URI formats are not supported." error.

A bonus for using this method is that it is compatible with the Office 2007/Vista Open Packaging addressing model, by way of the PackWebRequest class.

Ok, so you have the greatest control library ever made and Microsoft releases Asp.Net Ajax and none of them work anymore. What is one to do?

Eilon Lipton to the rescue! He writes a very good article about Ajax enabling your controls without linking to the System.Web.Extensions dll.

However, the article is a bit outdated. Here is a piece of code that solves the problems (at least for the latest version of Asp.Net Ajax):
Type scriptManagerType = Type.GetType("System.Web.UI.ScriptManager, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", false);
 if (scriptManagerType != null)
 {
 RegisterClientScriptResourceMethod = scriptManagerType.GetMethod("RegisterClientScriptResource", new Type[] { typeof(Control), typeof(Type),typeof(string) });
 RegisterStartupScriptMethod = scriptManagerType.GetMethod("RegisterStartupScript", new Type[] { typeof(Control), typeof(Type), typeof(string), typeof(string), typeof(bool) });
 }


This is because the namespace has changed since the writing of Elion's article from Microsoft.Web.UI to System.Web.UI and there are two methods named RegisterClientScriptResource and two named RegisterStartupScript so you have to get the right one. Else you get the "Ambiguous match found" error.

There you have it!

The .NET validation framework has two parts, the client Javascript validation and the server validation. That means that the Javascript code needs a value to validate and the server validation needs a property to validate.

So, first step, you create your web user control by putting some controls in it. Then, you want to add a validator to the page to reference the newly created user control. And you get the error "Control '{0}' referenced by the ControlToValidate property of '{1}' cannot be validated.". Why? because every control to be validated needs to be decorated with the ValidationProperty attribute:
[ValidationProperty("Text")]
public partial class ucDate : System.Web.UI.UserControl

Adding the first line to the control tells the validation framework to use the Text property of the UserControl.

Next step, you run the page and you notice the javascript doesn't work. The client validation works on html controls, by looking (recursively) for a 'value' attribute. When one looks at the source code, though, there is no html control that has the id of the user control. It doesn't use a span or a div to encapsulate its controls. All the controls have the id to show they are children to the user control, but the actual user control does not appear in the html source. So what is there to do?

<div id='<%=ClientID %>'></div>

You put all the controls in the ascx file of the User Control into this div. There you go! The validation works!

There is one more quirk regarding web user controls that have more children that render an html object with a 'value' attribute. In that case, remember that the validation starts from the very top, in our case the div. One could build simple javascript functions on the onchange or onsubmit javascript events, for example, to add a value attribute to the div. Best way would be using the onsubmit event, but be careful that the validation sequence also runs on the onsubmit event.

TextBox2.Attributes["onchange"]="document.getElementById('"+ClientID+"').value=this.value";


On popular demand, here is a complete example codeThis is a control that holds two TextBox controls. The control will be validated both on server and client by the value of the second Textbox, the first will be ignored.


using System;
using System.Web.UI;

[ValidationProperty("SecondTextboxValue")]
public partial class vuc : UserControl
{
public string SecondTextboxValue
{
get { return tbValidated.Text; }
}

protected void Page_Load(object sender, EventArgs e)
{
string script =
string.Format(
@"var vuc=document.getElementById('{0}');
var tb=document.getElementById('{1}');
if (vuc&&tb) {{
tb.vuc=vuc;
tb.onchange=function() {{ this.vuc.value=this.value; }}
}}"
,
ClientID, tbValidated.ClientID);

ScriptManager.RegisterStartupScript(Page, Page.GetType(), UniqueID + "_submit", script, true);
}
}


The ascx looks like this:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="vuc.ascx.cs" Inherits="vuc" %>
<div id="<%=ClientID %>"> value="<%=tbValidated.Text%>"
<asp:TextBox ID="tbIgnored" runat="server"></asp:TextBox>
<asp:TextBox ID="tbValidated" runat="server"></asp:TextBox>
</div>


How it works:
  • The javascript validator will look for an html element with the same id as the user control. If it has a value attribute, it will be validated, else it will go to the next control in the hierarchy. If the containing div would not have a value attribute, then the validation would have occured on the first textbox value, as the first element that has a value attribute. That's why the value attribute will be set on textbox change and when first loading the page.
  • The server validation will work because of the user control property that exposes the Text value of the second textbox and the ValidationProperty attribute that decorates the code.


I've seen many a forum and blog entries that look like the title of this entry. I was frantically trying to find the solution for this a few hours ago and found a lot of questions of the same type, most of them abruptly ending into nothing. No solution, only the questions. What was I to do? Debug!

The problem was that buttons with __doPostBack seemed to work and those with WebForm_DoPostBackWithOptions did not. I pressed them and nothing happened.

Debugging the hell out of the two javascript functions (both used by NET 2.0, on who knows what conditions) I realized that the problem was not in the javascript functions! The problem was a false one.

The real problem is in the validators. If you have a validator on a field and the field has some wrong values in it and both field and validators are hidden, the submit will not work and you will not see what the problem is. Let me make this simple: if you have problems with submit buttons that seem not to work, check your validators!

Now, why the field was hidden and its values filled and the validator enabled is a problem, but that I can fix easily. The "Oh, I am so stupid" phenomenon probably stopped a lot of people posting the solution after they found it.

Update: If you've experienced random PageRequestManagerParserErrorException errors that seem to vanish at a simple page refresh, read this post instead: An intermittent PageRequestManagerParserErrorException

I've built a TranslationFilter object that tries to.. well... translate ASP.Net pages. Everything ran smoothly until I had to use ASP.Net Ajax. I got the infamous error "Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.".

Starting analysing the problem, I soon understood that the Ajax requests go through the same Response mechanism as a normal page request, but the output is different. In case of normal page requests you get the HTML of the page, while in Ajax requests you get something formated like this:
contentLength|controlType|controlID|content|


If one uses Response.Write, the text is inserted both in the page HTML and the Ajax request format, resulting in something like "blablabla10|updatePanel|UpdatePanel1|0123456789" which cannot be parsed correctly and results in an error. The ScriptManager.IsInAsyncPostBack property shows us if the request is Ajax or not, so we can condition the Response.Write on this.

Also, if changing the content with a HttpResponse.Filter, the length of the content is no longer equal with the declared value. So what must be done is first detect if the content is Ajax. Unfortunately we cannot check the state of the ScriptManager from inside the HttpResponse.Filter, but we can check the format of the string to modify, then modify the content AND the contentLength, else it will all result in error.

Update: the content might not be changed by you! As one of the people asking me for help on the chat discovered, the web server provider might want to put in some ads, regardless if the request is an Ajax one, thus breaking the format. You need to patch the javascript ajax engine in order to work, that means changing the content the javascript function will get in order to not cause errors. You may find the solution here.

As an example, here is my Translate method:
        private string RecursiveTranslateAjax(string content)
        {
            Regex reg = new Regex(@"^(\d+)\|[^\|]*\|[^\|]*\|",
                         RegexOptions.Singleline);
            Match m = reg.Match(content);
            if (m.Success)
            {
                int length = To.Int(m.Groups[1]);
                reg = new Regex(
                         @"^(\d+)(\|[^\|]*\|[^\|]*\|)(.{" + length + @"})\|"
                         , RegexOptions.Singleline);
                m = reg.Match(content);
                if (m.Success)
                {
                    string trans = Translate(m.Groups[3].Value);
                    return trans.Length + m.Groups[2].Value 
                       + trans + "|"
                       + RecursiveTranslateAjax(content.Substring(m.Length));
                }
            }
            return Translate(content);
        }


Update:
I met this problem also when in the page there were Unicode characters. Everything works perfectly, then you can't postback anything, because some user text contains Unicode chars. The solution I used for this was to get the offending text (whether in Page.Render or in some other places based on specific situations) and take every character and check if it is ASCII. Web Pages should be UTF8 so any character bigger than 127 should be translated into a web page Unicode char &#[char code];

The code:

string s=[my string]
StringBuilder sb=new StringBuilder();
for (int c=0; c<s.Length; c++)
{
if (s[c]>127) sb.Append("&#"+((int)s[c])+";");
else sb.Append(s[c]);
}
s=sb.ToString();


Here is the full code
    private string RecursiveTranslateAjax(string content)
    {
        // look for the basic Ajax response syntax
        Regex reg = new Regex(@"^(\d+)\|[^\|]*\|[^\|]*\|", 
              RegexOptions.Singleline);
        Match m = reg.Match(content);
        // if found, search deeper, by taking 
        // into account the length of the html text
        if (m.Success)
        {
            // custom method to get an integer value
            int length = To.Int(m.Groups[1]); 
            reg = new Regex(@"^(\d+)(\|[^\|]*\|[^\|]*\|)(.{" + length + @"})\|",
                  RegexOptions.Singleline);
            m = reg.Match(content);
            if (m.Success)
            {
                string trans = Translate(m.Groups[3].Value);
                return
                    trans.Length + m.Groups[2].Value + 
                    trans + "|" + 
                    RecursiveTranslateAjax(content.Substring(m.Length));
            }
        }
        // if not Ajax, just translate everything,
        // it must be a normal PostBack or a string of some sort.
        return Translate(content);
    }
 
    // this method only fixes the weird characters
    // but you can put here any string change you would like
    // like search and replace some words.
    private string Translate(string content)
    {
        // Html code all chars that are not ASCII, thus getting rid of strange or Unicode characters
        StringBuilder sb = new StringBuilder();
        for (int c = 0; c < content.Length; c++)
        {
            if (content[c] > 127) sb.Append("&#" + ((int) content[c]) + ";");
            else sb.Append(content[c]);
        }
        return sb.ToString();
    }
 
    protected override void Render(HtmlTextWriter writer)
    {
        //base.Render(writer);
        // render to my own text writer
        HtmlTextWriter tw=new HtmlTextWriter(new StringWriter());
        base.Render(tw);
        // get the Rendered content of the page
        string content = tw.InnerWriter.ToString();
        content = RecursiveTranslateAjax(content);
        writer.Write(content);
    }


To.Int method
public static int Int(object o)
{
    if (o == null) return 0;
    if (IsNumericVariable(o)) return (int) CastDouble(o);
    string s = o.ToString();
    if (s == "") return 0;
    Match m = Regex.Match(s, "(-\\d+|\\d+)");
    if (m.Success)
        try
        {
            return Int32.Parse(m.Groups[0].Value);
        }
        catch
        {
        }
    return 0;
}
private static double CastDouble(object o)
{
    if (o is byte) return (byte) o;
    if (o is int) return (int) o;
    if (o is long) return (long) o;
    if (o is float) return (float) o;
    if (o is double) return (double) o;
    if (o is decimal) return (double) (decimal) o;
    throw new ArgumentException("Type is not convertable to double: " + o.GetType().FullName);
}

First of all, to turn a normal ASP.NET application to Ajax takes only a few minutes with the Microsoft Ajax.Net platform. It's as easy as moving some of the content in UpdatePanels and adding a ScriptManager. Of course, you need the Ajax.Net package installed.

What I am going to talk about is a simple way to enable/disable Ajax, and keeping the entire basic functionality intact. Why would I do that? Because sometimes you need to see the project working on a computer that doesn't have the Ajax framework installed. You might even want to work on the project itself. So this is what you do:

First of all, the ScriptManager control must appear on every Ajax page in the project, so why not move it to the MasterPage? Yes. Only one ScriptManager in the MasterPage suffices. Second of all, the UpdatePanel and the UpdateProgress controls are nothing more than normal Panels with the cool Ajax functionality added to them. So enabling or disabling Ajax surmounts to nothing more than replacing these controls with normal panels.

So here is the quick method of enabling, disabling Ajax.Net whenever you want:
Start off from the Ajax enabled application and save the web.config as web.config.ajax. Remove everything from it that resembles System.Web.Extensions and all other weird things that Ajax.Net adds to the web.config except this:
<system.web>
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</controls>

this you only replace with this:
<system.web>
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="MockAspNetAjax"/>
</controls>

Save it to Web.config.noajax

You might see where I am going already. Now create a new Visual Studio project, a class library, one that you will use for all these conversions, and call it MockAspNetAjax. Go to the project Properties and change the default namespace to System.Web.UI. Add three classes that do nothing but inherit from Panel: ScriptManager, UpdatePanel, UpdateProgress. The UpdateProgress will have some additional code:
 public UpdateProgress()
{
Init += new EventHandler(UpdateProgress_Init);
}

void UpdateProgress_Init(object sender, EventArgs e)
{
Visible = false;
}

because you don't want to see the Ajax Update Progress message continuously on your page.

In order to convert an ASP.NET Ajax application to a normal postback application you follow two simple steps:
1. overwrite the web.config with web.config.noajax
2. add MockAspNetAjax as a reference or include in the project if previously excluded.

back to Ajax:

1. overwrite the web.config with web.config.ajax
2. remove MockAspNetAjax as a reference or exclude the dll from the project, while keeping the dll there.

That's it!

Of course, Ajax has a lot more stuff to it, like for example the [ScriptService] web services that are directly accessed from Javascript. Or Ajax enabled controls or other controls which don't even work without the framework. These are more complex situations which cannot be solved with such a general solution, but the same basic principles apply: use web config to avoid Register tags in each page, replace controls with mockup controls, remove HttpHandlers and HttpModules, replace javascript code that uses Ajax with something that does nothing or emulates the same behaviour (preferably) through postbacks or hidden iframes, etc.

In ASP.NET a Control has a fully qualified id that can be deduced from the control hierarchy and can be accessed with the properties ClientID or UniqueID. It then becomes the unique id or name of rendered html controls. It makes sense that these properties should be used right after the control hierarchy is completely defined, that means before the rendering, therefore in the PreRender.

What is not so well known is that accessing those two properties sets the _cachedUniqueID member, which sets irrevocably the ID to a control. That's why using these properties in ItemCreated events, for example, makes the html id of controls to remain the default defined one.

Example: In a DataList, you have an item template that contains a control, let's call it Control1. The rendered id in html will look like this: ctl00_ContentPlaceHolder1_UcUserControl_DataList1_ctl00_Control1 , but if you use ClientID inside the DataList_ItemCreated event, the rendered html id will be just Control1, thus making any javascript manipulation futile.

Of course, one could create a method to return the UniqueID without setting the cached value, since there are moments when the partial hierarchy is enough to define a proper id. Unfortunately, for controls without a specific and declared id, ASP.NET creates and automatic ID like ctl[number] or _ctl[number] and, of course, those methods and fields are all private or internal. One could use Reflection to get to them, but what would be the point?
UniqueID and ClientID are overridable, though, so one can change their behaviour in user defined controls.

Related links:
Accessing ClientID or UniqueID too early can cause issues