Translating UI design to a CSS & Html can be a little complicated. You receive a design from a UI team/expert in your company, which includes general look for the web site,font sizes, images , positioning in the page, distances etc... and you have to create the CSS which shows the design in the page you create. This requires a lot of experience and attention to details. Wrong design can result in unexpected things, like div's disappearing , partial views and wrong positions of items.
For example, this simple html :
<body>
<div style="background-color:red;width:100px;height:100px">
<div style="left:0px;top:0px;position:absolute;background-color:blue;width:50px;height:50px;">
</div>
</div>
</body>
Results this view of the page :
The developer can spend hours of debugging and wondering why the panel doesn't appear in the specified position (until someone would tell him that an item is positioned according to the closest parent container which is explicitly positioned - which means that because the first div is not positioned explicitly, the second div is positioned according to the body element of the document).
This is only a simple example of bugs in CSS design. You can spend hours of trying to solve them, without much success.
Further more, while you write a web site , you need to view the resulting html code for debug of rendering or html injection which is done in runtime. View source is not relevant because it shows only the source of the page as it was loaded - it doesn't shows modifications of the html which can be done in JavaScript (for example , when you use document.write or container.innerHTML).
There are tools that can help you debug these sorts of problems and more.
Firebug (for firefox) and Internet Explorer developer toolbar (for Internet explorer) allows you to do this kind of debugging. They allow you to view the current DOM of your page, you can view the style definitions of each element, you can view the scripts and manipulate them, you can select elements by moving the mouse across the browser and a lot more.
Firebug :
IE Developer Toolbar :
In short, these are tools which are a must in each web developer's toolkit. Use them wisely.
In the last week I've encountered a very weird problem, when I tried to use the new DefaultButton property of an Asp.Net Panel in an UpdatePanel. I used two Panels, each one in a different UpdatePanel, and set the DefaultButton. Something like this :
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Panel ID="Panel1" runat="server" Height="50px" Width="125px" DefaultButton="Button1">
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<asp:Panel ID="Panel2" runat="server" Height="50px" Width="125px" DefaultButton="Button2">
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:Button ID="Button2" runat="server" Text="Button" OnClick="Button2_Click" />
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
But when I tried to use the default button (by entering some text in the text box and pressing enter) in the second panel after I used it in the first panel, weird things started to happen. Sometimes no button has been pressed, and sometimes the button from the first panel was pressed.
After a little digging I found the problem : The code which enables the DefaultButton property in System.Web caused it. I'll explain :
When you add the default button property to a panel , the following code is rendered in the div tag :
<div id="Panel1" onkeypress="BLOCKED SCRIPTreturn WebForm_FireDefaultButton(event, 'Button1')" ...
This is a JavaScript function which is in an Embedded Resource called "WebForms.js" in the System.Web assembly. The function simply checks if the key that was pressed was "enter", and clicks the button if so.
The problem with this function and Ajax was a global variable it used to determine if the button has already been clicked - to prevent an unnecessary post back, I suppose. It looks like this (thank you Reflector...) :
var __defaultFired = false;
function WebForm_FireDefaultButton(event, target) {
if (!__defaultFired && event.keyCode == 13 && !(event.srcElement && (event.srcElement.tagName.toLowerCase() == "textarea"))) {
var defaultButton;
if (__nonMSDOMBrowser) {
defaultButton = document.getElementById(target);
}
else {
defaultButton = document.all[target];
}
if (defaultButton && typeof(defaultButton.click) != "undefined") {
__defaultFired = true;
defaultButton.click();
event.cancelBubble = true;
if (event.stopPropagation) event.stopPropagation();
return false;
}
}
return true;
}
So when I pressed "enter" the second time, in the second panel, __defaultFired was true, so the default button was not clicked. (Why sometimes the first button was pressed ? Because it appears first in the html, it is considered by the browser, as the default button of the page...). This happens because the script expect to be reloaded after the button has been clicked - and when it does, the __defaultFired will be initialized again to false. This does not happen in Ajax - the whole page is not reloading , and so the __defaultFired stays true.
So I tried to fix this problem with the following JavaScript code :
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(resetDefaultButton);
function resetDefaultButton(sender,args) {
__defaultFired = false;
}
I simply reset the __defaultFired to false every time an Ajax request is finished.It worked like a charm.
And so, I went home and tried to recreate the problem (for this post). But it didn't happen! The default button worked great!
I started to analyze what can cause this to work in my personal computer and not at work. I fired up the reflector and to my surprise I found that in my computer the function looks like this :
function WebForm_FireDefaultButton(event, target) {
if (event.keyCode == 13 && !(event.srcElement && (event.srcElement.tagName.toLowerCase() == "textarea"))) {
var defaultButton;
if (__nonMSDOMBrowser) {
defaultButton = document.getElementById(target);
}
else {
defaultButton = document.all[target];
}
if (defaultButton && typeof(defaultButton.click) != "undefined") {
defaultButton.click();
event.cancelBubble = true;
if (event.stopPropagation) event.stopPropagation();
return false;
}
}
return true;
}
As you can see , __defaultButton is missing!
What the &%$#$ is happening here ?
Searched the web , came up with nothing. Until I found this post which describes a security update to the .Net framework. I entered the link for downloading the fix and to my surprise, saw the title : "NET Framework 2.0 SYSTEM.WEB.DLL and MSCOREE.DLL Security Update for Windows 2000, Windows Server 2003 and Windows XP"
Could it be that the assembly was updated in my personal computer by windows update, and updated the script ? It sound correct - because at work we do not receive updates - our development network is an internal network which is not exposed to the web - and therefore not to windows update.
Before I ran to install it, I was lucky to read the fine print... It causes problems in some operating systems - like windows 2000 sp4 - which we use in my company.
So, for now, I don't know what updated the assembly. It could have been an update from a long time ago - which I wasn't aware of. What else have been changed ? What else would work different ? What updates I should download in order to make the version of the .Net framework at work the newest and most updated ? Why the version of the assembly stayed the same even though it has been changed ? I couldn't find where the updates are documented in MSDN or Microsoft.
Any suggestions ?
In Updating an UpdatePanel in the Client side, I described how I solved the problem of updating an UpdatePanel from the client side. The solution was pretty simple : Create an extender which adds an hidden button to the update panel, and performs a click on it when the "update()" method is called from the JavaScript.
Recently I saw a new Webcast in the Asp.Net Ajax How Do I series which explains how to do this with and without the button. In short, the solution requires a call to a private method in the PageRequestManager which performs a post back :
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm._doPostBack(updatePanelClientID,'');
So, I decided to upgrade the extender - Which now calls this method instead of adding a button and clicking on it. It makes the server and client code of the extender cleaner, and more simple.
Also I have fixed the issue of the OnUpdated event - which is now working perfectly : It is triggered when the "update()" method in the client is called. I caused the event to trigger by using properties from the ScriptManager :
ScriptManager currentScriptManager = ScriptManager.GetCurrent(Page);
if ((currentScriptManager.IsInAsyncPostBack) &&
(currentScriptManager.AsyncPostBackSourceElementID == TargetUpdatePanel.ID))
{
OnUpdated(new UpdatedEventArgs());
}
I simply check if the post back is Asynchronies and was caused by the update panel (the id I used when I called the _doPostBack method in the client).
Feel free to download and use my brand new UpdatePanelExtender!
I will be glad to read your comments,suggestions, bugs, etc...
Have a great weekend.