Tuesday, June 30, 2009

Silverlight: Reflection Image Button

In continuing with building on Silverlight, I was working on a control strip that contains images. We want to style them fancy, and the "reflection" pattern seems to be quite popular. Applying a reflection to an image is fairly straightforward. My preference is to flip the image and apply an opacity mask:

     <Image x:Name="Reflection" RenderTransformOrigin="0.0,0.0">
            <Image.RenderTransform>
                <ScaleTransform x:Name="ReflectionTransform" ScaleY="-1"/>
            </Image.RenderTransform>         
            <Image.OpacityMask>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="#00000000" Offset="0.0"/>
                    <GradientStop Color="#55555555" Offset="1.0"/>
                </LinearGradientBrush>
            </Image.OpacityMask>
        </Image>

Some people prefer a stronger gradient but you get the idea - turn it over and fade it out. If you wanted to get really fancy, you could do a SkewTranform and cast the reflection at an angle.

The next thing I wanted to do was emphasize the solid image a bit when hovered to indicate it is "active." I tried a typical "bouncy" algorithm that moved the image up and down, but personally I prefer scaling the image instead. There are plenty of examples of "zoom toolbars" out there. For our purposes, we'll just barely expand the image to show it is selected, and do it using an animation so it's smooth and doesn't just suddenly pop up. The animations for expanding and contracting are simple:

<Image.Resources>
    <Storyboard x:Name="ImageExpand">
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="MainTransform" Storyboard.TargetProperty="ScaleX"
                         From="1.0" To="1.1"/>
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="MainTransform" Storyboard.TargetProperty="ScaleY"
                         From="1.0" To="1.1"/>
    </Storyboard>
    <Storyboard x:Name="ImageContract">
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="MainTransform" Storyboard.TargetProperty="ScaleX"
                         From="1.1" To="1.0"/>
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="MainTransform" Storyboard.TargetProperty="ScaleY"
                         From="1.1" To="1.0"/>
    </Storyboard>
</Image.Resources>

When initializing the control, we want to hook into the events for these animations completing so we can stop them for reuse:

public ReflectedZoomImage()
{
    InitializeComponent();           
    ImageExpand.Completed += _AnimationCompleted;
    ImageContract.Completed += _AnimationCompleted;
}

The _AnimationCompleted simply determines who triggered the call and then stops the animation and sets the final scale value - it could have been separate methods but we may eventually want to handle it generically by checking if sender is a storyboard, etc.

private void _AnimationCompleted(object sender, EventArgs e)
{
    if (sender == ImageExpand)
    {
        ImageExpand.Stop();
        MainTransform.ScaleX = 1.1;
        MainTransform.ScaleY = 1.1;
    }
    else if (sender == ImageContract)
    {
        ImageContract.Stop();
        MainTransform.ScaleX = 1.0;
        MainTransform.ScaleY = 1.0;
    }
}

Next, we wire in the events for mouse enter and mouse leave. We want to make sure an existing animation is not already firing before we start it over, so the code for expanding the image looks like this:

private void MainImage_MouseEnter(object sender, MouseEventArgs e)
{
    if (ImageExpand.GetCurrentState().Equals(ClockState.Stopped))
    {
        ImageExpand.Begin();
    }
}

Finally, I wanted to encapsulate this all into a control that I could reuse. The completed XAML for the control looks like this:

<UserControl x:Class="SilverTest.Controls.ReflectedZoomImage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    >
    <Grid x:Name="ReflectionGrid">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Image MouseEnter="MainImage_MouseEnter" MouseLeave="MainImage_MouseLeave" MouseLeftButtonDown="MainImage_MouseLeftButtonDown" x:Name="MainImage" Grid.Row="0">
            <Image.RenderTransform>
                <ScaleTransform x:Name="MainTransform"/>
            </Image.RenderTransform>
<Image.Resources>
    <Storyboard x:Name="ImageExpand">
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="MainTransform" Storyboard.TargetProperty="ScaleX"
                         From="1.0" To="1.1"/>
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="MainTransform" Storyboard.TargetProperty="ScaleY"
                         From="1.0" To="1.1"/>
    </Storyboard>
    <Storyboard x:Name="ImageContract">
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="MainTransform" Storyboard.TargetProperty="ScaleX"
                         From="1.1" To="1.0"/>
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="MainTransform" Storyboard.TargetProperty="ScaleY"
                         From="1.1" To="1.0"/>
    </Storyboard>
</Image.Resources>
        </Image>
        <Image x:Name="Reflection" RenderTransformOrigin="0.0,0.0" Grid.Row="1">
            <Image.RenderTransform>
                <ScaleTransform x:Name="ReflectionTransform" ScaleY="-1"/>
            </Image.RenderTransform>         
            <Image.OpacityMask>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="#00000000" Offset="0.0"/>
                    <GradientStop Color="#55555555" Offset="1.0"/>
                </LinearGradientBrush>
            </Image.OpacityMask>
        </Image>
    </Grid>
</UserControl>

To actually use the control, we need to expose a property so the consumer can set the image. I made a property called "ReflectionSource" and once that's set, I can wire it into the main image and the reflection and also set the heights:

public ImageSource ReflectionSource
{
    get
    {
        return MainImage.Source;
    }

    set
    {
        MainImage.Source = value;
        Reflection.Source = value;
        _ControlInit();
    }
}

private void _ControlInit()
{
    MainImage.Width = Width;
    MainImage.Height = Height / 2;

    MainTransform.CenterX = MainImage.Width/2;
    MainTransform.CenterY = MainImage.Height/2; 

    Reflection.Width = Width;
    Reflection.Height = Height / 2;
    
    ReflectionTransform.CenterX = Reflection.Width / 2;
    ReflectionTransform.CenterY = Reflection.Height / 2; 
}

If you noticed in the XAML, I wired into the "MouseLeftButton" event so we can register a click. I expose this as an actual Click event:

...
public event EventHandler<MouseButtonEventArgs> Click; 
...

... and then pass that through:

private void MainImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (Click != null)
    {
        Click(this, e); 
    }
}

Now I can reuse the control ... in this case, I've styled an "up" and "down" image, and put them next to each other with a spacer, like this:

<Border Background="White" CornerRadius="30" Margin="2" Grid.Column="1" Grid.Row="0">
    <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="1">
        <silvertest:ReflectedZoomImage Width="21" Height="42" Click="ReflectedZoomImage_Click" ReflectionSource="../Resources/green.png"/>
        <Rectangle Opacity="0" Width="5"/>
        <silvertest:ReflectedZoomImage Width="21" Height="42" Click="ReflectedZoomImage_Click" ReflectionSource="../Resources/red.png"/>
    </StackPanel>
</Border>

Note wiring into the click event and that I can set the width and height of the actual, rendered control, not the individual image (the image is 21, so the rendered control should be 42 to account for the reflection).

The finished product:

Reflected Zoom Image

...and with the red button emphasized:

Reflected Zoom Image 2

And there you have a simple Silverlight user control that takes advantage of the animation and transformation functionality that is built-in to the framework.

Jeremy Likness

Tuesday, June 23, 2009

IE 6.0 Drop downs - a Quick and Dirty Fix

If anyone has an easy, elegant solution for the IE 6 dropdown bug, let me know. I know there are custom controls, etc, but I've been searching for something generic that can be applied to hundreds of pages without rework.

What I came up with is not pretty, but gets the job done. Basically, IE 6 drop downs are pretty much "stuck in their ways" and only size to fit the width. That means if you have items that are wider than the given width, you're out of luck for reading or seeing what they are (other browsers are kind enough to expand the drop down when you click on it to accommodate the width of the largest item).

What I did was put a global script that checks if we're in bad territory (IE 6) and then goes through all of the drop downs and appends an anchor tag with a question mark, so you get something that looks like this:

?

The code:

function IE6_DropdownFix() {
    var badBrowser = (/MSIE ((5\.5)|6)/.test(navigator.userAgent) && navigator.platform == "Win32");
    if (badBrowser) {
        $("select").each(function(){
     $(this).after("?");
        });        
    }
} 

Then, when you click the tag, you get the contents of the dropdown. This is done by finding out who it was inserted after, then iterating the sub-options and putting them into an alert box:

