This is a message I got from the UpdatePanel Shrinker in a site we built:
Shrinkage: from 70000 to 10 = 0.014%.


Almost a year ago I thought of a way of compressing the UpdatePanel asynchronous output based on the previously sent information and created the UpdatePanel Shrinker. I waited all this time to test it and also I've used it in some small projects.

From today, the project is on Github, with an MIT licence, that means do whatever you want with it, but I would appreciate some words from you.

As for the details: it uses a sort of fast and dirty home made diff algorithm to compare the previously sent output for an UpdatePanel with the current one. The problem is that the effect can only be seen from the second async postback on, but when used, it yields fantastic compression rates.

You can use it for sites that are accessed from Internet challenged locations, for sites that have complex Ajax interactions and for sites that you "Ajaxify". And before I get angry comments from purists, yes, I know it is more efficient to use Ajax in a smart way to solve each problem in the best possible way, but if you just want the quick and dirty solution, like a MasterPage with a ScriptManager, an UpdatePanel and the page content in it, the Shrinker is the thing for you.

Take care to look in the Debug Output window. The shrinker will output the compression rate and any warnings it might have.

I have this cool thing I made that needs a HttpHandler to process some commands. Therefore, in the code, I am trying to change the web.config whenever possible. I do it in the usual way:
WebConfigurationManager.OpenWebConfiguration("~");
... .Save();
. That should work fine, unless you don't have writing rights on the web.config file. But you don't need only that! I gave rights to the web.config file, but I got this silly error: "changing web.config An error occurred loading a configuration file: Access to the path 'bla...bla...bla...bla.tmp' is denied.".

In other words, in order to write to the web.config you also need rights to add files to the web folder in order to create temporary files. Hmm, I thought the temporary files folder was used for that... :-|

You are using a PopupControlExtender from the AjaxControlToolkit and you added a Button or an ImageButton in it and every time you click on it this ugly js error appears: this._postBackSettings.async is null or not an object. You Google and you see that the solution is to use a LinkButton instead of a Button or an ImageButton.

Well, I want my ImageButton! Therefore I added to the Page_Load method of my page or control this little piece of code to fix the bug:

private void FixPopupFormSubmit()
{
  var script =
    @"if( window.Sys && Sys.WebForms 
    && Sys.WebForms.PageRequestManager 
	&& Sys.WebForms.PageRequestManager.getInstance) {
  var prm = Sys.WebForms.PageRequestManager.getInstance();
  if (prm && !prm._postBackSettings) {
    prm._postBackSettings = prm._createPostBackSettings(false, null, null);
  }
}";
  ScriptManager.RegisterOnSubmitStatement(
    Page, 
    Page.GetType(), 
    "FixPopupFormSubmit", 
    script);
}



Hope it helps you all.

I wanted to use this Accordion control on a page and so I specified the ContentTemplate and HeaderTemplate and gave it a DataTable as a DataSource and DataBound it. Nothing! No errors, no warnings, no display of any kind. After a few frustrating minutes of trying to solve it I asked buddy Google about it and it turned out that the Accordion control MUST receive a DataView as a DataSource and nothing else. Using datatable.DefaultView solved it.

Update: it has come to my attention that these kind of errors appear only when debug="true" in the web config. In production sites it might work regardless.

You want to programatically load a js in a user control. The user control becomes visible in the page only during async postbacks and it is hidden at page load. So you want to use some method of loading the external javascript file during that asynchronous UpdatePanel magic.

Use ScriptManager.RegisterClientScriptInclude. There is a catch, though. For each script that you want to include asynchronously, you must end it with
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
else the Ajax.Net script loader will throw an error like: Microsoft JScript runtime error: Sys.ScriptLoadFailedException:
The script '...' failed to load. Check for:
Inaccessible path. Script errors. (IE) Enable 'Display a notification about every script error' under advanced settings.
Missing call to Sys.Application.notifyScriptLoaded().
.

