Thursday, September 27, 2007

Tuesday, September 25, 2007

NumberStyles Enumeration - AllowHexSpecifier

This is from MSDN - http://msdn2.microsoft.com/en-us/library/system.globalization.numberstyles(vs.80).aspx and it works like described.  But I expected it to allow "0x". 

AllowHexSpecifier

Indicates that the numeric string represents a hexadecimal value. Valid hexadecimal values include the numeric digits 0-9 and the hexadecimal digits A-F and a-f. Hexadecimal values can be left-padded with zeros. Strings parsed using this style are not permitted to be prefixed with "0x".

Conditional UpdatePanel and Event Validation

Many people find it mysterious when they get a message box with the following error message:

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.

Unfortunately ASP.NET does not provide enough information to troubleshoot this exception easily.  I am going to explain reasons that can cause this exception and give a tip on how to troubleshoot it.

The event validation is designed to prevent malicious requests from breaking your software.  When a server page is rendered and sent to the browser web controls tell ASP.NET about all allowed values that the web page can submit back to the server.  The collected values are hashed and stored in the hidden input in the web page.  When the postback values are received from the client postback values are validated against the hashes stored in this hidden input.

<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWDALLu5z7BQKdkKesBQL0pYTCDAL1pYTCDAL2pYTCDAL3pYTCDALwpYTCDALxpYTCDALypYTCDALjpYTCDALspYTCDAL0pcTBDAn2Nrc2z9979msu0SdahUlFp5dq" />

If server encounters any value with a hash which is among hashes in the __EVENTVALIDATION field it throws an exception with the message above.  If you develop with pure ASP.NET and receive such exception it usually means that someone attempts to tamper a request and does it incorrectly.

With the ASP.NET AJAX extensions however it becomes possible to get this exception as a result of developer mistake.

I've constructed a sample which demonstrates what you have to do (really you should not do) to get this weird message box. 

<%@ Page Language="C#" AutoEventWireup="true" %>

<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<
html xmlns
="http://www.w3.org/1999/xhtml">
<
head runat
="server">
<title>Conditional Update Panel and Event Validation</title
>
</
head
>
<
body
>
<form id="form1" runat
="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"
/>
<div
>
<asp:UpdatePanel runat="server" ID
="FirstUpdatePanel">
<ContentTemplate
>
<asp:Button runat="server" ID="ClickMeButton" Text="Click Me!"
/>
</ContentTemplate
>
</asp:UpdatePanel
>
<asp:UpdatePanel runat="server" ID="UpdatePanel" UpdateMode
="Conditional">
<ContentTemplate
>
<asp:DropDownList runat="server" ID
="DropDownList">
</asp:DropDownList
>
</ContentTemplate
>
</asp:UpdatePanel
>
</div
>
</form
>
</
body
>
</
html
>

<
script runat
="server">

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
int startCounter = (int?)ViewState["Counter"] ?? 0;
startCounter++;
ViewState["Counter"] = startCounter;

DropDownList.Items.Clear();
for (int i = startCounter; i < startCounter + 10; i++)
{
ListItem listItem = new ListItem();
listItem.Value = i.ToString();
listItem.Text = i.ToString();
DropDownList.Items.Add(listItem);
if (i == startCounter)
{
listItem.Selected = true;
}
}
//UpdatePanel.Update(); // Uncomment this line to fix the sample!
}
</script
>


If you run this sample and click the button twice you will see exactly the same message box.  So, what is causing ASP.NET to throw an exception in the sample above?

The sample changes items in the DropDownList on each postback and changes them in a way that previously selected item disappears from the list on the next postback.  (Similar things happen if you implement cascading dropdowns with autopostbacks).  If you disable partial rendering by setting ScriptManager property

EnablePartialRendering="false"

you will see that the page works perfectly.  DropDownList tells ASP.NET about all values in the items collection while it is been rendered and ASP.NET successfully validates submitted value against hashes stored in the "event validation" hidden input on the next postback.  If you re-enable partial rendering in this sample, one small but still important thing changes.  Render method of the DropDownList is still invoked on every postback, but output of the render method is not sent to the browser, because it is in the UpdatePanel which is not updated in response to button click.  (I intentionally made button to cause the asynchronous postback without updating second UpdatePanel).  Now, __EVENTVALIDATION hidden input is successfully updated with new hash values for DropDownList, but the DropDownList itself remains unchanged on the client and still submits old selected value on the next postback.  When the server receives this value it cannot validate it against the new set of hashes in the "event validation" hidden input and throws he exception.

