Thursday, March 27, 2008

Fast cloning of dropdowns

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/fast_cloning_of_dropdowns.htm]

There are times that you'll want to dynamically add items to an html dropdown using JavaScript. It can be a huge performance gain, even with Ajax and update panels. You can easily do this like so:

 

      function DoStuff()
      {
        var selectbox = document.getElementById("Select1");
        addOption(selectbox, "AAA", "1");
        addOption(selectbox, "BBBBB", "2");
        addOption(selectbox, "CC", "3");
       
      }
   
      function addOption(selectbox, strText, strValue )
      {
        var optn2 = document.createElement("OPTION");
        optn2.text = strText;
        optn2.value = strValue;
        selectbox.options.add(optn2);    //This line is very slow
      }

 

The problem I wanted to solve was how to do this when adding 4000 items? It could take several seconds, and be very slow. (Yes, ideally a simple dropdown by definition doesn't have that many items, but let's say there are legacy constraints there, and the client wants a dropdown). For example, say you have a grid, each row has a dropdown, and that dropdown may be huge. A much faster way is to load only 1 dropdown, make it hidden (via JS on the client), and then clone it's nodes.

 

For example, have a hidden dropdown, which you populate from whatever server values:

 

<span id="Span1">span

And then, call this function to clone those values. It will clone the entire dropdown (sparing you from calling the very slow options.add method 4000 times), and then dump it in some nested container (like a span):

 

      function cloneDropdowns_fast(strSourceId, strContainerId)
      {
        o = document.getElementById(strSourceId).cloneNode(true);
        o.style.visibility = "";
        var container = document.getElementById(strContainerId);
        container.appendChild(o);
      }

 

      function PopulateDropdown()
      {
        cloneDropdowns_fast("Select2", "Span1");
      }

 

So, calling the method "PopulateDropdown" will copy the entire dropdown from "Select2", and dump a whole new dropdown in the "Span1" container. It works much faster (and appears to work in both IE and FF).

Monday, March 17, 2008

Tips for converting from Silverlight 1.1 Alpha to 2.0 Beta

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/tips_for_converting_from_silverlight_11_alpha_to_20_beta.htm]



As I convert some Silverlight 1.1 Alpha apps to the new 2.0 Beta, I'm running across a lot of issues. So far, all have been solvable. Besides the breaking changes documented on MSDN, here are some others. Also, check the Silverlight Bugs forum if something just doesn't appear to be working right.

Silverlight app does not appear to automatically clip an object moving outside the canvas. I.e. if you define a canvas at 100 x 100, and place an image at (200,200), it will still show up. This can be a problem for widgets with moving parts, especially if the location for those parts are based on complex rules (i..e. the physics engine for a game). You can solve this by having your html host page explicitly set the Silverlight apps' boundaries (instead of using percentages):

            <object data="data:application/x-silverlight," type="application/x-silverlight-2-b1" width="300" height="400">

Image source uses strongly typed bitmap instead of a string url source. You can still dynamically load images.

Downloading xml files by default is now asynchronous. This quickstart tutorial explains how to download files. Sure, it is often a good practice to download the file asycnh so that you do not block the main UI thread. But, for small "legacy" apps that you just want to convert quickly, which download small files from the same server as the silverlight app (i.e. no cross domain), where security is not an issue - it's nice to just have a quick synch method to download files. JackBond wrote a good wrapper utility to get around this, re-pasted here for convenience:
//Method from JackBond at: http://silverlight.net/forums/t/11508.aspx
public static string DownloadXmlStringSync(string url)
{
  ScriptObject request;
  request = HtmlPage.Window.CreateInstance("XMLHttpRequest");

  request.Invoke("open", "GET", url, false);
  request.Invoke("send", "");

  return (string)request.GetProperty("responseText");
}
The source for relative paths has changed, especially when referenced from a user control. Before, relative paths were referenced from root directly. Now, they appear to be referenced from ClientBin (I think). One easy work around is to always just convert your relative paths to absolute ones.

UserControls are no longer just Canvases. This is good and bad.
  • The good is that you now have strongly-typed names for all your controls. No more calling this.GetType().Assembly.GetManifestResourceStream, and then doing (root.FindName("TxtMessage") as TextBlock).Text = strText. Rather, you can just say this.TxtMessage.
  • The Bad is that (at least to my understanding) you cannot make a base class that all your UserControls inherit from. Normally (like in ASP.Net or with 1.1 Alpha Canvases), you could make MyBase which derives from Control, and then have all your custom controls inherit MyBase. But now in Silverlight, you cannot do this because the generated partial class from the Xaml hard-codes a reference to UserControl, and that locks you in. Although, I hope I missed something there, or it's changed in the next beta, because using abstract base classes obviously has its benefits.
