Thursday, 26 June 2008

Text is not wrapping in my fixed width div!!

Now, this is a stupid post. And By stupid I mean that kind of thing that if you hear about it you think "Oh, it was obvious", but if it happends to you you waste a lot of time trying to fix, then you think "Oh, it was obvious. If I blog about it I will sound stupid". Well, this kind of blog posts are the most important, I think, so here it is, sounding stupid:

I had a div, absolutely positioned and of fixed width, with a long text inside that would just not wrap! That means the div would either expand its width to accomodate my text (Internet Explorer) or bleed the text out (FireFox). Was it white-space:nowrap? No! Was it that some of the table cells that contained elements had the "noWrap" attribute? No! What the hell is going on?!

Solution and explanation: wrapping occurs only on NORMAL SPACES, not  . The text inside my div didn't really have any spaces in it, it was one continuous string of words joined by the innocent  .

Gantz - Weird but fun manga

I was looking for new manga to read on the OneManga site and I found Gantz. It has senseless violence, gratuituos sex scenes and great looking chicks in erotic positions. The concept is that a weird black ball is taking people right from before they die and brings them to a room from which they are sent to battle aliens in the streets of Tokyo. Normal people can't see them or the monsters, but they can be killed by them. And they often are.



This is something only a Japanese could have thought of. People are dying, most people around don't care and they are all trying to show how superior they are compared to others. And then they find something to PROTECT and they cry all the time.

Bottom line: Monsters, Aliens, Vampires, Hot chicks, Sword fighting, Gore, Sex, Rape, Emotional torture... they are all in there. The script doesn't make much sense, though, and I think all characters are emotionally stumped to the level of three year olds. That's how Naruto and InuYasha won so many fans, through carefully crafted emotional landscapes, something Gantz lacks almost completely.

Read Gantz at OneManga.
imDb link for the anime

Saturday, 21 June 2008

User Experience Workshop - Sinaia 15th-20th June 2008

It was great! Not only the setting was nice (the four star Smart hotel is exactly what I had expected a hotel should be, except the restaurant, maybe), but the weather was cool, the presentation helpful, the tutor (Aurelian Popa) was above expectations and the people pleasant. Not to mention a week away from boring stuff. ;) I feel it would be pointless to detail what we did there, since it was either my own personal life or the actual workshop (which involves work), so I will give you some impressions of the technology and point you towards the resources that would allow you to go through the same learning process.

The whole thing was about WPF and SilverLight and I can tell you two conclusions right now:
WPF/XAML/SilverLight are a great technology and I expect a lot of .Net applications to migrate towards it in the next 6 to 12 months.
The complexity of this technology is likely to put a lot of people off, therefore the tools like Expression Blend and the Visul Studio interface become completely indispensable and must evolve to have great ease of use and become more intuitive.

The entire presentation model allows one to use any graphical transformation available, including 3D, on any part of the interface. The controls are now without appearance. They come with a default appearance that can be totally replaced with your own. A weird example is to use a 3D cube with video running on each side as a button. Of course, the whole thing is still work in progress and some stuff is yet difficult to do. Besides, you know Microsoft: a lot of complicated things are easy to do, while some of the simplest are next to impossible.

You can taste the Microsoft confidence on this by watching them release an entire design oriented suite (Expression) and working on making Silverlight available on all platforms and browsers. Just the fact that Silverlight can access directly the browser DOM is enough to make me remove all those patchy javascript scripts and replace them with nice Silverlight C# code.

Enough of this. Go learn for yourself!

Tools:
Silverlight is at version 2 beta 2. That is painfully obvious when new bugs are introduced and beta 1 applications break. The Expression Blend tool is at version 2.5 June 2008 CTP and it has also a long walk ahead towards becoming useful. Visual Studio 2008 performs rather well when faced with XAML and WPF stuff, but the Resharper 4.0 addon helps it out a lot. You need the Visual Studio 2008 Silverlight Tools, too. After this compulsory tool kit you could also look at Snoop, Blender and Expression Deep Zoom Composer.

Learning material:
Simplest thing to do is to go to Silverlight Hands-on Labs or download the WPF Hand-on labs and download them all and run through the documentation script that is included with each one. There are video tutorials about how to use the tools, too. Here is one for Blend. Of course, all blogs and materials available online at the search of a Google are helpful, as well.

Community:
As any community, it depends on your desired locality and interests. You can look for local .Net / WPF groups or browse for blogs half way around the globe from you. From my limited googling during the workshop I can see that there are people talking about their issues with WPF and SL, but not nearly enough: the technology still needs to mature. I haven't really searched for it, but I've stumbled upon this site: WindowsClient.NET that seems to centralize WPF, Windows Forms and a bit of Silverlight information.

