Tuesday, December 28, 2010

The Social Network

I finally saw The Social Network, the not entirely factual but somewhat documentary-based film about Mark Zuckerberg and the rise of Facebook from its launch in 2004 to 500 Million users today. Here's the trailer:



Many more clips from the film can be seen here.

I must say that I was both enthralled and inspired by the movie.  How closely it resembles what actually happened with Facebook over the past 7 years is irrelevant.  What is important and inspirational for all software developers the world over is how the determination of one person created a multi-billion dollar company out of nothing.  Furthermore, if you take a look at what Facebook actually does on a technical basis, there is no "genius" at work, per say.  In saying this, I'm not taking anything away from Zuckerberg or his colleagues, for what they have achieved is phenomenal.  However, if a Microsoft or Yahoo or Google or Apple wanted to replicate the functionality on Facebook, how long would it take one of their teams to do it - a few months at most perhaps?!  What is clear is that there was marketing genius at work.  In this regard, I think Facebook proves the old adage: "A good product and great marketing will always outsell a great product and poor marketing."

When I look at Facebook, I always think, "Will this still be the place to go for social networking 25 years from now or even just 10 years from now?  My intuition tells me not.  There are several reasons why.  One of the primary ones is that the "walled garden" of Facebook is not where the Internet was supposed to be heading.  Instead it's a throwback to the days of AOL and Compuserve.  I fully understand why it's so much easier to connect millions (or even billions) of people together on a common website but surely one day the technology community must be able to move Beyond Facebook ?!?

From the moment the closing credits of the movie started rolling, I started making a whole lot of notes of what would be required to indeed move beyond Facebook.  I am continuing to add to these notes and frankly, think I have some pretty darn good ideas.  There's nothing I can do to implement them at this very moment since I'm up to my ears with a very important commitment to a longstanding client of mine but once that's over and a bit more money is in the bank, I seriously do plan to build a prototype to turn my ideas into tangible examples [sic] of where I believe things need to go next.

I'm writing this post for several reasons, not the least of which is that I'd very much like to find other entrepreneurs out there - not just software developers but technology company entrepreneurs - to network with and see if there's some common ground to move forward with beginning in 2011 Q3 or Q4.  If so, please do drop me a line via the e-mail address shown on this blog and introduce yourself.  I'm interested to meet other interesting, sincere people who are also willing to put in the hard work to build something truly great!

How to Get a Footer to Behave Like You Expect

When developing web applications I must confess that dealing with CSS issues is amongst my least favourite things.  I understand the basics of CSS, of course, but take no pleasure in learning every last detail.  And don't even get me started about the differences in behaviour between browsers!!!


With the web app I've been working on for the past few years I had a partial solution for a footer on pages of varying heights.  It "kind of" worked the way I wanted it to but not entirely.  After trying a few different approaches to get it working, with no success, I just added it to my "To Do" list and left it be.  But recent testing by others on different sized screens revealed it to be much more of a problem than I had originally thought.

So I did a new search and came across a website devoted entirely to this very problem: CSS Sticky Footer

The solution they present is straight forward, easy to implement, and works great ... at least in the IE8 environment that my client will be using.  Definitely check it out if you're experiencing problems with footer placement and/or behaviour.

Thursday, September 9, 2010

Adding Items to the ASP.Net ListView

Have you ever encountered what you thought would be an easy programming task but then realized you were in the middle of a nightmare? This is exactly what happened to me the past few days when I tried to implement the ListView control into my ASP.Net 3.5 project.  Here's a screenshot of my prototype version:


Essentially what I was trying to build was a fancy looking horizontal listbox.  Having no previous experience with the ListView control in ASP.Net, I just assumed that it pretty much worked like the ListBox control.  How incredibly wrong I was!

Binding the ListView to a DataTable was simple enough.  Then, when I determined that I wanted the ListView to be 1 row tall x 4 columns wide, I fairly quickly implemented a Left and a Right DataPager control.  Everything seemed to be on track.

Where things went totally awry though was when I implemented an external "Add New Item" button.  As any reasonable person would assume, I naturally thought that I could instantiate a new DataRow item, append it onto the DataTable, rebind this to the ListView, and then just select the new last item in the control.  Sounds logical, right?  But lo and behold, setting the ListView's "SelectedIndex" property to the index of this new item had NO effect!  This is where the nightmare began.

I tried everything - and I mean everything - to get it to work.  After a countless number of wasted hours I finally got some help from ASP.net regular, Peter, a super bright developer in the Eastern U.S. and this posting.

