My Blog is Moving to http://weblogs.asp.net/ysolodkyy
My blog is moving to http://weblogs.asp.net/ysolodkyy
RSS feed at www.feedburner.com remains unchanged: http://feeds.feedburner.com/ItCouldBeDone.
My blog is moving to http://weblogs.asp.net/ysolodkyy
RSS feed at www.feedburner.com remains unchanged: http://feeds.feedburner.com/ItCouldBeDone.
Posted by Yuriy Solodkyy at 10:53 PM 0 comments
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".
Posted by Yuriy Solodkyy at 10:49 PM 0 comments
Labels: .Net
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
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:
Now when you know which controls causes the problem you should check:
Posted by Yuriy Solodkyy at 1:04 AM 10 comments
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;
}
}
Posted by Yuriy Solodkyy at 12:56 AM 7 comments
Labels: .Net
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.
Posted by Yuriy Solodkyy at 7:44 PM 7 comments
Labels: .Net
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:
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>
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.
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.
You can download the sample web site here.
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.
Posted by Yuriy Solodkyy at 9:35 PM 27 comments
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.
Posted by Yuriy Solodkyy at 10:40 PM 2 comments
Labels: ASP.NET