Inuyasha ends

A while ago I was recommending the anime called Inuyasha and then reading on the manga. Well, after 558 episodes - each having around 19 manga (comics) slides - Inuyasha has reached the end. A bit anticlimactic, considering the things that attracted me to the story in the first place, but an end nevertheless.

You can read the entire story at Onemanga.

Sunday, 15 June 2008

Templated Web User Controls

I wanted to write this great post about how to make Web User Controls that would have templates, just like Repeaters or GridViews or whatever, face any problems, then solve them. Unfortunately, MSDN already has a post like this: How to: Create Templated ASP.NET User Controls. So all I can do is tell you where to use this and what problems you might encounter.

I think the usage is pretty clear and useful: whenever you have as?x code that repeats itself, but has different content, you can use a templated Web User Control. The best example I can think of is a collapsable panel. You have a Panel, with some javascript attached to it, a hidden field to hold the collapse state, some buttons and images and texts to act as a header, but every time the content is different.

Now with the issues one might encounter. In Visual Studio 2005 you get an error, while in VS 2008 you get a warning telling you the inner template, whatever name you gave it, is not supported. This is addressed by the
[ PersistenceMode(PersistenceMode.InnerProperty) ]
decoration of the ITemplate property of the control.
Then there is the issue of the design mode, where you get an ugly error in all Visual Studio versions: Type 'System.Web.UI.UserControl' does not have a public property called '[yourTemplatePropertyName]'. As far as I know there is no way of getting rid of this. It is an issue within Visual Studio. However, the thing compiles and the source as?x code is warning free. I think one could easily sacrifice some design time comfort to reusability.

The politics of Bucharest show arrogance does not win

I am not usually one to talk politics, especially since I don't really think there are essential differences between people participating in this game. Yes, I do see it as a game, with rules that you need to follow to get the prize. But this year's mayor election proved that some of the rules are more subtle than just spending the biggest amount of money in promotional ads and having the biggest party support you. Well, pretending to be "one of the people" seems to always help, though. :)

What happened? Internal struggles within the main opposition party led to them choosing their candidate for the city hall not the man with the most popular votes (as resulting from opinion poles), but the man with the most connections in the party. Therefore the other guy decided to candidate independently. He spent almost nothing on campaign ads, while the leading party candidate spent about 600000 euros just for the first part of the elections and God knows how much for the final part.

Conclusion? Sorin Oprescu, the independent candidate, has won the elections. The leading party candidate lost, with all his ad money, while no one even noticed the candidate from the oposition party. Apparently, a great victory for the people in Bucharest. I have no idea if the guy will be any good as a mayor, and I think that is the major flaw in Romanian elections, but the arrogant belief that party support and lots of money can just land you in a popular position has failed once more in Bucharest today.

But my theory is that Oprescu didn't win just by charisma or by the total lack of charisma of his opponent, Blaga, but from the ugly and cheap attacks against him and other candidates from the main party. With slogans like "Let's get rid of the garbage in sector 5, dump Vanghelie" and images of a bulldog and a snake with glasses (Blaga looks like a big ugly dog, while Oprescu wears glasses) they pushed people away. I guess that the fact that the snake is a symbol of wisdom in many cultures past them by completely.

Anyway, my conclusion is that arrogance is the worst thing a Romanian politician can do right now. They can be stupid, corrupt, pathetic, but NOT arrogant. It is traditional in Romania to dream to become powerful, rich, above all others, and it is even more traditional, since most people never do get rich or famous, to totally despise and hate the people that do or behave like they do. Today was a lesson in humility for the political class.

Saturday, 14 June 2008

Very slow UpdatePanel refresh when containing big ListBoxes or DropDownLists

Update: this fix is now on Github: Github. Get the latest version from there.

The scenario is pretty straightforward: a ListBox or DropDownList or any control that renders as a Select html element with a few thousand entries or more causes an asynchronous UpdatePanel update to become incredibly slow on Internet Explorer and reasonably slow on FireFox, keeping the CPU to 100% during this time. Why is that?

Delving into the UpdatePanel inner workings one can see that the actual update is done through an _updatePanel Javascript function. It contains three major parts: it runs all dispose scripts for the update panel, then it executes _destroyTree(element) and then sets element.innerHTML to whatever content it contains. Amazingly enough, the slow part comes from the _destroyTree function. It recursively takes all html elements in an UpdatePanel div and tries to dispose them, their associated controls and their associated behaviours. I don't know why it takes so long with select elements, all I can tell you is that childNodes contains all the options of a select and thus the script tries to dispose every one of them, but it is mostly an IE DOM issue.