Practically, I found it easiest to just delete my Canvas files, and then re-create a UC with the same name, and then manually copy the old canvas code back in.

Reminder: Storyboard needs to be started from Page_Load, not the constructor. Thanks to Bill Reiss for pointing this out. While not a breaking change, it's a good reminder. Before, a Silverlight User Control (i.e. based on the Canvas element) had a Page_Load event, so you could start your StoryBoard from that. But now, the UserControls have a constructor. So, you'll need to add the Load event, and then start your story board from that. Else, you'll waste time like I did wondering "why doesn't this storyboard start?"

Clicking a button, and then making that same button invisible in its click event, causes an exception. More documented on the forums. One suggestion is to set the size to 0.

Third Party controls may mess up your references. If you were using 3rd party controls, it may mess up your name spaces and you get a "Cannot find that type" error. For example, I used a 3rd party button control, and then it could no longer find the new Beta 2.0 buttons, so I kept getting a "Cannot find class or namespace Button...." compile error. I needed to manually add this to the csproj file in the <ItemGroup> for the References:
       
    <Reference Include="System.Windows.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

Application_Exit unexpectedly hit by attaching the mouseHandler to a canvas. It's hard for me to describe this one. But, if you're walking through some mouse-handling code where you attach and detach it, or call the GetPosition on a target canvas that's been removed from the Page , it may throw an exception. I know that's ambiguous, but it's something I was seeing.

Friday, March 14, 2008

Dynamically load an image in Silverlight 2.0 Beta

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/dynamically_load_an_image_in_silverlight_20_beta.htm]

You can easily dynamically load images in Silverlight. given a relative url, you can convert that to an absolute url, and then to an ImageSource, and then use that to set an image. Here's a sample method that takes the relative path, the parent canvas that we'll add the image too, and an x-y point:

 

    private void AddImage(string strRelativeImagePath, Canvas myCanvas, Point p)
    {
      //Convert from RelativePath to AbsolutePath to Uri to ImageSource
      string strFullUrl = GetAbsoluteUrl(strRelativeImagePath);
      Uri u = new Uri(strFullUrl, UriKind.Absolute);
      System.Windows.Media.Imaging.BitmapImage b = new System.Windows.Media.Imaging.BitmapImage(u);

      //Create the image here
      Image img = new Image();
      img.Source = b;
      img.SetValue(Canvas.LeftProperty, p.X);
      img.SetValue(Canvas.TopProperty, p.Y);
      //set other properties here...

      //now add to a canvas:
      myCanvas.Children.Add(img);
    }

 

And just call it like so:

 

    AddImage("MyImage.png", this.parentCanvas, new Point(100, 50));

 

Note some changes from the 1.1 Alpha:

  • Image.Source is no longer just a string url, but rather a strongly-typed class. You need to create an ImageSource object first, such as by instantiating a Bitmap image (this allows other image types, like png, not just bmp).

  • The relative-to directory has changed, such that it's easiest to just convert to an absolute url. When you directly add this to the page, it works fine, but if you add it to a userControl from another class library, then it doesn't. Just using absolute urls makes it work (hence the GetAbsoluteUrl method I blogged about yesterday).

Thursday, March 13, 2008

Silverlight 2.0 Convert Relative Url Paths to Absolute

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_20_convert_relative_url_paths_to_absolute.htm]

As you've probably heard, Silverlight 2.0 Beta 1 is out. You can get it from Silverlight.Net. And Bill Reiss is starting another great tutorial series on Silverlight games.

 

I see a lot of breaking changes (as documented on MSDN), and I'll blog more about my adventures upgrading. One change is that it seems like you need Absolute urls in a few more places (like for accessing xml files or images). Because I like to think in terms of relative urls, here's an easy utility to convert relative to absolute. It should handle both file paths (file:///aaa) and web (http://aaa).

 

    public static string GetAbsoluteUrl(string strRelativePath)
    {
      if (string.IsNullOrEmpty(strRelativePath))
        return strRelativePath;

      string strFullUrl;
      if (strRelativePath.StartsWith("http:", StringComparison.OrdinalIgnoreCase)
        || strRelativePath.StartsWith("https:", StringComparison.OrdinalIgnoreCase)
        || strRelativePath.StartsWith("file:", StringComparison.OrdinalIgnoreCase)
        )
      {
        //already absolute
        strFullUrl = strRelativePath;
      }
      else
      {
        //relative, need to convert to absolute
        strFullUrl = System.Windows.Application.Current.Host.Source.AbsoluteUri;
        if (strFullUrl.IndexOf("ClientBin") > 0)
          strFullUrl = strFullUrl.Substring(0, strFullUrl.IndexOf("ClientBin")) + strRelativePath;
      }

      return strFullUrl;
    }

 

More to come soon...

 

Sunday, March 9, 2008

Coding vs. Configuration

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/coding_vs_configuration.htm]