For the aspx version, use the ScriptManagerProxy control:
    <asp:ScriptManagerProxy runat="server">
<Scripts>
<asp:ScriptReference Path="~/js/myScript.js" NotifyScriptLoaded="true" />
</Scripts>
</asp:ScriptManagerProxy>

So you did some work in an ASP.Net web site and then you decided to or to add the Ajax extensions to it or switch to ASP.Net 3.5 or whatever and suddenly you get Ajax errors like "Sys is undefined" or you don't see images and the css files are not loaded. And after googling like crazy and finding a zillion possible solutions you decide to enter in the browser address bar the url of the offending image, css file or even ScriptResource.axd files containing the ajax javascript and you see a beautiful ASP.Net error page displaying "Session state is not available in this context.". Huh?

There are some reasons why this might happen, but let's examine the one that actually prompted me to write the article. Someone made a change in global.asax, trying to work with Session there, more precisely in the Application_AcquireRequestState event. The error was returned by the Session property of the HttpApplication object, the one that the global.asax file represents! In fact, this error can only be thrown from there in the current ASP.Net implementations.

First mistake: there was a Session property available in the HttpApplication object and it was immediately assumed that it is the same as Page.Session or HttpContext.Current.Session. It is about the same, except it throws an error if the underlying Session object is null.

Ok, but why is Session null? You are a smart .Net developer and you know that the Session should be available in that event. Any documentation says so! Yes, but it applies to your page, not to all the IHttpHandler implementations, like ScriptResource.axd! Also, the css and image problems occured for me only when opening the site in Cassini, not in IIS, so maybe it opens separate threads to load those resources and ignores the session or something similar, at least at that level.

Well, how does one fix it? Adding in global.asax a condition on Session not being null throws the same error, so you have to add it on HttpContext.Current.Session. In other words:
if (HttpContext.Current.Session!=null) //do stuff with sessions

Maybe this problem occurs in other places as well, but only the Session property of the HttpApplication will throw the "Session state is not available in this context." error.

It has been a pain in the ass for me to use the graphical designer in Visual Studio. Instead I have used writing the markup of web pages and controls using the keys. It went several times faster, but still, it lacked the speed of any text editor I have ever seen. Moving up, down, left, right with the keys would make the system lag, jerk, etc. It was never too annoying to investigate until today.

What happends is that whenever you work in a VS window, it sends events to just about everything on the screen. The more windows you have open, the more it lags. And the culprit for this particular problem: the Properties window. Every time I was moving with the keys it tried to update itself with the information of the object I was moving over. Closing the Properties window fixed it! \:D/

I will write a really short post here, maybe I'll complete it later. I had to create some controls dynamically on a page. I used a ViewState saved property to determine the type of the control I would load (in Page_Load). It all worked fine until I've decided to add some ViewState saved properties in the dynamic controls. Surprise! The dynamically created controls did not persist their ViewState and my settings got lost on postback.

It's a simple google search away: dynamically controls created in Page_Load have empty ViewState bags. You should create them in Page_Init. But then again, in Page_Init the page doesn't have any ViewState, so you don't know what control type to load. It seemed like a chicken and egg thing until I tried the obvious, since the ViewState is loaded in the LoadViewState method, which is thankfully protected virtual, why not create my control there, after the page loads its view state? And it worked.

Bottom line:
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
CreateControl(ControlName);
}

Well, I was trying to use for the first time the Microsoft ReportViewer control. I used some guy's code to translate a DataSet XML to RDLC and ran the app. Everything went OK except for the export to Excel (which was the only real reason why I would attempt using this control). Whenever I tried exporting the report, the 'Asp.net Session expired' error would pop up.