Simply put, setting the SelectedIndex property does not work under most circumstances.  I do not understand why but it does not.  So in order to show that a given ListView item is selected you have to add special code to the ItemCreated event handler.  Here's an example:

    protected void listView1_ItemCreated(object sender, ListViewItemEventArgs e)
    {
      if (listView1.SelectedIndex > -1)
        return;

      ListViewDataItem item = (ListViewDataItem)e.Item;
      if (item.DataItemIndex == DataTableRowIndex)
      {
        listView1.SelectedIndex = DataTableRowIndex;
        if (listView1.SelectedItemTemplate != null)
        {
          e.Item.Controls.Clear();
          listView1.SelectedItemTemplate.InstantiateIn(e.Item);
        }

        labelListView.Text = DataTableRowIndex.ToString();
      }
    }

Note: In my case, 'DataTableRowIndex' is an integer property that I set in the 'SelectedIndexChanging' event handler.  Doing so then prepares for what occurs in the 'ItemCreated' event handler.  It's like an indirect, arguably manual way of setting 'SelectedIndex'.

Summary

I've added this posting in the hopes that it will save others the grief I experienced because of Microsoft's very poor implementation of this control. On a positive note, the ListView control is very powerful and highly customizable. Here's an example of my finished implementation:


And here you can download a sample test project.

Monday, August 2, 2010

Is VS2008 Hanging After Debugging an ASP.net Project?

I start & stop Visual Studio 2008 hundreds of times per day as part of the normal debugging cycle.  In my case, it's running my ASP.net project in Internet Explorer 8.  For some time now, there's been a delay of upwards of 10 seconds when stopping the project, before I could gain control of VS2008 again.

This finally got so annoying that I did some research and found this.  I tried every single idea presented in that thread but nothing worked.  Finally I got to the last item - May 7, 2010 - which suggested the culprit to the be the solution's "SUO" file.  Rather than delete it outright, I instead moved it to another folder.  Incidentally, in my case the file was over 11MB in size.  I restarted VS2008 and voilĂ  ... the delay disappeared!

I don't know all that the SUO file contains but I've suffered no adverse effects from deleting it.  In fact, Visual Studio has already started rebuilding it!

Tuesday, July 27, 2010

Expanding the Usefulness of Global.asax

I've long utilized the 'Global.asax' file to run assorted startup methods, both from an Application and Session perspective.  One limitation of this file is that you can't introduce "using" statements like you can in any normal C# file.  This forces you to prefix every method and class with its full definition.  This isn't so bad but I've reached a point where I have more complex code I need to run and have grown tired of all those prefixes.  Frankly, I think they also make the code much less readable.

To resolve this issue I found this excellent blog post by Ross Nelson which explains how to modify things.  Essentially Global.asax is altered to contain just this one line:
<%@ Application Language="C#" Inherits="Global" %>
This then lets you create a new 'Global.cs' file in which all of the previous contents of 'Global.asax' are placed.

Wednesday, July 14, 2010

Multiple Interchangeable User Controls on One Web Page

I'm building an ASP.Net web page in which there's a need to display any one of about twenty different user controls at any one time.  The contents of each user control is varied, with each holding a different assortment of labels, textboxes, dropdown listboxes, etc.  Assorted criteria on the page determines which user control is displayed.  The image shown on the left shows a trio of examples representing the [blue] web page and different [orange] user controls.

I don't have a lot of experience with ASP.Net user controls but when I have used them there'd typically be 2 or 3 on every page in the project, representing such things as a toolbar, a pulldown menu, a navigation menu, etc.  In those cases it was easy enough to just refer to the user control directly, performing a simple cast like this:

         ToolBar toolBar = (ToolBar)...

But the situation with this page is quite different.  With it I have a PlaceHolder control called "placeHolderSubForm" into which one user control at a time will be loaded.  Where the difficulty arises is when it's time to cast from a Control object to a specific User Control type.  For example, if the first user control shown were loaded into the web page then the cast might look like this:

UserControlA ucA = (UserControlA)placeHolderSubForm.Controls[0]

That works okay if there are one or two user controls to deal with but with 20 of them I immediately realized that the code would get rather unwieldy, with a whole lot of switch-case statements necessary to determine which User Control type to cast to.  I wanted something much more generic!

At the end of the day the interaction between the web page and each user control is fairly basic, consisting of:
  1. Passing some basic parameters to each user control.
  2. Retrieving a varied assortment of user entered data from each user control.
After much research I opted to construct a base class that each user control would be derived from.  It's still evolving but here's what it looks like so far:
public class UserControlBase : System.Web.UI.UserControl
  {
    // Input Parameters (more to be added when required)
    public virtual int ContractIdx { get; set; }

    // Output Data - passed via SortedList
    public SortedList NewData = new SortedList();

    // Generic event handler to notify parent web page
    public virtual event EventHandler NewData_Handler;
  }