function IE6_ShowContents(elem) {
 var dropdown = $(elem).prev();
 var text = ''; 
 $(dropdown).find("option").each(function(){
  text = text + $(this).text() + "\r\n";
 });
 alert(text);
}

Again, not the prettiest of fixes but at least it gets the job done ... interested in any other solutions you might have!

Jeremy Likness

Friday, June 19, 2009

Code Browser Take 2: Silverlight

After publishing an AJAX-based source code browser last week, I decided to do an iteration with Silverlight support. Our company is exploring this technology, so it was the perfect "proof of concept" project to get familiar with Silverlight before wiring it in at the office.

Download the Source Code

This post will walk you through the steps I took to go from a project that had no Silverlight whatsoever to the existing project available here.

My first step was to do some basic re-factoring of the layout. If you recall, the first version used a base page to wire in the JavaScript and style sheets. Because I now have two pages, I decided to pull that functionality into a MasterPage so that I could have a common header/footer/etc. This was fairly straightforward:

  1. Create a master page with the common elements, including the script manager and outer form
  2. Move the logic from the base page to the master page code behind
  3. Rename Default.aspx to Default_.aspx and then add a new Default.aspx with the MasterPage reference
  4. Move over the pertinent items, and test

My first round of testing found that my styling broke because the CSS was looking for a #tree to override the anchor tag behavior. Because this was a span running on the server so the controller could render into it, I moved the span element up and used a placeholder to render the control instead. Note the span has a "local" id whereas the placeholder has a server id that basically takes on the id of the master page in front of it. It looks like this in the .aspx:

<span id="_tree">
   <asp:PlaceHolder ID="_tree" runat="server"/>
</span>

But gets rendered like this:

<span id="_tree">
 <div id="ctl00__contentPlaceHolder1_ctl00__pnlTree">
 ... etc etc

The next step was to prepare the models for consumption by a service. Silverlight runs in the browser and therefore cannot directly access any of the existing assemblies (it actually has its own, stripped-down version of the CLR). This becomes true "client-server". While we used callbacks for the AJAX version, this would use a true service. Because my main web project was using interfaces, I needed something concrete to serialize across the wire. I created a folder called Transport and began building my objects.

The first change I had to make was with the parent directory. The original models had an IDirectory reference, which would obviously need to be concrete for a service. Because I really don't need the parent directory, I decided to split it from the original interface and create a new IParentDirectory interface instead. Both the FileModel and the DirectoryModel then simply implement IFile, IParentDirectory and IDirectory, IParentDirectory respectively.

FileTransport was first. I could implement IFile for this and keep it fairly much "as is." To ease converting from my interface type to a concrete instance, I allowed the constructor to take in an IFile and set its properties. I also added a flag on the constructor to determine whether or not to load the content. On building the tree, I will not send content across the wire, only when requesting the actual source. The completed class looks like this:

using System;
using Interface.Model;

namespace CodeBrowser.Transport
{
    /// <summary>
    ///     File transport
    /// </summary>
    [Serializable]
    public class FileTransport : IFile 
    {
        /// <summary>
        ///     Required constructor for serialization
        /// </summary>
        public FileTransport()
        {
            
        }

        /// <summary>
        ///     Constructor - no content
        /// </summary>
        /// <param name="file">A file to transport</param>
        public FileTransport(IFile file) : this(file, false)
        {
            
        }

        /// <summary>
        ///     Constructor with content
        /// </summary>
        /// <param name="file">A file to transport</param>
        /// <param name="loadContent">True if content should be loaded as well</param>
        public FileTransport(IFile file, bool loadContent) 
        {
            Name = file.Name;
            Path = file.Path;
            Extension = file.Extension;
            Size = file.Size;
            Content = loadContent ? file.Content : new byte[0];
        }

        /// <summary>
        ///     Name of the node
        /// </summary>
        public string Name { get; set; }
        
        /// <summary>
        ///     Path to the node
        /// </summary>
        public string Path { get; set;}
        
        /// <summary>
        ///     Extension for the file
        /// </summary>
        public string Extension { get; set; }
        
        /// <summary>
        ///     Content of the file
        /// </summary>
        public byte[] Content { get; set; }
        
        /// <summary>
        ///     Size of the file
        /// </summary>
        public int Size { get; set; }                       
    }
}

The DirectoryTransport was a little more interesting. I needed to send concrete directories and concrete files, so using the IList defined in the interface was not possible. I settled for implementing IFileSystemNode (just a name and a path) and then created a distinct List<FileTransport> and List<DirectoryTransport> instead. The constructor also would take an IDirectory and then recurse to build out the transport structure. The finished class:

using System;
using System.Collections.Generic;
using Interface.Model;

namespace CodeBrowser.Transport
{
    /// <summary>
    ///     Directory transport
    /// </summary>
    [Serializable]
    public class DirectoryTransport : IFileSystemNode
    {
        /// <summary>
        ///     Required constructor for serializable
        /// </summary>
        public DirectoryTransport()
        {
            
        }

        /// <summary>
        ///     Constructor from interface
        /// </summary>
        /// <param name="directory"></param>
        public DirectoryTransport(IDirectory directory)
        {
            Name = directory.Name;
            Path = directory.Path;
            FileContents = new List<FileTransport>();
            DirectoryContents = new List<DirectoryTransport>();
            
            foreach(IFileSystemNode node in directory.Contents)
            {
                if (node is IFile)
                {
                    FileContents.Add(new FileTransport(node as IFile));
                }
                else if (node is IDirectory)
                {
                    DirectoryContents.Add(new DirectoryTransport(node as IDirectory));
                }
            }
        }
        /// <summary>
        ///     Name of the node
        /// </summary>
        public string Name { get; set; }
        
        /// <summary>
        ///     Path to the node
        /// </summary>
        public string Path { get; set; }
                
        /// <summary>
        ///     Files 
        /// </summary>
        public List<FileTransport> FileContents { get; set; }

        /// <summary>
        ///     directories
        /// </summary>
        public List<DirectoryTransport> DirectoryContents { get; set; }
                    
    }
}

The transport objects made wiring in a service very easy. The first service that provides the full tree node (sans code) was as simple as:

[WebMethod]
public DirectoryTransport GetMasterNode()
{
    return new DirectoryTransport(TreeCache.GetMasterNode()); 
}

The same cache is tapped into, the IDirectory fed into the DirectoryTransport and then serialized across the wire (you can see what it looks like locally on your machine by browsing to the service and then invoking it).

The file detail method was also straightforward. Given a path, it iterates the tree structure until it finds the right file (this is not intelligent yet - no hashing or understanding partial paths, etc — that will be a refactor) and then processes the code to send as a string.