So, the conclusion is very simple: "Don't change controls during the async postback that are outside an update panel or are in the update panel which is not updated during this asynchronous postback."  Or vice versa: "If you have conditional update panels force them to update their client state if you change controls inside the update panel." (See commented code in the sample and try uncommenting it).

Downloadable version of the sample: EventValidation.zip

Troubleshooting


It is not always easy to find what causes the "event validation failed" problem in the complex ASP.NET page.  I recommend following steps to find the control causing the problem:


  1. Open Visual Studio and configure your website to run in debugging mode, if haven't done it already.

  2. Be sure that your Visual Studio is configured to show exceptions when they are thrown
     Enabling Exceptions

  3. Disable "Exception Assistance" and "Just My Code" debugger features in the options dialog box
    Setting Debugging Options

  4. Start your web site under the debugger and reproduce the exception (you may get some other internal ASP.NET exceptions while starting the website - click "continue" until you get the "event validation failed" exception
    Exception

  5. Click on break button and open "call stack" tool window
    CallStack

  6. The selected stack frames on the following screen-shot tells you that the problematic control is DropDownList
    SmallCallStack

  7. Unfortunately it is not always possible to find what particular control causes the exception, but for DropDownList it is typically possible to do.  Double click the DropDownList.LoadPostData stack frame and open "Locals" debug tool window.
    Locals
    You should be able to expand "this" value.  (I don't know if you can find a stack frame with accessible this variable for other controls. )

  8. The final step is to get the UniqueID of the failing control.  Just open a QuickWatch and type UniqueID there.
    QuickWatch

Now when you know which controls causes the problem you should check:


  • whether you change properties of this controls and whether you need to change them.  If so, 

  • whether this control is in the update panel and whether this update panel is updated during the asynchronous postback

More Cases Causing the Same Problem



  • DropDownList must not be in the update panel to cause the problem.  It is enough to change its Items collection during asynchronous postback.

  • Do you know other ways to get this exception with Microsoft ASP.NET AJAX Extensions?

Monday, August 27, 2007

WebClient: Handling Cookies

WebClient class is very useful when you need to download or upload date from or to the Web.  However, when you need to make a sequence of calls you find that WebClient does not preserve cookies set by the server between requests.  Fortunately, WebClient gives you an opportunity to handle cookies by yourself. 

The very simple solution is to override GetWebRequest method of the WebClient class and set CookieContainer property.

Here is my implementation:

    public class CookieAwareWebClient : WebClient
{

private CookieContainer m_container = new CookieContainer();

protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request is HttpWebRequest)
{
(request as HttpWebRequest).CookieContainer = m_container;
}
return request;
}
}

Saturday, August 18, 2007

Calling Virtual Method of a Class Base to Base Class

You will probably never really need to do this, but if it happens, here is how it can be accomplished.

So, assume you have two classes Base and Derived.  The Base class introduces a virtual method named Test.  The Derived class overrides this method.

Now, as I said in very rare circumstances, you may need to derive another class from the Derived class and in the overridden method Test call original version of Test method.  By original I mean implementation provided by the Base class.

C# and VB.Net, the most popular .Net languages do not provide any way to write something similar to:

class DerivedFromDerived : Derived
{
public override void Test()
{
base.base.Test();
}
}

When you call a base class version of Test() C# compiler generates:

.method public hidebysig virtual instance void Test() cil managed
{
.maxstack 8
L_0000:
nop
L_0001:
ldarg.0
L_0002:
call instance void Derived::Test()
L_0007:
nop
L_0008:
ret
}


The goal is to make compiler generate:


L_0002: call instance void Base::Test()


The syntax of method name used in IL reminded me that C++ always allowed invoking an implementation of the very base class.  And indeed, the straightforward solution is to implement it in C++/CLI:

    public ref class DerivedFromDerived: public Derived
{
public:
virtual void Test()
override
{
Base::Test();
}
};

This C++ code generates verifiable safe IL code. 

Wednesday, August 1, 2007

Restoring Lost Focus in the Update Panel with Auto Post-Back Controls