Googling I found these possible solutions:
  • set the ReportViewer AsyncRendering property to false - doesn't work
  • use the IP address instead of the server name, because the ReportViewer has issues with the underline character - doesn't work, albeit, I didn't have any underline characters in my server name to begin with
  • set the maximum workers from 2 to 1 in the web.config (not tried, sounds dumb)
  • setting cookieless to true in the web.config sessionState element - it horribly changed my URL, and it worked, but I would never use that
  • setting ProcessingMode to local - that seemed to work, but then it stopped working, although I am not using the ReportViewer with a Reporting Services server
  • Because towards the end I've noticed that the problem was not an expiration, but more of a Session communication problem, I tried setting machineKey in web.config, although it doesn't work for the InProc setting. So it didn't work either.


For a few days, this blog post showed the last solution as working. Then it failed. I don't know why. I fiddled with the RDLC file a little (arranging columns and colors and stuff) and then it seemed to work again. I have no idea why.

I got mad and used Reflector to get the source code for the ReportViewer control and see where it all happends and why! I have found the following:
  • the error message looks like this:
    ASP.NET session has expired
    Stack Trace:
    [AspNetSessionExpiredException: ASP.NET session has expired]
    Microsoft.Reporting.WebForms.ReportDataOperation..ctor() +556
    Microsoft.Reporting.WebForms.HttpHandler.GetHandler(String operationType) +242
    Microsoft.Reporting.WebForms.HttpHandler.ProcessRequest(HttpContext context) +56
    System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181
    System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75
  • the error occurs in the constructor of ReportDataOperation:
    public ReportDataOperation():
    this.m_instanceID =
    HandlerOperation.GetAndEnsureParam(requestParameters, "ControlID");
    this.m_reportHierarchy =
    (ReportHierarchy) HttpContext.Current.Session[this.m_instanceID];
    if (this.m_reportHierarchy == null)
    throw new AspNetSessionExpiredException();
  • the Session object that makes the error be thrown is set in the SaveViewState() override method in ReportViewer
  • Apparently, the error occurs only sometimes (probably after the HttpApplication was restarted and during the debug mode of Visual Studio).


This reminded me of the time when I was working on a Flash file upload control and I used a HttpHandler to get the flash file from the assembly. Back then the control would not work with FireFox and some other browsers which would use different sessions for the web application and for getting the Flash from the axd http handler.

This time it works with FireFox and IE, but it fails in debug mode and only in IE. I am using IE8, btw.

My conclusion is that
  1. the ReportViewer control was poorly designed
  2. the ASP.Net Session expired error is misdirecting the developer, since it is not an expiration problem
  3. the actual problem lies in the inability of the ReportViewer control to communicate with the HttpHandler.
  4. The problem also could be related to the browser using separate threads to get the application and access the HttpHandler.

Actually, I think this applies to any dynamic modification of drop down list options. Read on!

I have used a CascadingDropDown extender from the AjaxControlToolkit to select regions and provinces based on a web service. It was supposed to be painless and quick. And it was. Until another dev showed me a page giving the horribly obtuse 'Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%@ Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.'. As you can see, this little error message basically says there is a problem with a control, but doesn't care to disclose which. There are no events or overridable methods to enable some sort of debug.

Luckily, Visual Studio 2008 has source debug inside the .Net framework itself. Thus I could see that the error is caused by the drop down lists I mentioned above. Google told me that somewhere in the documentation of the CascadingDropDown extender there is a mention on setting enableEventValidation to false. I couldn't find the reference, but of course, I didn't look too hard, because that is simply stupid. Why disable event validation for the entire page because of a control? It seems reasonable that Microsoft left it enabled for a reason. (Not that I accuse them of being reasonable, mind you).

Analysing further, I realised that the error kind of made sense. You see, the dropdownlists were not binded with data that came from a postback. How can one POST a value from a select html element if the select did not have it as an option? It must be a hack. Well, of course it was a hack, since the cascade extender filled the dropdown list with values.