Because of converting from bytes to string, I ripped out the translation piece from Leaf and put it into Utility with a switch to encapsulate in a <pre> tag for syntax highlighting (alas, the Silverlight version doesn't highlight or allow selection ... yet).

You can peruse the code to see this. These were the modifications to the main code base. You can see how having a multi-tiered application made it very easy and straightforward to stand up the pieces we needed for a service! No major refactoring or restructuring of objects, just a few tweaks and we were ready to go. Now for the fun part ... the Silverlight piece.

Of course, all of the prerequisites for Silverlight need to be pulled down, installed, and available. I used the latest non-beta version (2.0 as of this writing) and grabbed the toolkit as well. The page I made was very simple and straightforward. My steps to make the Silverlight project:

  1. Added a new project to my solution as a Silverlight application.
  2. When prompted, I chose to embed the test page into an existing web application (CodeBrowser)
  3. In my CodeBrowser application, I created a new page called Silver.aspx and simply added the Silverlight control and reference to the application, then deleted the test pages. My MasterPage has a link to the AJAX and Silverlight versions.
  4. Finally, I added a reference to the DataAccess.asmx service to pull in the tree and file contents

The Xaml was simple - a stacked panel with a tree view and a scroll viewer for the code, with some styling and colors and fonts sprinkled in via Expression Blend. It looks like this:

<UserControl xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"  x:Class="SourceSilver.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="1024" Height="768" BorderThickness="2" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
    <StackPanel Orientation="Horizontal" Width="1000" Height="760" Background="White">
          <controls:TreeView  x:Name="trvTree" Width="250" BorderThickness="0,0,0,0" FontFamily="Arial" FontSize="10" FontWeight="Bold" TabNavigation="Cycle" Cursor="Hand"/>
        <TextBlock Width="10"/>
        <StackPanel Orientation="Vertical">
                <TextBlock Height="10"/>
                <ScrollViewer Width="700" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Height="700" x:Name="svCode" FontFamily="./Fonts/Fonts.zip#Consolas" Cursor="IBeam" Margin="1,1,1,1" d:IsStaticText="True" 
FontSize="10">                                        
                 <ScrollViewer.Background>
                  <LinearGradientBrush EndPoint="0.5,1"
 StartPoint="0.5,0" SpreadMethod="Repeat">
                   <GradientStop Color="#FFF5EEEE"/>
                   <GradientStop Color="#FFCF9494" Offset="1"/>
                  </LinearGradientBrush>
                 </ScrollViewer.Background>                                        
                </ScrollViewer>
            </StackPanel>
    </StackPanel>    
</UserControl>

The code behind was easier than I expected. First, I instantiated my web service agent:

...
readonly DataAccessSoapClient _service = new DataAccessSoapClient();
...

Next, wire in some events for listening to the service and start the service to load the main nodes (done in the constructor):

public Page()
{
    InitializeComponent();
    
    _service.GetMasterNodeCompleted += _ServiceGetMasterNodeCompleted;
    _service.GetFileDetailsCompleted += _ServiceGetFileDetailsCompleted;
    _service.GetMasterNodeAsync();
}

Finally, in the events, I wired in two activities. Grabbing the node starts a process to build nested TreeViewItem that I add to the control. I wire in a click event on the directories so they toggle:

static void _DirectorySelected(object sender, System.Windows.RoutedEventArgs e)
{
    TreeViewItem item = sender as TreeViewItem;
    if (item != null)
    {
        item.IsExpanded = !item.IsExpanded;
    }
}

I recurse the nodes, making more items as needed. Note for the files that I bind a separate click event and make the FileTransport the DataContext for that node:

foreach (FileTransport file in node.FileContents)
{
    TreeViewItem fileItem = new TreeViewItem {DataContext = file, Header = file.Name};
    fileItem.Selected += _FileItemSelected;
    root.Items.Add(fileItem);
}

Down the road I may refactor to use templates but this worked well for now. When the file is selected, we take the data context and cast it back to the file, then call the service to give us the actual code:

FileTransport file = item.DataContext as FileTransport;
if (file != null)
{
    svCode.Content = new TextBlock {Text = "Loading..."}; 
    _service.GetFileDetailsAsync(file.Path);
}

Easiest of all, when the call returns, we simply inject the code into the ScrollView control:

void _ServiceGetFileDetailsCompleted(object sender, GetFileDetailsCompletedEventArgs e)
{
    svCode.Content = e.Result;
}

That's it! I published it and was able to run it. Obviously, there is still a lot of work to do. The AJAX side needs to stop loading the entire tree and instead lazy load as you expand nodes. The Silverlight side needs some highlighting and clean-up of the UI. Hopefully, however, if you are new to Silverlight, this was a useful walk through for taking existing code and wiring it in, and using some of the new controls in Silverlight.

Until next time ...

Jeremy Likness

Thursday, June 11, 2009

ASP.NET AJAX-based Source Code Browser

This project started out when I began searching for an quick way to put source code on my website to make it easy for guests to browse projects. Time is very valuable so I don't always like downloading a zipped archive and then exploding it to see it - I'd rather have a space online.

Download Source Code

Browse Source Code (using this project itself)

It was then I realized this was a perfect candidate for a mini-project. It will demonstrate the flexibility of having a solid architecture, provides plenty of opportunity for refactoring, and demonstrates some basic programming paradigms while solving the problem I had of showing source code!

My initial requirement was that I would be able to throw "Iteration 1" together in a few hours. I spend enough time coding as it is and don't need a runaway project taking any time or focus away from my main goals. So, the requirements were quite simple:

  • Show a tree view to browse down to various nodes, directories or source files
  • Show the source with some sort of highlighting
  • Don't show everything, just the pertinent stuff

Not bad, so where to start?

First, I wanted to create a flexible model. My thought for iteration 1 was to parse the file system and show nodes, but thinking ahead, I may eventually want to link to third-party repositories like SVN. Therefore, I decided to interface my models.

Code Browser Model Interfaces

I have the most generic part of the structure, the IFileSystenNode, which basically contains a name and a path. This can be further resolved to a IDirectory, which contains a collection of other IFileSystemNode, or an IFile that is clarified by an extension (maybe I should change that to type) and possibly has actual content to render.

Next step was implementation of these models. I created a base abstract class for the node and then created a DirectoryModel and a FileModel.

Code Browser File and Directory Models

The point here is that I could have an implementation that holds a Uri and exposes path as a URL, etc. In keeping with agile concepts, I'm not going to think too far ahead and go with what I have for now.

Next is the data access layer. First step was a generic IDataAccess that simply allows a "Load" of an entity:

namespace Interface.DataAccess
{
    /// <summary>
    ///     Data access method
    /// </summary>
    /// <typeparam name="T">The type of data access</typeparam>
    public interface IDataAccess<T> where T: IFileSystemNode 
    {
        /// <summary>
        ///     Loads the selected node
        /// </summary>
        /// <param name="path">The path to the node</param>
        /// <returns>The node</returns>
        T Load(string path);
    }
}

Because I don't need to get the contents of a file until requested, I created a IFileDataAccess that specifically takes a reference to a file object and then loads the contents.

The implementation is relatively straightforward. The DirectoryDataAccess recursively iterates through the structure, and either builds a collection for each directory or calls out to the file data access to load the file contents:

public DirectoryModel Load(string path)
{
    if (string.IsNullOrEmpty(path))
    {
        throw new ArgumentNullException("path");
    }

    DirectoryModel retVal = ModelFactory.GetInstance();

    if (Directory.Exists(path))
    {
        retVal.Path = path; 
        DirectoryInfo dir = new DirectoryInfo(path);
        retVal.Name = dir.Name;
        
        // recursively add subdirectories
        foreach(DirectoryInfo subDir in dir.GetDirectories())
        {
            DirectoryModel subDirectory = Load(subDir.FullName);
            subDirectory.ParentDirectory = retVal; 
            retVal.Contents.Add(subDirectory);
        }

        foreach(FileInfo file in dir.GetFiles())
        {
            FileModel fileModel = _fileAccess.Load(file.FullName);
            fileModel.ParentDirectory = retVal; 
            retVal.Contents.Add(fileModel);
        }
    }

    return retVal; 
}

Here is my first note to self ... instead of recursing, I could easily just load the names for the top level directory items and set an "isLoaded" flag. Then, only if the user drills down to a lower level do I need to start recursing there. That will be a refactoring we do later ... right now I suck the whole thing into memory at once, but that obviously won't scale to a large set of projects or make sense going over the network to a source repository. We'll get there!

The FileDataAccess pulls out the filename and extension information. It implements the "LoadContents" by pulling in all of the bytes for the file:

...
if (File.Exists(file.Path))
{
   file.Content = File.ReadAllBytes(file.Path);
}
...

I like to have a service layer on top of the data access. This layer can coordinate multiple data access (i.e. in the future when I add an SVN implementation, the service layer can instantiate a file system and an SVN data access handler and then aggregate the results to pass up to the presentation layer). In this project, the service layer mostly acts as a pass-through to the data access. However, it also contains a list of valid extensions that we will show the end user, and therefore manages the call to the "LoadContents" based on whether or not the extension is eligible. This prevents us from trying to load DLLs, for example.

Now that we have that all wrapped up, let's go to the controls. I could have tapped into the extensive control library that AJAX provides, but I thought it would be fun to go ahead and roll my own controls. I did decide to use JQuery because it makes manipulating the client so easy.

First, the control architecture. I want to have a "tree" that has branches (directories or files) and then a "leaf" the is the display of the code. This was fairly easy to visualize and map out.

I decided to make the branches a server control based on a Panel. The branch can then control the visibility of the child branches. The branch contains an IFileSystemNode and generates an anchor tag for that node. Then, based on if it is IDirectory or IFile, it generates a click event to expand children or load the code contents. I decided to let the branches recursively load themselves. For this phase, the entire tree structure is output and then the second-level branches are collapsed by default. Again, this is the first iteration: the next refactoring for the UI will be to lazy load the child nodes. This won't scale to a ton of projects "as is."

You'll find some interesting code in the JavaScript for the branches. For example, I found that initially hiding the DIVs somehow didn't synchronize with the internal DOM state and forced users to click twice in order to expand a node. To solve this, I created an artificial attribute that is attached to the nodes and call a redundant "hide" to get the DOM in synch:

// for some reason the visibility isn't sorted out, so
// the first time we'll hide it again to get the flags set
// and then the toggle works fine
if (!$(child).attr('init')) {
   $(child).hide();
   $(child).attr('init','true'); 
}

The rest is just a simple toggle of the child visibility and its siblings. The entire architecture ends up looking like this:

Code Browser Control Architecture

The branch is a server control, the tree is a container for the related branches. The branches expose an event that other clientside controls can listen to when the user clicks on a node to view. This is where the "leaf" comes in. It is basically a title and a DIV, and it registers to listen for the click event. When that happens, it makes a callback to grab the code contents and then renders them in the DIV.

For syntax highlighting, I used Alex Gorbatchev's Syntax Highlighter. It does a great job and is what I use in my C#er : IMage blog. Once the div is rendered, with a little help in the Leaf to figure out the right brush based on the file extension (this is presentation logic, not service), the Syntax object is called and it does its job with highlighting the code.

I used the Callback function in the Leaf "as intended" which is to generate the code with a call to GetCallbackEventReference which generates the appropriate JavaScript. In order to prevent users from aggressively clicking nodes while they are still loading, we set a global flag and flash an error until the current leaf is fully loaded. It ends up looking like this:

function leafCallback(path,name) {
        if (window.loading) {
            alert('An existing request is currently loading. Please wait until the code is loaded before clicking again.');
            return;
        }
        window.loading = true;
        $("#_codeName").html(name); 
        $("#_divCode").html("Loading..."); 
        var arg = path + '|' + name; 
        <%=Page.ClientScript.GetCallbackEventReference(this,"arg","leafCallbackComplete",null) %>;
    }

We pass down the path and the name so we can find the correct node on the server and then the server sends back the bytes to render. The callback function simply injects the source and then calls the syntax highlighter:

function leafCallbackComplete(args, ctx) {
    $("#_divCode").html(args);
    window.loading = false;
    window.scrollTo(0, 0);
    setTimeout('SyntaxHighlighter.highlight()', 1);    
}

I decided to build a static cache and retain one copy of the entire structure in memory. This is refreshed every hour so it will pull in new source as I add new projects. The Cache simply uses the ASP.NET Cache object, and either pulls the tree from cache, or invokes the service to load the tree recursively. Obviously, this strategy will need to change when I refactor to support lazy loading:

namespace CodeBrowser.Cache
{
    /// <summary>
    ///     Caches a global copy of the tree and refreshes it every hour
    /// </summary>
    public static class TreeCache
    {
        /// <summary>
        ///     Key for storing in the cache
        /// </summary>
        private const string CACHE_KEY = "Master.Node.Key";

        /// <summary>
        ///     The expiration (1 hour)
        /// </summary>
        private static readonly TimeSpan _expiration = new TimeSpan(0,1,0,0);

        /// <summary>
        ///     Gets the master tree node
        /// </summary>
        /// <returns>The root <seealso cref="IDirectory"/></returns>
        public static IDirectory GetMasterNode()
        {
            IDirectory retVal = (IDirectory) HttpContext.Current.Cache.Get(CACHE_KEY);

            if (retVal == null)
            {
                IService<DirectoryModel> service = ServiceFactory.GetService<DirectoryModel>();
                retVal = service.Load(ConfigurationManager.AppSettings["root"]);
                HttpContext.Current.Cache.Insert(CACHE_KEY, retVal, null, DateTime.Now.Add(_expiration), TimeSpan.Zero);
            }

            return retVal; 
        }
    }
}

Note that the main web project only deals with interfaces and factories (and concrete models), not actual implementations of services (it doesn't even know about data access behind a service). This allows us plenty of flexibility to extend down the road.

The only other thing I did was create a base page which registers all of the includes and CSS for the project. This makes the default page nice and neat as all of the set up is done in the background. I used a loose version of my MVC for WebForms architecture, but didn't scope out interfaces and control/controller factories. This is another place where I can refactor down the road and abstract these a bit more. For now, the controllers simply inject the controls and use the cache to pull in the file system structure.

You can view a live copy of the browser by clicking here (and use it to browse the code). To download the source in a zip file, click here. I hope you enjoy and learn from this project and I look forward to doing some refactoring and extending it in future posts.

Jeremy Likness

Wednesday, June 10, 2009

Code Browser

Quick post ...

I was looking for a way to easily allow visitors to browse source code on the site. Then I realized, hey, why not make a project out of it? So on the weekend when I needed a break from writing wireless code, I put together a little code browser project that does just that. When I have more time (very busy right now) I'll put together a more detailed post to walk through the source, but in the meantime, you can use the Code Browser to, um, well, view the source code for the Code Browser!

Jeremy Likness

Friday, June 5, 2009

Inject Dynamic JavaScript into an ASP.NET UpdatePanel

I've been so caught up in my hand-rolled callback events and JQuery that I was presented with an interesting issue today: injecting JavaScript into an UpdatePanel. At first glance, it should be easy, but took a little digging. Turns out that the ScriptManager is the key.

Presented with this:

<asp:UpdatePanel ID="updPanel" UpdateMode="Conditional" runat="server">
   <Triggers>
      <asp:AsyncPostBackTrigger ControlID="btnClick" EventName="Click" />
   </Triggers>
   <ContentTemplate>
       <asp:ImageButton 
          ID="btnClick" 
          ImageUrl="button.png" 
          runat="server"
          CausesValidation="false" 
          OnClick="btnClick_Click"
          ToolTip="Click Me" 
          AlternateText="No, seriously, click me!" />
   </ContentTemplate>
</asp:UpdatePanel>

I wanted to dynamically inject some JavaScript. As long as my ScriptManager control has EnablePartialRendering set to true, it's a simple matter of registering the script. What threw me off is that you don't use the current Page or event current ScriptManager, you call the static method on the class. It looks like this:

protected void btnClick_Click(object sender, ImageClickEventArgs e)
{
   btnClick.Text = "I was clicked."
   btnClick.Disabled = true;
   ScriptManager.RegisterClientScriptBlock(updPanel, typeof(UpdatePanel),updPanel.ClientID, "alert('wow!');",true);            
}

That's it! Use the class, point it to the instance of your update panel, pass it a unique key (I'm cheating by using the ClientID but if I wanted multiple scripts I could generate multiple keys) and then the JavaScript you want, and it will be executed once the update panel is done rendering.

Jeremy Likness

Thursday, June 4, 2009

MVC vs. MVP: A question of pride?

My MVC with WebForms article generated an interesting comment that it was closer to MVP (model-view-presenter) and WebForms cannot or is not MVC. I replied with a point I think is relevant: we need to frame discussion around the pattern vs. the implementation. Microsoft MVC is an implementation, but I would suggest that having URL routing or having actions that return a view is part of an implementation, not fundamental to the MVC pattern.

Anyway, after some digging around to see where these ideas actually evolved from to get to where they are today, I found a very interesting article on GUI architectures that discusses both MVC and MVP.

My favorite take from the whole article:

As a result there's been a steady movement to design UIs in such a way that minimizes the behavior in objects that are awkward to test. Michael Feathers crisply summed up this approach in The Humble Dialog Box. Gerard Meszaros generalized this notion to idea of a Humble Object - any object that is difficult to test should have minimal behavior. That way if we are unable to include it in our test suites we minimize the chances of an undetected failure.

A question of pride? We need to code more humble objects. Seriously!

Jeremy Likness

Tuesday, June 2, 2009

JavaScript and Server Controls 101

After my previous post called JavaScript and User Controls 101, I had several people ask me if I could also talk about server controls. That is a great point so I threw together a quick little application to demonstrate server controls.

A common interview question is, "What is the difference between a user and a server control?" Of course, the server control is "server side" but can also have client side script. A server control is a truly atomic control that can be referenced in an external DLL and comes with its own properties and scripts. It can only be manipulated via the properties (both runtime and design-time) that it exposes. A user control, on the other hand, is a composite object that participates explicitly in page life-cycle events (init, load, render, etc) and can contain many other controls.

Server controls are typically used for fine-grained functionality. For example, a form that requests a block of information is most likely a user control, while a field in the form that is designed to allow entry of a file name might be a special server control (these are also referred to as ASP.NET controls).

For our example, we are going to make a new server control called a "BlurTextbox." The purpose of this control is to raise a callback event any time it loses focus. This will send the controls value to the server for some type of processing. Whatever the server returns will be evaluated as JavaScript.

In essence, you could use this to validate something on the server side and return a call to show an error message or an alert. Imagine, for example, entering a record with a unique name, and you want to give the illusion of "client-side validation" by showing if the name violates this constraint before the user even tabs from the form.

The logical way to extend a server control is to provide a client side representation. The AJAX framework provides extensive support for doing this, so let's get started.

First, we are going to inherit our control from a TextBox. I add a local reference to the ScriptManager that I can use in several places (this is the control we use to interact with the AJAX framework). I added a single property to my control called "ControlCallbackID." This property will have the client identifier for the control is going to respond to our callback request. The server control itself cannot plug into this (but it could expose events or delegates, that's beyond the scope of this example). To listen to the callback, we simply set this to the client ID of the control that is listening. In our example, it will be the actual page that hosts the control. Note the standard pattern to persist the property in the ViewState property bag.

    public class BlurTextbox : TextBox
    {
        private ScriptManager _sm;

        public string CallBackControlID
        {
            get
            {
                if (ViewState[CALLBACKCONTROLID] == null)
                {
                    return string.Empty;
                }

                return ViewState[CALLBACKCONTROLID].ToString();
            }

            set
            {
                ViewState[CALLBACKCONTROLID] = value;
            }
        }
       ...

The next step is to implement IScriptControl. This identifies the control as having a client-side counterpart. IScriptControl asks us to implement IEnumerable<ScriptDescriptor> GetScriptDescriptors() and IEnumerable<ScriptReference> GetScriptReference().

Script Descriptors

The script descriptors allow us to bind our server-side properties to the client-side JavaScript object. We start by identifying the fully qualified name of the object (we will have the namespace, OnBlurTextBox.TextBox, match on both the client and server). Then we add our properties by calling AddProperty with the client-side name of the property and the value for the property.

public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
   ScriptControlDescriptor descriptor = new ScriptControlDescriptor(
                GetType().FullName, ClientID);
   descriptor.AddProperty(JS_CALLBACKCONTROLID, CallBackControlID);
   return new ScriptDescriptor[] { descriptor };
}

Script References

The next piece to implement is a list of references for script for the control. In this case, I only have one JavaScript to include. It is an embedded resource with the same name as the control itself. Therefore, I can register it with the full namespace and the extension (.js) as well as the assembly. Don't forget that you must go into Properties -> AssemblyInfo.cs and add an [assembly: WebResource...] reference for this. The ScriptReference object has several overloads and can handle a URL, local, or embedded resource for the script (this is a good place, for example, to also include a library if you are using those functions along with your control).

public IEnumerable<ScriptReference> GetScriptReferences()
{           
   ScriptReference reference = new ScriptReference(string.Format({0}.js",GetType().FullName),
   GetType().Assembly.GetName().FullName);
   return new[] { reference };
        }

Now we need to register these with the AJAX script manager. In PreRender we register the control itself, letting AJAX know it must call our overrides and wire up a client-side object.

protected override void OnPreRender(EventArgs e)
        {
            if (!DesignMode)
            {
                _sm = ScriptManager.GetCurrent(Page);

                if (_sm == null)
                {
                    throw new HttpException(ERROR_SCRIPTMANAGER);
                }
               
                _sm.RegisterScriptControl(this);
            }

            base.OnPreRender(e);
        }

In Render, at the last possible moment, we register the descriptors to emit the properties.

 protected override void Render(HtmlTextWriter writer)
        {
            if (!DesignMode)
            {               
                _sm.RegisterScriptDescriptors(this);
            }

            base.Render(writer);
        }

That's it for the server control ... the rest is done on the client. Let's take a look at the client-side script:

/// 

Type.registerNamespace("OnBlurTextBox");

// we extend a textbox and really just add a property (callbackcontrolid) and 
// an event (onblur)
OnBlurTextBox.BlurTextbox = function(element) {
    OnBlurTextBox.BlurTextbox.initializeBase(this, [element]);
    // this is the property to refer to the corresponding callback control
    this._callBackControlID = null;
    this._oldBlur = null; 
}

// prototype for the clientside representation
OnBlurTextBox.BlurTextbox.prototype = {
    initialize: function() {
        OnBlurTextBox.BlurTextbox.callBaseMethod(this, 'initialize');
        // bind the handler for calling the callback on blur
        this._onblurHandler = Function.createDelegate(this, this._onBlur);
        // store any existing "blur" event
        this._oldBlur = this.get_element().onblur;
        // register the event handler
        $addHandlers(this.get_element(),
            { 'blur': this._onBlur },
            this);
    },
    dispose: function() {
        //Add custom dispose actions here
        $clearHandlers(this.get_element());
        OnBlurTextBox.BlurTextbox.callBaseMethod(this, 'dispose');
    },
    oldBlur: function() {
        if (this._oldBlur) {
            this._oldBlur();
        }
    },
    // getter for callback binding
    get_callBackControlID: function() {
        return this._callBackControlID;
    },
    // setter for callback binding
    set_callBackControlID: function(value) {
        if (this._callBackControlID != value) {
            this._callBackControlID = value;
            this.raisePropertyChanged('callBackControlID');
        }
    },
    // wire into the blur event to trigger the callback
    _onBlur: function(e) {
        if (this.get_element() && !this.get_element().disabled) {
            // callback to the server and then simply evaluate what is returned as javascript
            WebForm_DoCallback(this.get_callBackControlID(), this.get_element().value, function(args, ctx) {
                eval(args);
                ctx.oldBlur(); 
            }, this, null, false);
        }
    }
}

// register the class
OnBlurTextBox.BlurTextbox.registerClass('OnBlurTextBox.BlurTextbox', Sys.UI.Control);

// let 'em know we're loaded
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

There's a lot going on here, but it's not as complicated as it may first appear. First, we register the namespace for our control. Next, we create a prototype for instantiating the object. Notice that it takes a parameter called "element". This is the actual DOM element it will be bound to. Because we are extending a Textbox, the element will be an input element. The prototype is the JavaScript way of typing an object. The object has getters and setters as well as methods and handlers. The first thing to notice is how we initialize and expose getters and setters for the callbackControlID. Next, take a look at the "blur handler" which creates a delegate and registers the event. Finally, note our private field "_oldBlur" that holds the original blur event so we can chain to whatever is done by the consuming page or control.

The _onBlur event is straightforward. We make sure we have a reference to the valid input element and that it is not disabled. Then, we simply invoke WebForm_DoCallback. We pass the callback control (if this isn't set, the callback just gets ignored), the value of the element (input box) we are bound to, and pass an inline function for the callback to evaluate whatever was sent by the server and then call the original "blur" event. Note that we pass the AJAX object itself as "context" (the third to last parameter). This allows us to reference our specific instance on the callback to get the original blur event.

Finally, we register the class with the AJAX framework and notify that this script has been loaded.

Whew! That was a bit, but how do we use it? This is the fun part.

Open up Default.aspx. You'll notice I've inlined a bit of JavaScript. One function sets the focus for the control (using the timeout function for IE 6.0 compatibility). The other inserts text dynamically into an unordered list. We register the control at the top by pointing to the namespace and the assembly and giving it a prefix. Then, we embed two of our custom "blurtextbox" controls. I'll explain the button that does nothing in a minute. We also make sure we have a ScriptManager and that's it.

Now to the code. On the code-behind, I implement ICallbackEventhandler so I can process the events raised by the new controls. In the Page_Load I get a callback reference to make sure the page is listening. I also take the ClientID of the page and set it to the custom controls so they have their reference for the callback. Finally, I add a blur event to the second textbox. I'm basically asking it to set the focus back to the first textbox. This will demonstrate how our custom control does not override the events we set. The reason I put the dummy button on the page is because without it, the last tab on IE would set the focus to the address bar and I'd lose my ability to set focus on the page. By having a button, the default focus would go to the button, then I intercept this and set it back to the first textbox. If you tab through the textboxes, you should find you are alternating between the first and second.

The callback event itself is quite simple: I simply take the value and respond with a call to my function to insert the list item. This will basically build the unordered list on the page each time you tab or click out a text box. You can try typing "This[TAB]Is[TAB]A[TAB]Test" and you should see this:

  • This
  • Is
  • A
  • Test

That's it ... to summarize:

  1. Build your server control
  2. Implement IScriptControl to wire in your properties and script references
  3. Build your javascript to encapsulate whatever client-side functionality you need the control to exhibit
  4. Include the control in a page

That's all there is to JavaScript with server controls.

You can see the working application in action at http://apps.jeremylikness.com/onblurtextbox.

If you view the source, you can learn a lot more about how the AJAX framework interacts with server controls. The start of the page looks pretty much like our .aspx template, with a few added pieces such as the viewstate and event arguments. You'll notice that unlike our original embedded JavaScript (WebResource) we now have some ScriptResource references as well.

Our custom control looks just like an ordinary input box, nothing special. You can even see the blur event that we wired in:

   <div>
        Type something, then TAB out of the textbox<br /> 
        <input name="_bTextBox" type="text" id="_bTextBox" /> 
        <input name="_bTextBox2" type="text" id="_bTextBox2" onblur="setNewFocus('_bTextBox');" />    
        <input type="button" id="btn" value=" " />
        <br />
        (You can also try clicking between the textboxes)
    </div>

The key is at the bottom. Notice that there are two add_init calls to a function called $create. There you'll see something familiar: the reference to the callback control (pointing to the page as __Page, and the $get function to get a reference to the input boxes. This is where everything is bound (the prototype and actual call are in the script references that were included earlier).

Jeremy Likness

Monday, June 1, 2009

Model-View-Controller using ASP.NET WebForms View Engine

Posted my first official article to the CodeProject. We'll see how it goes. You can view it here: Model-View-Controller using ASP.NET WebForms View Engine.

Here is a local post for your viewing pleasure, but if you like it, I would ask that you head over to the CodeProject and let them know with your vote! Thanks!

Introduction

With the release of the ASP.NET MVC (Model-View-Controller) view engine, many developers are rejoicing that the old pattern has finally made it to the .NET world. In reality, there have been MVC view engines in existence for ASP.NET for quite some time. Contrary to popular belief, the common WebForms view engine, which is the default ASP.NET rendering engine so many developers have been familiar with, readily supports the model-view-controller paradigm. The purpose of this article is to demonstrate a sample framework that uses the MVC pattern successfully with the traditional ASP.NET WebForms engine.

Background

Model-View-Controller has been around for quite some time and is attributed to the SmallTalk language/platform. You'll find like many design patterns that actual explanations and implementation of the MVC pattern may vary. It is not my intent to provide a "pure" solution adhering to some golden standard, but to present a variation of MVC that works well with ASP.NET WebForms and explain what and why it is being used.

There are three intrinsic parts to MVC, and these are described as follows:

The Model

The model is often referred to as the "domain entity" and is essentially data. It is a container for information that is moving through your system. Typically, a model should contain attributes and properties but little to no function nor operation, short of constructors and possibly validation. An example of a model would be contact information or a set of credentials.

The View

The view takes the data from the model and presents it to the end user as meaningful information. A view might render a list as a bulleted list, a drop down, a multi-select box or event a paged grid. There might be a view for a console that emits text and another view for a rich web interface that contains 3D graphics controls.

I consider the following rules important to follow for a view.

  • A view only understands the model and its own "rendering space" (web, console, WinForm, etc).
  • The view is primarily concerned with mapping the model to the render space, and possibly receiving changes from that render space (i.e. user input).
  • Views should only manipulate data to the extent that it is necessary to pull from the model or place back within the model. Business logic belongs elsewhere (we'll discuss this in a minute with the controller)
  • Views do not understand how to write data to databases (or retrieve it) and are unaware of complex logic such as security concerns or calculations. Instead, views expose properties that allow their conditions to be set by another mechanism that understands these.
  • Views receive information via exposed properties and methods.
  • Views are like most people in the movie, "The Matrix" ... they are completely unaware that they are being controlled and have absolutely ZERO affinity to a controller.
  • Because views are ignorant of being controlled, if a view requires additional information or is responding to a command from the UI such as "delete" or "update," the view is only able to marshall these values into a container and raise an event. The view raises the event and forgets about it.

The separation of the view from the business logic is important. In a rapid prototyping environment when we need UI for proof of concept, we don't have time to wire in the backend services. A view that is ignorant of control can be easily set up with a "mock" controller that simply pushes static data for the prototype. On the flipside, in a production environment there may be a controller that manages lists of entities but must present these differently based on the user. In this case, multiple views can be invoked with the same controller. On a large development team, a view that is cleanly separated from its control can be developed independently of the controller itself, allowing for example the mobile device team to build the UI on the device while the Silverlight experts build their own rich control.

The Controller

Finally, we have the controller. The controller is responsible for handling all of the business logic, fetching data, processing changes, responding to events, and making it all work. The controller is aware of both the view and the model. However, I believe a controller should also follow a set of its own rules to fit solidly within an enterprise software environment.

  • A controller should never understand which concrete instance of a view it is working with but only interact with a view interface
  • The controller should never, ever concern itself with view logic. This means controllers do not emit HTML fragments, do not understand drop downs and don't have to worry about JavaScript. The controller simply deals with lists of models and events that contain models and nothing else. A true test of a controller is that it functions perfectly whether the view is console output, a WinForm, or a web interface.
  • Controllers should never try to interact with another controller's views. Instead, controllers can raise their own events for consumption by other controllers.
  • Controllers communicate to views by setting attributes or calling methods on the view, and by handling events raised by the view.
  • Controllers should never talk directly to another controller, only to a controller interface (therefore a "super" controller might have sub-controllers, but again, it is interacting with interfaces, not concrete instances).

By following these rules, you can have a flexible yet powerful controller architecture. One controller can generate the static models needed for a prototype. Another controller might set up some mock objects to run unit tests, while the production controller interacts with services to pull lists of models and manipulate the models by responding to events.

MVC (model-view-controller) Implementation Diagram

Traditional, out-of-the-box ASP.NET seems to violate these principles. While the code-behind separates the code from the display elements, there is a strong affinity between the two. In fact, most pages must embed a specific, static reference to user controls in order for them to function. This creates a dependency that doesn't allow the flexibility we discussed. Furthermore, doing things like taking a text field value and moving it into the model and sending to the service violates the rules of the controller not understanding the nuances of the view or the view not knowing its controller.

Fortunately, through the use of a framework to define the views as controls and controllers, along with dynamic user controls, we can build a platform that adheres to the tenants of MVC. Dynamic user controls are very important so that the controller can deal with the view interface and a factory or other mechanism can invoke the concrete instance of the view being used at runtime. The included application presents this framework and provides a proof-of-concept for showing a controller and two views that are controller-ignorant yet can still interact through the use of events.

Using the Code

The included code is a proof-of-concept implementation of the MVC pattern using WebForms. The source application includes a full-blown framework to support the application. The "model" exists in our domain projects with the purpose to describe the business data. In our example, this is a set of reserved keywords for the C# language. The KeywordModel contains a unique identifier for the keyword and the value which is the keyword itself.

The code is straightforward and should compile "out of the box". You can right-click on the Default.aspx page to view. You'll see a very plain page with two columns: one with a "currently selected" keyword and a dropdown, another with a list of all of the keywords, and finally a submit button. When you select a keyword, it should dynamically show next to the "selected" label, and the corresponding keyword in the right column will become bold. When you click submit, the last selected keyword will render red in the right column to demonstrate the server-side actions that "remember" the recent selection (there is no query string nor forms parsing in the application).

I created an Interface.Data to describe interactions with persisted data. There is a load and a list function. To save time, instead of wiring into a full-blown database, I simply embedded an XML resource and used a concrete class called KeywordDataAccess to pull values from the XML. In my applications, I try to keep the data layer as focused as possible (reads, writes, updates). Any business logic such as calculations and sorts are in the service layer that sits on top of the data layer. You'll find that everything above the data layer references an IDataAccess<KeywordModel>, not a concrete instance.

The service layer simply invokes the data layer to grab the data. Note that we use the factory pattern to grab our concrete instance. This will allow us to stub out "mock" data layers for unit tests or even swap the XML data layer with a SQL, Access, or other data layer without having to change anything else but the concrete class for the data layer and the instance that the factory returns. For a larger application, of course, a Dependency Injection framework would help wire these in — hence the constructor that takes the data access reference for constructor injection. In this case, the only "extra" handling of the domain model that the service is required to do is to perform a sort of the data prior to presenting it (see the sort in the List method).

Finally we get to the presentation layer, serviced as a web application. We've already discussed the model that contains the business data. Now we have two more pieces: the view, and the controller. Let's talk about the controller first.

For the application to truly scale, the controller should function independently of the specific view it needs to control. A controller is simply of type T (where T is a model) and can operate on any view of type T. The following diagram illustrates the controller interface and implementation.

MVC WebForms Controller Diagram

Note that the controller itself simply has a constructor that takes in the view it will be managing. Everything else is handled by the base class, DynamicController<T> that implements IController<T>. There are a few things we have wired into our base controller:

  • Context — a guid is generated to manage state through callbacks and postbacks.
  • The controller knows the type (T) it is managing, so our model allows us to simply go to the ServiceFactory and get the default service for T. Note that a SetService method is also exposed for injection of the service for things like unit tests using mocked services.
  • The controller keeps track of its view (_control) and exposes events for other controllers. Remember our rule: controllers talk to controllers and their own control, controls only raise events and are ignorant of being controlled.
  • There is a list of models along with a "current" model
  • The controller responds to a "select" event. If we were performing CRUD or deletes, etc, we would process this in the controller and then pass it through. In our sample, we just pass it through so any higher-level controllers can respond to the event as well.
  • Finally, we have an Initialize method that is meant to be invoked the first time the controller is created. Subsequent postbacks and callbacks do not call this method, and internal lists are managed via state. More on that when we discuss the controls.

The interface is simple:

using System;
using Interface.Domain;

namespace Interface.Controller
{
    /// <summary>
    ///     Interface for a controller 
    /// </summary>    
    public interface IController<T> where T : IKey, new() 
    {
        /// <summary>
        ///     Called the first time to initialize the controller
        /// </summary>
        void Initialize();

        /// <summary>
        ///     Raised when a model is selected
        /// </summary>
        event EventHandler<EventArgs> OnSelect;

        /// <summary>
        ///     Current active or selected model
        /// </summary>
        T CurrentModel { get; set; }

        /// <summary>
        ///     Context (state management)
        /// </summary>
        Guid CurrentContext { get; set; }
    }
}

...and the base controller class:

    /// <summary>
    ///     Controller base class
    /// </summary>
    /// <typeparam name="T">The type to control</typeparam>
    public class DynamicController<T> : IController<T> where T : IKey, new()
    {
        /// <summary>
        ///     Manages the state of the controller and control
        /// </summary>
        public Guid CurrentContext
        {
            get { return _control.CurrentContext; }
            set { _control.CurrentContext = value; }
        }

        /// <summary>
        ///     Inject the control and grab the default service
        /// </summary>
        /// <param name="control"></param>
        public DynamicController(IControl<T> control)
        {
            _control = control;
            _service = ServiceFactory.GetDefaultService<T>();
            _control.NeedData += _ControlNeedData;
            _control.OnSelect += _ControlOnSelect;
        }

        /// <summary>
        ///     Fired when the child control raises the select event
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Args</param>
        private void _ControlOnSelect(object sender, EventArgs e)
        {
            if (OnSelect != null)
            {
                OnSelect(this, e);
            }
        }

        /// <summary>
        ///     Needs the list again
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _ControlNeedData(object sender, EventArgs e)
        {
            _control.ControlList = _service.List();
        }

        /// <summary>
        ///     Fired when a model is selected from the list
        /// </summary>
        public event EventHandler<EventArgs> OnSelect;

        /// <summary>
        ///     The control this controller will work with
        /// </summary>
        protected IControl<T> _control;

        /// <summary>
        ///     Service related to the entity
        /// </summary>
        protected IService<T> _service;

        /// <summary>
        ///     Current active or selected model
        /// </summary>
        public virtual T CurrentModel
        {
            get { return _control.CurrentModel; }
            set { _control.CurrentModel = value; }
        }

        /// <summary>
        ///     Allows injection of the service
        /// </summary>
        /// <param name="service">The service</param>
        public virtual void SetService(IService<T> service)
        {
            _service = service;
        }

        /// <summary>
        ///     Called the first time to initialize the controller
        /// </summary>
        public virtual void Initialize()
        {
            _control.Initialize(_service.List());
        }
    }

Now that we have a decent understanding of the controller, let's move on to the controls. The project defines two controls for the same model: KeywordDropdownView and KeywordListView.

MVC WebForms Controls Diagram

Both of these views implement IControl<T>, the view for the KeywordModel entity. The controls are a bit more in depth. You'll note the controls contain an IControlCache which allows the control to persist internal state. For example, instead of going out to the service (and hence, data) layers to request a list each time, the control can store these lists in the cache. When the cache expires, the control simply raises the NeedData event and the controller will supply a new list.

Two examples of the IControlCache implementation are included to give you some ideas of how to code this. One, SimpleCache, simply shoves the objects in the Session object. The other takes advantage of the ASP.Net Cache object and stores the items in cache for 5 minutes.

Each control is wired to receive a context: the unique GUID that identifies the instance. This solves a common problem. Many developers are happy to store objects in session with generic keys, like this:

...
Session["ControlList"] = ControlList;
...

The problem is that if you open multiple tabs in the same browser, each tab now fights for the same session variable and you can collide across pages. Generating a GUID in the page and using the guid for caching and session storage ensures that each instance in the browser, even when they share the same session, is managed appropriately.

The view contract:

    /// <summary>
    ///     Interface for a generic control
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IControl<T> where T : IKey, new()
    {
        /// <summary>
        ///     Initializes the control with the list of items to manage
        /// </summary>
        /// <param name="list"></param>
        void Initialize(List<T> list);

        /// <summary>
        ///     Raised when something is selected from the list
        /// </summary>
        event EventHandler<EventArgs> OnSelect;

        /// <summary>
        ///     Raised when the control needs data again
        /// </summary>
        event EventHandler<EventArgs> NeedData;

        /// <summary>
        ///     The list for the control
        /// </summary>
        List<T> ControlList { get; set; }

        /// <summary>
        ///     The current active model
        /// </summary>
        T CurrentModel { get; set; }

        /// <summary>
        ///     Caching mechanism for the control to save/load state
        /// </summary>
        IControlCache ControlCache { get; set; }

        /// <summary>
        ///     Context (state management) for the control
        /// </summary>
        Guid CurrentContext { get; set; }
    }

And the view base class:

    /// <summary>
    ///     A dynamic control
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class DynamicControl<T> : UserControl, IControl<T> where T : IKey, new()
    {
        /// <summary>
        ///     Context for this control
        /// </summary>
        private Guid _context = Guid.Empty;

        /// <summary>
        ///     Cache for the control
        /// </summary>
        public IControlCache ControlCache { get; set; }

        /// <summary>
        ///     Unique context for storing state
        /// </summary>
        public Guid CurrentContext
        {
            get
            {
                if (_context.Equals(Guid.Empty))
                {
                    _context = Guid.NewGuid();
                }

                return _context;
            }

            set
            {
                _context = value;
                LoadState();
            }
        }


        /// <summary>
        ///     List that the control works with
        /// </summary>
        public virtual List<T> ControlList { get; set; }

        /// <summary>
        ///     The current selected model
        /// </summary>
        public virtual T CurrentModel { get; set; }

        /// <summary>
        ///     Initializes the control with the list of items to manage
        /// </summary>
        /// <param name="list"></param>
        public virtual void Initialize(List<T> list)
        {
            ControlList = list;
        }

        /// <summary>
        ///     Allow override of tis event
        /// </summary>
        public virtual event EventHandler<EventArgs> OnSelect;

        /// <summary>
        ///     Need data?
        /// </summary>
        public virtual event EventHandler<EventArgs> NeedData;

        /// <summary>
        ///     Load event - allow things to wire and settle before trying to bring in state
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            LoadState();
        }

        /// <summary>
        ///     Last thing to do is save state
        /// </summary>
        /// <param name="e"></param>
        protected override void OnUnload(EventArgs e)
        {
            SaveState();
            base.OnUnload(e);
        }

        /// <summary>
        ///     Save the control state
        /// </summary>
        public virtual void SaveState()
        {
            if (ControlCache != null)
            {
                object[] savedState = new object[] {ControlList ?? new List<T>(), CurrentModel};
                ControlCache.Save(CurrentContext, savedState);
            }
        }

        /// <summary>
        ///     Load the control state
        /// </summary>
        public virtual void LoadState()
        {
            if (ControlCache != null)
            {
                object[] loadedState = ControlCache.Load(CurrentContext) as object[];
                if (loadedState != null && loadedState.Length == 2)
                {
                    ControlList = loadedState[0] as List<T> ?? ControlList;
                    CurrentModel = (T) loadedState[1];                    
                }
            }
        }
    }

The KeywordDropdownView view simply takes the list of models from the controllers and renders them into a drop down. It contains some JavaScript code (embedded as a resource, this is covered in more detail in JavaScript and User Controls 101) that responds to a selection. The example demonstrates how a control can be responsive on both the client and server sides. When a new keyword is selected in the dropdown, it raises an event on the client that other controls can subscribe to called keywordDropdownChanged. It then does a callback to the control on the server side, which raises the OnSelect event for server-side management. We'll examine these events in more detail.

Instead of my favorite JQuery library add-on, I decided to use traditional JavaScript that is cross-browser compatible (I tested on IE 6, IE 7, IE 8, and FireFox). This will give you some examples of DOM manipulation on the fly as well as one way to wire in a callback.

The KeywordListView uses a simple repeater to list the keywords as labels, which render as DIV tags on the client. Again, this is a proof of concept to show two interactions. First, on the client, the control registers for the keywordDropdownChanged event. When the event is raised, it searches through its own rendered list of keywords and changes the target to bold. You can see this happen on the client as an example of two controls talking to each other without any knowledge of their existance nor implementation.

The second pieces is that the main page itself acts as a "master" controller. It responds to the OnSelect event from the dropdown control, and sends the selected keyword to the list control. The list control persists this information and colors the keyword red when it renders a new list. You can see this behavior by selecting a keyword and then clicking submit. Notice that all of the interactions are done through events, not some awkward mechanism that is parsing form data and responding by calling various controls directly.

The final piece that really ties the MVC concept together is the main page. Here you'll see we are only dealing with abstractions (IController<KeywordModel>). This is where I feel my framework breaks with many traditional models I've seen. Most attempts at MVC in WebForms still require a strong affinity between the page and the control. How many times have you found yourself embedding a user control by using the <%Register%> tag to point to a control? This leaves little room for flexibility (for example, grabbing a different view if the user is on a PDA, but using the same controller).

In our example, we simply put placeholders in the page where the views can render. We use a view factory to grab the view. There is a default view mapped to the dropdown and a more specific view requested for the repeater. The same controller manages both views, proof of true abstraction of the controller from the view it is controlling. The page, as a "master" controller, generates a context that both controllers can share, and coordinates the controllers by taking an event from one and pushing the value to the other. The controllers themselves are ignorant both of each other's existance as well as the actual implementation of the view they manage — despite the dropdown and list, the controller still simply works with an IControl<KeywordModel>.

This is the "skeleton" for the page:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DynamicControls.Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Dynamic Control Example</title>
</head>
<body>
    <h1>Dynamic Control Example</h1>
    <form 
        id="_form1" 
        runat="server">
        <asp:ScriptManager ID="_sm" runat="server" />
        <asp:HiddenField ID="_hdnGlobalContext" runat="server" />
        <table>
            <tr valign="top">
                <td>
                    <asp:Panel 
                        ID="_pnlLeft" 
                        runat="server" />
                </td>
                <td>
                    <asp:Panel 
                        ID="_pnlRight" 
                        runat="server" />
                </td>
                <td>
                    <asp:Button 
                        ID="_btnSubmit"     
                        runat="server" 
                        Text=" Submit " />                
                </td>
            </tr>
        </table>        
    </form>
</body>
</html>

And here is the "master controller" code behind. Notice how we reference the interface and the factory. We wouldn't even reference control at all except to manage the event args, this would normally be hidden inside a "master controller."

using System;
using System.Web.UI;
using Domain;
using DynamicControls.Control;
using DynamicControls.Factory;
using Interface.Controller;

namespace DynamicControls
{
    /// <summary>
    ///     Default page
    /// </summary>
    public partial class Default : Page
    {
        /// <summary>
        ///     Controller for the page
        /// </summary>
        private IController<KeywordModel> _ctrlrLeft;

        /// <summary>
        ///     Another controller
        /// </summary>
        private IController<KeywordModel> _ctrlrRight;

        private Guid _context = Guid.Empty;

        /// <summary>
        ///     Init
        /// </summary>
        /// <param name="e"></param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            _ctrlrLeft = ControllerFactory.GetDefaultController(ViewFactory.GetDefaultView<KeywordModel>(_pnlLeft));
            _ctrlrRight = ControllerFactory.GetDefaultController(ViewFactory.GetKeywordListView(_pnlRight));

            _ctrlrLeft.OnSelect += _CtrlrLeftOnSelect;
        }

        /// <summary>
        ///     This is when viewstate is first available to us to wire in the appropriate context
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreLoad(EventArgs e)
        {
            base.OnPreLoad(e);
            // bind a global context so the controllers and controls all can talk to each other with the same 
            // instance of state
            if (string.IsNullOrEmpty(_hdnGlobalContext.Value))
            {
                _context = Guid.NewGuid();
                _hdnGlobalContext.Value = _context.ToString();
            }
            else
            {
                _context = new Guid(_hdnGlobalContext.Value);
            }

            _ctrlrLeft.CurrentContext = _context;
            _ctrlrRight.CurrentContext = _context;
        }

        /// <summary>
        ///     Let the right hand know what the left hand is doing
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _CtrlrLeftOnSelect(object sender, EventArgs e)
        {
            KeywordSelectArgs args = e as KeywordSelectArgs;
            if (args != null)
            {
                _context = args.CurrentContext;
                _ctrlrRight.CurrentContext = _context;
                _ctrlrRight.CurrentModel = args.SelectedModel;
            }
        }

        /// <summary>
        ///     Page load event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsCallback && !IsPostBack)
            {
                _ctrlrLeft.Initialize();
                _ctrlrRight.Initialize();
            }
        }
    }
}

I purposefully used a straight script reference for the list control (instead of an embedded resource) so you can get a feel for the differences between the two options.

Note how the state management works in the list control. It shoves the list and the selected model into an array of objects and then sends these to the IControlCache. The GUID controls the keys for the cache. The master page sets this initially. When the dropdown fires the callback, it passes the context to the server, so the server can reset the controls with the context and make sure they are pulling the correct state. Otherwise, the control would have no "idea" of what the list was or what the currently selected model is.

That explains the sample application and framework. You can play with a working copy of it at http://apps.jeremylikness.com/mvcwebform/. This is a bare bones proof-of-concept, no fancy graphics or fonts.

Points of Interest

I always like to view the source and see just how much is being rendered: what and why. Of course, the first thing you'll notice is the view state. Because we are managing our own state via the guid that is embedded in the page (just view source and search for _hdn), we could actually turn viewstate off.

ASP.NET wires in the __doPostBack function which is bound to controls like the submit button. After this function, you can see the JavaScript for our custom controls. The first embedded one references WebResource.axd which is responsible for pulling the JavaScript out of the assembly and rendering it to the browser. The next is a straight reference to KeywordListView.js because we did not embed it. This is followed by a few includes that wire up the AJAX framework, and then we're in the midst of our actual controls.

Note the wired "onchange" on the select box. We bound this on the server side and emitted the context as well as the client IDs of the various controls. We do this because the code must work no matter how nested the control is. Each control wires its respective init function, and the last code emitted is the initialization for the AJAX framework itself.

To extend this framework, you will want to create different types of views (IListControl, IUpdateControl, etc) that perform different functions (the most fun I had with my own company was creating the controller and control for grids that manage large data sets and paging). You can even experiment with unit tests and building "test" controls that don't inherit from UserControl and therefore don't require HttpContext to work. There are lots of possibilities but hopefully this example forms a decent foundation for you to work from.

Jeremy Likness