In this way, within each user control I can add newly entered data to the general purpose SortedList container 'NewData' via a key & value pair.  Thus, one user control might retrieve the following data from a user:
  • Width: 5.0
  • Height: 7.0
  • Length: 144
  • Grade: 2.7%
  • Overtime: Yes
  • Crew: Alpha Prime
Then back in the web page the container can be retrieved like this:
SortedList newData = ((UserControlBase)placeHolderSubForm.Controls[0]).NewData;

Please note that I have posted this blog entry not as "the definitive way" to do this kind of thing but simply to throw out to the ASP.Net developer community what approach I've used.  I very much look forward to feedback from others about alternate approaches they prefer and will definitely modify things accordingly if I learn of a better practice.

Monday, May 3, 2010

Tracking a Telerik RadTreeView via the UniqueID Property of Each Node

The large ASP.Net project I've been working on makes extensive use of Telerik's excellent TreeView control.  Here's an example of its implementation:


It's important to note that the Save button you see on the toolbar is used for saving all changes on the page as a batch.  There is no need for a user to press "OK" (or equivalent) after changes on each node.  To accomplish this was a little tricky because the user has the ability to make one or more changes on a given record (ie. the controls on the right side) and then click on another node or go to another module or close the browser altogether.  In all of these cases the aforementioned changes are correctly recorded, associated with the appropriate node.

I solved this by introducing a modular level property called "CurrentNodeID".  It keeps track of the currently selected node.  There's also an associated property called "CurrentNode".  It uses CurrentNodeID as a lookup value to find the currently selected node.

To ensure that each CurrentNodeID was unique, I initially just generated a new GUID and stored it in each node's "id" attribute.  This worked very well.  It was especially easy to find find the current node with a statement like this:

treeView.FindNodeByAttribute("id", CurrentNodeID)

It's also important to realize that I'm not populating the entire tree at once but only populating on demand.  To accomplish this I originally had each node's "ExpandMode" property set to "ServerSide".  Configured this way, finding a node via its "id" attribute property was very straightforward.

But more recently I've been experimenting with a newer ExpandMode property value: ServerSideCallBack  It's very powerful, using AJAX to perform a much quicker population of child nodes.  But testing quickly revealed that once the ExpandMode property was changed this way, the "id" attribute was no longer visible.  An inquiry with Telerik revealed that perhaps I should use an attribute name other than "id", as it's already the name of a node property.

Rather than go down that road I instead went back to basics and asked myself why I was using the attribute approach in the first place, for I had long known that each node's "UniqueID" property had a unique value as well.  So I set about to drop the use of the "id" attribute and instead use this new approach.  When it came to the "PreviousNodeID", rather than storing the randomly generated GUID in it, instead I'd just store the UniqueID value.  This change worked very well except for two things:
  1. You can search a treeview for a node via its attribute value but there's no direct way to search for a node via its UniqueID value.
  2. When you drag & drop a node, its UniqueID value changes.

To resolve issue #1 I had to create a new method.  Here is that method, along with how I've defined the CurrentNodeID and CurrentNode properties:

  /// References the value of the UniqueID property of the most current treeview node.
    public string CurrentNodeID
    {
      get
      {
        if (ViewState["CurrentNodeID"] == null)
          return null;

        return ViewState["CurrentNodeID"].ToString();
      }

      set
      {
        ViewState["CurrentNodeID"] = value;
      }
    }


    /// References the most current treeview node.
    public RadTreeNode CurrentNode
    {
      get
      {
        if (CurrentNodeID == null)
          return null;
      
        return FindNodeByUniqueID(treeViewMain, CurrentNodeID);
      }
    }


    /// Every node has a Unique ID value.  There's no built-in method to locate
    /// a node in a treeview via its Unique ID.  This method accomplishes that.
    public static RadTreeNode FindNodeByUniqueID(RadTreeView treeView, string uniqueID)
    {
      RadTreeNode foundNode = null;

      int nodeCount = treeView.GetAllNodes().Count;
      RadTreeNode[] allNodes = new RadTreeNode[nodeCount];
      treeView.GetAllNodes().CopyTo(allNodes, 0);

      foreach (RadTreeNode node in allNodes)
      {
        if (node.UniqueID == uniqueID)
        {
          foundNode = node;
          break;
        }
      }

      return foundNode;
    }


To resolve issue #2, one must just reset the value of PreviousNodeID after a node is moved:

PreviousNodeID = node.UniqueID

I now have a UI that works faster and there ends up being a bit less code than before.  That's a true Win-Win!