What is the solution? Enter the ScriptManager.RegisterDispose method. It registers dispose Javascript scripts for any control during UpdatePanel refresh or delete. Remember the first part of _updatePanel? So if you add a script that clears all the useless options of the select on dispose, you get instantaneous update!

First attempt: I used select.options.length=0;. I realized that on Internet Explorer it took just as much to clear the options as it took to dispose them in the _destroyTree function. The only way I could make it work instantly is with select.parentNode.removeChild(select). Of course, that means that the actual selection would be lost, so something more complicated was needed if I wanted to preserve the selection in the ListBox.

Second attempt: I would dynamically create another select, with the same id and name as the target select element, but then I would populate it only with the selected options from the target, then use replaceChild to make the switch. This worked fine, but I wanted something a little better, because I would have the same issue trying to dynamically create a select with a few thousand items.

Third attempt: I would dynamically create a hidden input with the same id and name as the target select, then I would set its value to the comma separated list of the values of the selected options in the target select element. That should have solved all problems, but somehow it didn't. When selecting 10000 items and updating the UpdatePanel, it took about 5 seconds to replace the select with the hidden field, but then it took minutes again to recreate the updatePanel!

Here is the piece of code that fixes most of the issues so far:
/// <summary>
/// Use it in Page_Load.
/// lbTest is a ListBox with 10000 items
/// updMain is the UpdatePanel in which it resides
/// </summary>
private void RegisterScript()
{
string script =
string.Format(@"
var select=document.getElementById('{0}');
if (select) {{
// first attempt
//select.parentNode.removeChild(select);


// second attempt
// var stub=document.createElement('select');
// stub.id=select.id;
// for (var i=0; i<select.options.length; i++)
// if (select.options[i].selected) {{
// var op=new Option(select.options[i].text,select.options[i].value);
// op.selected=true;
// stub.options[stub.options.length]=op;
// }}
// select.parentNode.replaceChild(stub,select);


// third attempt
var stub=document.createElement('input');
stub.type='hidden';
stub.id=select.id;
stub.name=select.name;
stub._behaviors=select._behaviors;
var val=new Array();
for (var i=0; i<select.options.length; i++)
if (select.options[i].selected) {{
val[val.length]=select.options[i].value;
}}
stub.value=val.join(',');
select.parentNode.replaceChild(stub,select);

}};"
,
lbTest.ClientID);
ScriptManager sm = ScriptManager.GetCurrent(this);
if (sm != null) sm.RegisterDispose(lbTest, script);
}


What made the whole thing be still slow was the initialization of the page after the UpdatePanel updated. It goes all the way to the WebForms.js file embedded in the System.Web.dll (NOT System.Web.Extensions.dll), so part of the .NET framework. What it does it take all the elements of the html form (for selects it takes all selected options) and adds them to the list of postbacked controls within the WebForm_InitCallback javascript function.

The code looks like this:
if (tagName == "select") {
var selectCount = element.options.length;
for (var j = 0; j < selectCount; j++) {
var selectChild = element.options[j];
if (selectChild.selected == true) {
WebForm_InitCallbackAddField(element.name, element.value);
}
}
}
function WebForm_InitCallbackAddField(name, value) {
var nameValue = new Object();
nameValue.name = name;
nameValue.value = value;
__theFormPostCollection[__theFormPostCollection.length] = nameValue;
__theFormPostData += name + "=" + WebForm_EncodeCallback(value) + "&";
}
That is funny enough, because __theFormPostCollection is only used to simulate a postback by adding a hidden input for each of the collection's items to a xmlRequestFrame (just like my code above) in the function WebForm_DoCallback which in turn is called only in the GetCallbackEventReference(string target, string argument, string clientCallback, string context, string clientErrorCallback, bool useAsync) method of the ClientScriptManager which in turn is only used in rarely used scenarios with the own mechanism of javascript callbacks of GridViews, DetailViews and TreeViews. And that is it!! The incredible delay in this javascript code comes from a useless piece of code! The whole WebForm_InitCallback function is useless most of the time! So I added this little bit of code to the RegisterScript method and it all went marvelously fast: 10 seconds for 10000 selected items.
string script = @"WebForm_InitCallback=function() {};";
ScriptManager.RegisterStartupScript(this, GetType(), "removeWebForm_InitCallback", script, true);


And that is it! Problem solved.