I have tried to find a way to override something, make only those two dropdownlists not have event validation enabled. Couldn't find any way to do that. Instead, I've decided to register all possible values with Page.ClientScript.RegisterForEventValidation. And it worked. What I don't understand is why did this error occur only now, and not in the first two pages I have built and tested. That is still to be determined.

Here is the code

foreach (var region in regions)
Page.ClientScript.RegisterForEventValidation(
new PostBackOptions(ddlRegions,region)
);


It should be used in a Render override, since the RegisterForEventValidation method only allows its use in the Render stage of the page cycle.

And that is it. Is it ugly to load all possible values in order to validate the input? Yes. But how else could you validate the input? A little more work and a hidden bug that appears when you least expect it, but now even the input from those drop downs is more secure.

Update:
My control was used in two pages with EnableEventValidation="false" and that's why it didn't throw any error. Anyway, I don't recommend setting it to false. Use the code above. BUT, if you don't know where the code goes or you don't understand what it does, better use this solution and save us both a lot of grief.

I had this really old site that I was asked to "upgrade". Net 1.1 to 3.5. So I had to change old ADO.Net SqlConnection to Linq-to-Sql. A lot of unforseen issues. Sometimes it is close to impossible to recreate a simple SQL query without changing the database, as in the case of updating tables or views without primary keys.