UpdatePanel control is used for different purposes like reducing flickering of the page and reducing network traffic generated by a web site. Developers often wrap input boxes into an UpdatePanel to implement cascading drop-downs and update other related controls.  Cascading drop-downs can be implemented with a help of Ajax Control Toolkit control extenders, but in general case you will either need to write script code or place controls in an update panel.  The latest approach is very easy to implement, but it also has a lot of drawbacks.  If you wrap input boxes (and other input controls) in the update panel, you must be aware about the following consequences:

  1. If a user changes value of an auto post-back control and "tabs" to the next control, he or she starts asynchronous (AJAX) post-back.  However, as the post-back is asynchronous the user can continue typing (selecting value) in the next selected control. If the server responds to asynchronous post-back after the user has started typing in the next control, everything typed in this control is lost once AJAX post-back completes.

    If the server responds quite fast, this situation is unlikely to happen, but if the server is busy or web site is browsed from the dial-up connection, it is likely to happen.  It is still less probable than with full-post back, but possible.
  2. The second annoying behavior you may experience with input controls inside an UpdatePanel control is lost focus.  Input (keyboard) focus moves to nowhere once AJAX post-back completes.  Web site users who prefer to use keyboard need to use mouse to activate appropriate input box or press TAB multiple times.  (The exact behavior is a little different for Internet Explorer and Mozilla FireFox).

You can place this UpdatePanel to an ASPX page to see the second problem by yourself:

<asp:UpdatePanel runat="server" ID="up">
<ContentTemplate
>
<div
>
<asp:DropDownList ID="ddl1" runat="server" AutoPostBack
="True">
<asp:ListItem>1</asp:ListItem
>
<asp:ListItem>2</asp:ListItem
>
<asp:ListItem>3</asp:ListItem
>
</asp:DropDownList></div
>
<div
>
<asp:DropDownList ID="ddl2" runat="server" AutoPostBack
="True">
<asp:ListItem>1</asp:ListItem
>
<asp:ListItem>2</asp:ListItem
>
<asp:ListItem>3</asp:ListItem
>
</asp:DropDownList></div
>
<div
>
<asp:TextBox runat="server" ID="tb"></asp:TextBox><br
/>
</div
>
</ContentTemplate
>
</
asp:UpdatePanel>

Why Does It Happen?

I need to remind some details of how UpdatePanel works to answer this question.


If ScriptManager EnablePartialRendering property is set to true, controls on the web page can initiate asynchronous post-back.  When it happens web page send asynchronous request to the server.  The web page instance at the server goes through all the phases in its normal lifecycle, but instead of full rendering only partial rendering happens.  The html content for updated panels is sent back to the client in the format that client script manager control can recognize.  The client part of the ScriptManager control parses the response and set innerHTML properties of all DIV elements generated by UpdatePanels.


So, what happens to the UpdatePanel with input controls?  When client script receives a response from the server with new Html content for the UpdatePanel it assigns this content to the DIV element generated by the UpdatePanel.  Input focus at this time is in one of the controls inside the DIV element (UpdatePanel).  The browser destroys the old content of the UpdatePanel including input controls and creates new elements by parsing new content assigned to the innerHTML property.  Input focus moves out of the update panel when focused input controls is being destroyed and it is not restored later when the new controls are created from the assigned html.


Solution of the Problem with the Lost Input Focus

The basic idea behind the solution is to save the ID of the control with input focus before the update panel is updated and set input focus back to that control after the update panel is updated.


I come with the following JavaScript which restores the lost focus in the update panel. 

var lastFocusedControlId = "";

function focusHandler(e) {
document.activeElement = e.originalTarget;
}

function appInit() {
if (typeof(window.addEventListener) !== "undefined") {
window.addEventListener("focus", focusHandler, true);
}
Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(pageLoadingHandler);
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoadedHandler);
}

function pageLoadingHandler(sender, args) {
lastFocusedControlId = typeof(document.activeElement) === "undefined"
? "" : document.activeElement.id;
}

function focusControl(targetControl) {
if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
var focusTarget = targetControl;
if (focusTarget && (typeof(focusTarget.contentEditable) !== "undefined")) {
oldContentEditableSetting = focusTarget.contentEditable;
focusTarget.contentEditable = false;
}
else {
focusTarget = null;
}
targetControl.focus();
if (focusTarget) {
focusTarget.contentEditable = oldContentEditableSetting;
}
}
else {
targetControl.focus();
}
}