Real-world applications need to be configurable. There are lots of reasons an app must be configured - different environment, modify the thresholds, even change what providers are used for a task. There's a big difference between coding, and configuration:

  • Coding implies writing the actual logic in some language (C#, VB, Java, Html, JavaScript, CSS, Sql, etc...). Coding, by its nature, can deal with complex logic, and may reside in many files across many directories. Sometimes code needs to be compiled into binaries. There's a lot of room for error here, which is why IDEs (like Visual Studio) provide code debugging, and why the community encourages unit tests and code reviews.

  • Configuration implies using knowledge of the domain to tweak the app, after it's been deployed, usually via a small number of config files.

 CodingConfiguration
Requires knowledge of language syntaxYes (and usually for many languages)No (syntax is usually just xml. A huge point of configs is that they abstract out syntax).
Requires business domain knowledgeYesYes
Requires re-compilingSometimes (C#, Java --> Yes, Html/Sql --> No)No. Usually just xml or plain text
Number of filesMany - a 10,000 line app, with 50 files, could still just have 1 app.configFew (usually just 1, or 1 per directory)

 

More and more, the push is to abstract coding into configuration. I believe WCF is big on this - instead of getting bogged down in how to transport data (web services, remoting, FTP, messaging, etc...) you just configure the app as appropriate. We also see this in domain specific languages and code generation. For example, .Net tiers has a big config file, but it makes perfect sense if you understand the concepts of N-Tier architecture. So, you can do a little configuration, and it generates 1000s of lines, which spares you from tons of tedious coding.

Thursday, March 6, 2008

Career path from hobbyist to professional

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/career_path_from_hobbyist_to_professional.htm]

Software Engineering is unique in that it offers a gradual path from a hobbyist to a professional. Many professions do not. For example, there's a world of difference between building a dog house vs. a sky-scraper, or giving first aid vs. performing open-heart surgery. To be an authorized civil engineer or doctor, who offers products and services to the public, there are rigorous standards that must be met. It's not just that 10 years of hobbyist experience of building dog houses won't give you the necessary professional experience to design a sky-scraper, it's that federal law won't even allow you to authorize the blue-prints. For example, if memory serves me right, to authorize civil engineering blue prints, you need to first go to an accredited university, then pass a huge "Engineering in Training" exam, then work under the guidance of a professional engineer for 5 years, then pass a second exam. Then, and only then, are you legally even allowed to approve a blue-print.  There is not a gradual path, but rather one with steep cliffs.

 

Software Engineering is totally different. There is a gradual path. You could start doing hobbyist web sites on your personal laptop. You can then continually learn more doing harder projects (perhaps for non-profits, or just for fun). All the while, you can read the same books, go to the same conferences, and use most of the same tools that the professionals use. You could then enter the lower rung of the professional world by getting a low-paying contractor job on a short assignment, publishing in an online journal, or contributing to a popular open-source project. From there, you could jump to larger teams, or do harder projects, and the rest is history.

 

This potential gradual path from hobbyist to professional has pros and cons. It offers great opportunity, because almost anyone who has the mental ability, passion, and a computer can enter the profession.  However, the con is that many developers (doing paid, real-world, work), still treat it like a care-free hobby, as opposed to professional work.

 

What separates a hobbyist from professional? Scott McChonnell's good book, Professional Software Development, offers a lot of good ideas. I think some important traits of a professional include:

  1. Having public process that works for a team of developers - i.e. a team build server, team source control, etc...

  2. Writing code that works on other people's machines.

  3. Writing code that scales, is secure, and maintainable by other people

  4. Producing deterministic results

  5. Providing schedules and estimates

  6. Using industry best practices and design patterns, not just your own personal bag of tricks

  7. Referencing official standards, not just "I heard somewhere on some blog"

Of course there's more, but this is a start. The good news is that even as a hobbyist transitions to a professional, they can still pick up things like these. It makes me appreciate Software Engineering as a truly wonderful field.