Anyway, I got stuck in a very simple issue: How to sort a Linq query based on the string returned by the GridView Sorting event. After a lot of tries and Googling I found it was easier to use third party software (although it's more of a 2.5rd party software, as it is made by Scott Guthrie from Microsoft). Here is the link to the blog entry: Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library) . You can find there a sample project to download, with the LINQ Dynamic Library inside.

With this library and the OrderBy extension methods the code becomes:
var somethings=from something in db.Somethings....;
var data = somethings.OrderBy(Sort + " asc");
gv.DataSource=data;
gv.DataBind();

I had this calendar extender thingie in my site and the client was Italian so I had to translate it into Italian. Nothing easier. Just enable EnableScriptGlobalization and EnableScriptLocalization and set either the Web.config or Page culture settings.

However, the footer of the calendar kept showing "Today" instead of "Oggi". I checked the source and I noticed that it used a AjaxControlToolkit.Resources.Calendar_Today javascript variable to create the footer. Step by step I narrowed it down to the AjaxControlToolkit resources! Funny thing is that they didn't work. I tried just about everything, including Googling, only to find people having the opposite problem: they would have all these resource dlls created in their Bin directory and couldn't get rid of them. I had none! I copied the directories from the ACTK and nothing happened.

After some research I realized that the Ajax Control Toolkit does NOT include the option for multi language UNLESS in Release mode. I was using everything in the solution, including the ACTK, in Debug mode. Changing the AjaxControlToolkit build to Release in the solution made this work.

A few days ago a coworker asked me about implementing an autocomplete textbox with not only text items, but also images. I thought, how hard can it be? I am sure the guys that made the AutoCompleteExtender in the AjaxControlToolkit thought about it. Yeah, right!

So, I needed to tap into the list showing mechanism of the AutoCompleteExtender, then (maybe) into the item selected mechanism. The AutoCompleteExtender exposes the OnClientShowing and the OnClientItemSelected properties. They expect a function name that accepts a behaviour and an args parameters.

Ok, the extender creates an html element to contain the list completion items or gets one from the property CompletionListElementID (which is obsoleted anyway). It creates a LI element for each item (or a DIV in case of setting CompletionListElementID). So all I had to do was iterate through the childNodes of the container element and change their content.

Then, on item selected, unfortunately the AutoCompleteExtender tries to take the text value with firstChild.nodeValue, which pretty much fails if the first child of the item element is not a text node. So we will tap in OnClientItemSelected, which args object contains item, the text extracted as mentioned above (useless to us), and the object that was passed from the web service that provided the completion list. The last one we need, but keep reading on.

So the display is easy (after you get the hang of the Microsoft patterns). But now you have to return a list of objects, not mere strings, in order to get all the information we need, like the text and the image URL. Here is the piece of code that interprets the values received from the web service:
// Get the text/value for the item
try {
var pair = Sys.Serialization.JavaScriptSerializer.deserialize('(' + completionItems[i] + ')');
if (pair && pair.First) {
// Use the text and value pair returned from the web service
text = pair.First;
value = pair.Second;
} else {
// If the web service only returned a regular string, use it for
// both the text and the value
text = pair;
value = pair;
}
} catch (ex) {
text = completionItems[i];
value = completionItems[i];
}


In other words, it first tries to deserialize the string received, then it checks if it is a Pair object (if it has a First property) else it passes the object as value and text! If deserialization fails, the entire original string is considered. Bingo! So on the server side we need to serialize the array of strings we want to send to the client. And we do that by using System.Web.Script.Serialization.JavaScriptSerializer. You will see how it goes into the code.

So far we displayed what we wanted, we sent what we wanted, all we need is to set how we want the completion items to appear. And for that I could have used a simple string property, but I wanted all the goodness of the intellisense in Visual Studio and all the objects I want, without having to Render them manually into strings.

So, the final version of the AutoCompleteExtender with images is this: A class that inherits AutoCompleteExtender, but also INamingContainer. It has a property ItemTemplate of the type ITemplate which will hold the template we want in the item. You also need a web service that will use the JavascriptSerializer to construct the strings returned.

Here is the complete code:
AdvancedAutoComplete.cs

using System;
using System.IO;
using System.Web.UI;
using System.Web.UI.WebControls;
using AjaxControlToolkit;

namespace Siderite.Web.WebControls
{
/// <summary>
/// AutoCompleteExtender with templating
/// </summary>
public class AdvancedAutoComplete : AutoCompleteExtender, INamingContainer
{
private ITemplate _template;

[TemplateContainer(typeof(Content))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ItemTemplate
{
get { return _template; }
set { _template = value; }
}

protected override void OnInit(EventArgs e)
{
base.OnInit(e);
const string script = @"
function AdvancedItemDisplay(behaviour,args) {
var template=behaviour.get_element().getAttribute('_template');
//if (!template==null) template='${0}';
for (var i=0; i<behaviour._completionListElement.childNodes.length; i++) {
var item=behaviour._completionListElement.childNodes[i];
var vals = item._value;
var html=template;
for (var c=0; c<vals.length; c++)
html=html.replace(new RegExp('\\$\\{'+c+'\\}','g'),vals[c]);
item.innerHTML=html;
}
}

function AdvancedSetText(behaviour,args) {
var vals=args._value;
var element=behaviour.get_element();
var control = element.control;
if (control && control.set_text)
control.set_text(vals[0]);
else
element.value = vals[0];
}
"
;
ScriptManager.RegisterClientScriptBlock(this, GetType(), "AdvancedAutoComplete", script, true);

OnClientShowing = "AdvancedItemDisplay";
OnClientItemSelected = "AdvancedSetText";
}

protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
string template = GetTemplate();
((TextBox)TargetControl).Attributes["_template"] = template;
}

private string GetTemplate()
{
Content ph=new Content();
ph.Page = Page;
_template.InstantiateIn(ph);
HtmlTextWriter htw = new HtmlTextWriter(new StringWriter());
ph.RenderControl(htw);
return htw.InnerWriter.ToString();
}
}
}


MainService.cs

using System.Collections.Generic;
using System.Web.Script.Serialization;
using System.Web.Script.Services;
using System.Web.Services;

/// <summary>
/// Web service to send auto complete items to the AdvancedAutoComplete extender
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MainService : WebService
{
[WebMethod]
[ScriptMethod]
public string[] GetCompletionList(string prefixText, int count)
{
JavaScriptSerializer jss=new JavaScriptSerializer();
List<string> list=new List<string>();
for (int c = 0; (list.Count< count) && (c < 100000); c++)
{
string s = (c*c).ToString();
if (s.StartsWith(prefixText))
{
object[] item = new object[] {s, "images/demo.gif"};
list.Add(jss.Serialize(item));
}
}
return list.ToArray();
}
}



An example of the use

<asp:TextBox runat="server" ID="tbMain"></asp:TextBox>
<cc1:AdvancedAutoComplete ID="aceMain" runat="server" BehaviorID="mhMain" TargetControlID="tbMain"
ServiceMethod="GetCompletionList" ServicePath="~/MainService.asmx" MinimumPrefixLength="0"
CompletionInterval="0">
<ItemTemplate>
<asp:Image runat="server" ID="imgTest" ImageUrl="${1}" /><asp:Label runat="server"
ID="lbTest" Text="${0}"></asp:Label>
</ItemTemplate>
</cc1:AdvancedAutoComplete>


That's it! All you have to do is make sure the controls in the template render ${N} text that gets replaced with the first, second, Nth item in the list sent by the web service. The text that will be changed in the textbox is always the first item in the list (${0}).

Restrictions: if you want to use this a control in a library and THEN add some more functionality on the Showing and ItemSelected events, you need to take into account that those are not real events, but javascript functions, and the design of the autocompleteextender only accepts one function name. You could create your own function that also call on the one described here, but that's besides the point of this blog entry.

Usually when I blog something I am writing the problem and the solution I have found. In this case, based also on the lack of pages describing the same problem, I have decided to blog about the problem only. If you guys find the solution, please let me know. I will post it here as soon as I find it myself. So here it is:

We started creating some tests for one of our web applications. My colleague created the tests, amongst them one that does a simple file upload. She used the following code:
var fu = ie.FileUpload(Find.ByName("ctl00$ContentPlaceHolder1$tcContent$tpAddItem$uplGalleryItem$fuGalleryItem"));
fu.Set(UploadImageFile);

and it worked perfectly. She was using WatiN 1.2.4 and MBUnit 2.4.

I had Watin 2.0 installed and MBUnit 3.0. Downloaded the tests, removed the ApartmentState thing that seems not to be necessary in MBUnit 3.0, ran them.
On my computer the FileUpload Set method opens a file upload dialog and stops. I've tried a lot of code variants, to no avail; I've uninstalled both MBUnit and WatiN and installed the 1.2.4 and 2.4 versions. Tried all possible combinations actually, using .NET 1.1 and 2.0 libraries and changing the code. Nothing helped. On my computer the setting of the file name doesn't work.

I've examined the WatiN source and I've noticed that it used a FileUploadDialogHandler that determines if a window is a file upload window or not by checking a Style property. I have no idea if that is the correct solution, but just to be sure I inherited my own class from FileUploadDialogHandler and I've instructed it to throw an exception with a message containing the style of the first window it handles. The exception never fired, so I am inclined to believe that the handler mechanism somehow fails on my computer!

I have no idea what to do. I have a Windows XP SP3 with the latest updates and I am running these tests in Visual Studio 2008 Professional.

Update:
The only possible explanation left to me is that Internet Explorer 8 is the culprit, since my colleagues all have IE7. The maker of WatiN himself declared that identifying the windows by style is not the most elegant method possible, but he had no other way of doing it. My suspicion is that the window handling doesn't work at all in IE8, but I have no proof for it and so far I have found no solution for this problem.

Also, you might get a Sys.WebForms.PageRequestManagerServerErrorException with code 500 when using Ajax. It usually happends when you click on a button in a GridView or another bound control. You expect it to work, but it doesn't, even if the code is relatively clear.

The answer is (probably) that you are binding the container bound control every time you load the page (instead of only on !IsPostBack). The thing is this used to work in ASP.Net 2.0.

Bottom line: check your binding, see if you are not doing any DataBind in between the button click and the eventual event catch.