function pageLoadedHandler(sender, args) {
if (typeof(lastFocusedControlId) !== "undefined" && lastFocusedControlId != "") {
var newFocused = $get(lastFocusedControlId);
if (newFocused) {
focusControl(newFocused);
}
}
}

Sys.Application.add_init(appInit);

If you save this code to FixFocus.js file, it can be used as:

    <asp:ScriptManager ID="c_scriptManager" runat="server">
<Scripts
>
<asp:ScriptReference Path="~/FixFocus.js"
/>
</Scripts
>
</asp:ScriptManager>

Unfortunately, different browsers handle input focus a little differently.  Mozilla FireFox browser does not provide an easy way to get currently focus element at all.  The script, therefore, handles these differences between browsers. 


You may find the focusControl function a little strange.  I cannot explain the magic it does, but this is really required to set focus to the control in the Internet Explorer.  ASP.NET AJAX extensions use this code itself when setting focus set by ScriptManager.SetFocus method.


I tested this code with Internet Explorer 7 and the most recent build of Mozilla FireFox


Sample Web Site

You can download the sample web site here.


UPDATE

You can often solve the problem by placing each input control into its own update panel.  However, this approach does not solve the problem if user press TAB to go to the next control, which is in its own UpdatePanel. 


The code above is a workaround.  If you need to implement cascading drop-downs it is better to go with Ajax Control Toolkit Cascading Drop-Down Control Extenders.  If you need to use UpdatePanel or several UpdatePanels use this code, but be aware of problem #1 which is not solved by this script.

Tuesday, July 31, 2007

Hidden Controls and ViewState

I don't know if it is obvious how important ViewState is for hidden controls.  I would like to stress where ViewState plays important role for hidden controls (not visible) as it often leads to not obvious problems.

If you like to explore how different ASP.NET controls use ViewState to store their data, Nikhil Kothari's Web Development Helper is a very helpful tool.  I definitely recommend to download and install it, if you like to know what is in your ViewState.  

TextBox when placed on a web form usually does not require ViewState to be enabled to provide its core functionality "being input box".  You can check that __VIEWSTATE hidden field is the same across post-backs if you change the value in your TextBox on the following form:

    <form id="form1" runat="server">
<div>
<asp:TextBox runat="server" ID="TextBox1"></asp:TextBox>
<asp:TextBox runat="server" ID="TextBox2"></asp:TextBox>
<asp:Button runat="server" ID="Submit" Text="Submit" />
</div>
</form>

ASP.NET does not store value of the Text property of TextBox in the ViewState, because its value is submitted by the <input type=text> to the server on each post-back.  (ASP.NET does save Text property into ViewState if TextChanged event has at least one event handler.  This is required to compare currently submitted value with a previous value and raise event only when the value has changed).


However, when TextBox control is not visible or it is placed in any not visible control, TextBox controls does not render <input type=text> to the Html writer and ViewState plays its important role.  Invisible TextBox controls save their Text property as all other properties in its ViewState collection. 


It is easy to see how disabled ViewState impacts behavior of:

<asp:MultiView runat="server" ID="MultiView">
<asp:View runat="server" ID="View1">
<asp:Button runat="server" ID="SwitchToNextButton" Text="Next" OnClick="SwitchToNextButton_Click" />
<asp:TextBox runat="server" ID="TextBox" />
</asp:View>
<asp:View runat="server" ID="View2">
<asp:Button runat="server" ID="SwitchToPreviousButton" Text="Previous" OnClick="SwitchToPreviousButton_Click" />
</asp:View>
</
asp:MultiView>
<
asp:Button runat="server" ID="Button" Text="Postback" />
    protected void SwitchToNextButton_Click(object sender, EventArgs e)
{
MultiView.SetActiveView(View2);
}

protected void SwitchToPreviousButton_Click(object sender, EventArgs e)
{
MultiView.SetActiveView(View1);
}

You can type some text in the TextBox and switch to another View in the MultiView.  Then, if you click "Previous" button, the first View appears and TextBox control shows text you have typed.


If you disable ViewState for the MultiView (and thus for all its children), TextBox keeps showing your text only while it is visible.  Once you hide it by switching to another view, TextBox becomes empty.


Be accurate when disabling ViewState for a whole page or for a large part of the page, if you don't know what controls exactly will be placed there.