Richard Rasala’s Blog

Friday, May 23, 2008       12:50:51 PM

Blog Software Bug: Definition and Calculation of a Recent Item

Carl Nayak pointed out to me a bug in the Blog.cs code in terms of the computation of what is a recent blog item. The boolean expression used in the original code was utterly incorrect.

In conversation with Carl, I decided that not only should the technical bug be fixed, the definition of recent should be changed.

First of all, recent should be measured from the date of the most recent blog entry not from the date that a visitor accesses the blog. In that way, if the blog has not been active, the visitor will still see content and will see the date of the most recent item since by default the blog initially posts in reverse order.

Secondly, Carl suggested to me that there should be a minimum number of recent items posted. What this minimum should be is somewhat arbitrary and I decided to choose 5.

The algorithm for forming the collection of recent blog items is now implemented as follows:

  • Initialize the collection of recent blog items with the 5 most recent blog items.
  • Expand the collection of recent blog items to include any additional items posted within one month of the most recent item posted.
Friday, March 21, 2008       9:16:35 AM

Deep Zoom Samples

The Microsoft Blend and Design team produced a “Hello World” example for pan and zoom of images in Silverlight 2:

Deep Zoom Sample with MouseWheel/Pan/Click-Zoom

This example is based on work by Scott Hanselman:

MultiScaleImage Mouse Wheel Zooming and Panning

I decided to download the Blend and Design sample, examine it, and install it on my web site. Here is a link:

Deep Zoom Sample

I also decided to build a second parallel project usings my own photos taken at the Broadmoor Audubon Sanctuary in Natick, MA. I started with a new Silverlight project in VS 2008 and adapted code from the first project. I also needed to make some changes to this default project to get things to work.

Broadmoor Audubon Sanctuary Photos

Before getting into details, let me explain how the mouse works in these applications.

  • Click: Zoom in by 2
  • Shift-click: Zoom out by 2
  • Scroll-wheel-forward: Gradual zoom in
  • Scroll-wheel-reverse: Gradual zoom out
  • Drag: Pan

On a PC, you must click once or press space-bar to activate the photo control.

If you wish to look at Sources Server, both projects are located in the silverlight2 folder on my site. To make new file types visible in Sources Server, I added the following types to FileTools.cs.

  • .xaml
  • .csproj
  • .user

Now to the details ...

The Blend and Design DeepZoomSample ships as a Visual Studio 2008 “solution” in which there are 2 projects named DeepZoomOutput and DeepZoomOutput_Web. The first project contains the code and is used to compile information for the second project which is used for deployment on the web. Here are screen snapshots of the file structure of this solution when directly opened in VS 2008.

DeepZoomOutput viewed in the solution

    DeepZoomOutput viewed in the solution

DeepZoomOutput_Web viewed in the solution

    DeepZoomOutput_Web viewed in the solution

Since our server setup at CCIS only permits us one web site overall, I wanted to embed this solution into a subfolder of my web site in such a way that it would deploy without error. I was able to do this. Here are screen snapshots of the file structure of this solution when I open my entire web site project in VS 2008 and then drill down into the appropriate subfolder.

DeepZoomOutput viewed in the web site

    DeepZoomOutput viewed in the web site

DeepZoomOutput_Web viewed in the web site

    DeepZoomOutput_Web viewed in the web site

You will notice that several additional files are visible. In a VS web site project, all files in all folders are visible. In contrast, in a solution, additional helper files are maintained in the solution file structure but are not shown in VS. Thus, by placing the solution within the web site, I get to see what is really present. Of course, it is important to make sure that what is placed within the web site does not prevent the site from being compiled and deployed. I will revisit this.

First let us look at the file DeepZoomOutput.html used to deploy the DeepZoomOutput sample.

<!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" >
<!-- saved from url=(0014)about:internet -->
<head>
    <title>Silverlight Project Test Page </title>

    <style type="text/css">
    html, body {
        height: 100%;
        overflow: auto;
    }
    body {
        padding: 0;
        margin: 0;
    }
    #silverlightControlHost {
        height: 100%;
    }
    </style>
    
    <script type="text/javascript">
        function onSilverlightError(sender, args) {
            if (args.errorType == "InitializeError")  {
                var errorDiv = document.getElementById("errorLocation");
                if (errorDiv != null)
                    errorDiv.innerHTML = args.errorType + "- " + args.errorMessage;
            }
        }
    </script>
</head>

<body>
    <h3>
    This site is copied from the
    <a
        href="http://blogs.msdn.com/expression/archive/2008/03/09/deep-zoom-sample-with-mousewheel-pan-click-zoom.aspx"
        target="_blank">
    Deep Zoom Sample</a>
    site.<br />
    It is used to both to test the installation of Silverlight 2 Beta 1 and to illustrate deep zoom.
    </h3>
    
    <!-- Runtime errors from Silverlight will be displayed here.
    This will contain debugging information and should be removed or hidden when debugging is completed -->
    <div id='errorLocation' style="font-size: small;color: Gray;"></div>

    <div id="silverlightControlHost">
        <object
            data="data:application/x-silverlight,"
            type="application/x-silverlight-2-b1"
            width="100%" height="100%">
            <param name="source" value="ClientBin/DeepZoomOutput.xap"/>
            <param name="onerror" value="onSilverlightError" />
            <param name="background" value="white" />
            
            <a
                href="http://go.microsoft.com/fwlink/?LinkID=108182"
                style="text-decoration: none;">
                <img
                    src="http://go.microsoft.com/fwlink/?LinkId=108181"
                    alt="Get Microsoft Silverlight"
                    style="border-style: none"/>
            </a>
        </object>
        <iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe>
    </div>
</body>
</html>

Notice:

  • The first few lines of the body simply provide a link to the original source of the sample on the Blend and Design site.
  • The inline Javascript in the head simply wires up the error handler to place debug information in the errorLocation div tag.
  • The Silverlight object is place in the silverlightControlHost div tag.
  • An HTML object tag is used bring in the Silverlight code which is stored in the file ClientBin/DeepZoomOutput.xap.
  • There is a link to get the Silverlight 2.0 Beta 1 plug-in if it is not already installed on the system.

The file ClientBin/DeepZoomOutput.xap is in fact a zip file with a special extension that is supposed to indicate that it contains information for a Silverlight application or control. I suppose that Microsoft had good reasons for doing this but it meant that Peter Douglass had to go into the IIS server to add a line that would permit this file to be served. The contents of this file are:

DeepZoomOutput.xap opened in WinZip

    DeepZoomOutput.xap opened in WinZip

The first file, DeepZoomOutput.dll, is the compiled binary of the the code in the files in the DeepZoomOutput development project in the solution. It is the heart of what will actually happen on the web site.

The next two files, System.Windows.Controls.dll and System.Windows.Controls.Extended.dll are the fixed libraries of Silverlight code. You will notice that these files are large and total close to 470K.

The last file, AppManifest.xaml, simply is an XML file that collects the information of what files are being deployed. Here is its contents.

<Deployment
     xmlns="http://schemas.microsoft.com/client/2007/deployment"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     EntryPointAssembly="DeepZoomOutput"
     EntryPointType="DeepZoomOutput.App"
     RuntimeVersion="2.0.30226.2">
  <Deployment.Parts>
    <AssemblyPart x:Name="DeepZoomOutput"
        Source="DeepZoomOutput.dll" />
    <AssemblyPart x:Name="System.Windows.Controls"
        Source="System.Windows.Controls.dll" />
    <AssemblyPart x:Name="System.Windows.Controls.Extended"
        Source="System.Windows.Controls.Extended.dll" />
  </Deployment.Parts>
</Deployment>

From Microsoft’s viewpoint, the big advantage of using a single file, ClientBin/DeepZoomOutput.xap, is that precisely one file is downloaded to launch an application.

However, to me, this design has the serious disadvantage that the lion’s share of the bytes do not change frequently but the browser cannot cache this data since the data is hidden in the .xap file which does change every time the application is recompiled. In particular, there is no way to share the libraries across applications.

Let us now look at the source files in the DeepZoomOutput project in the solution.

The application root is in App.xaml.

<Application
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    x:Class="DeepZoomOutput.App">
    <Application.Resources>
        
    </Application.Resources>
</Application>

The code-behind is in App.xaml.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DeepZoomOutput
{
    public partial class App : Application
    {

        public App()
        {
            this.Startup += this.Application_Startup;
            this.Exit += this.Application_Exit;
            this.UnhandledException += this.Application_UnhandledException;

            InitializeComponent();
        }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            // Load the main control
            this.RootVisual = new Page();
        }

        private void Application_Exit(object sender, EventArgs e)
        {

        }
        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {

        }
    }
}

The root page is in Page.xaml.

<UserControl x:Class="DeepZoomOutput.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="640" Height="480" OpacityMask="#FF000000">

    <Canvas x:Name="LayoutRoot" Background="#FFFFFFFF">
        <MultiScaleImage
            Source="vistaWallpapers/info.bin" x:Name="msi"
            Width="600" Height="440"
            Canvas.Left="20" Canvas.Top="20"/>
    </Canvas>
</UserControl>

The code-behind is in Page.xaml.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DeepZoomOutput
{
    public partial class Page : UserControl
    {
        //
        // Based on prior work done by Lutz Gerhard, Peter Blois, and Scott Hanselman
        //
        Point lastMousePos = new Point();

        double _zoom = 1;
        bool mouseButtonPressed = false;
        bool mouseIsDragging = false;
        Point dragOffset;
        Point currentPosition;

        public double ZoomFactor
        {
            get { return _zoom; }
            set { _zoom = value; }
        }

        public Page()
        {

            InitializeComponent();
            this.MouseMove += delegate(object sender, MouseEventArgs e)
            {
                if (mouseButtonPressed)
                {
                    mouseIsDragging = true;
                }
                this.lastMousePos = e.GetPosition(this.msi);
            };

            this.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
            {
                mouseButtonPressed = true;
                mouseIsDragging = false;
                dragOffset = e.GetPosition(this);
                currentPosition = msi.ViewportOrigin;
            };

            this.msi.MouseLeave += delegate(object sender, MouseEventArgs e)
            {
                mouseIsDragging = false;
            };

            this.MouseLeftButtonUp += delegate(object sender, MouseButtonEventArgs e)
            {
                mouseButtonPressed = false;
                if (mouseIsDragging == false)
                {
                    bool shiftDown = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;

                    ZoomFactor = 2.0;
                    if (shiftDown) ZoomFactor = 0.5;
                    Zoom(ZoomFactor, this.lastMousePos);
                }
                mouseIsDragging = false;
            };

            this.MouseMove += delegate(object sender, MouseEventArgs e)
            {
                if (mouseIsDragging)
                {
                    Point newOrigin = new Point();
                    newOrigin.X = currentPosition.X
                        - (((e.GetPosition(msi).X - dragOffset.X) / msi.ActualWidth) * msi.ViewportWidth);
                    newOrigin.Y = currentPosition.Y
                        - (((e.GetPosition(msi).Y - dragOffset.Y) / msi.ActualHeight) * msi.ViewportWidth);
                    msi.ViewportOrigin = newOrigin;
                }
            };

            new MouseWheelHelper(this).Moved += delegate(object sender, MouseWheelEventArgs e)
            {
                e.Handled = true;
                if (e.Delta > 0)
                    ZoomFactor = 1.2;
                else
                    ZoomFactor = .80;

                Zoom(ZoomFactor, this.lastMousePos);
            };
        }

        public void Zoom(double zoom, Point pointToZoom)
        {
            Point logicalPoint = this.msi.ElementToLogicalPoint(pointToZoom);
            this.msi.ZoomAboutLogicalPoint(zoom, logicalPoint.X, logicalPoint.Y);
        }
    }
}

The file Page.xaml.cs contains the basic mouse code but the mouse-wheel code is deferred to MouseWheelHelper.cs.

using System;
using System.Windows;
using System.Windows.Browser;

namespace DeepZoomOutput
{

    // Courtesy of Pete Blois
    public class MouseWheelEventArgs : EventArgs {
        private double delta;
        private bool handled = false;

        public MouseWheelEventArgs(double delta) {
            this.delta = delta;
        }

        public double Delta {
            get { return this.delta; }
        }

        // Use handled to prevent the default browser behavior!
        public bool Handled {
            get { return this.handled; }
            set { this.handled = value; }
        }
    }

    public class MouseWheelHelper {

        public event EventHandler<MouseWheelEventArgs> Moved;
        private static Worker worker;
        private bool isMouseOver = false;

        public MouseWheelHelper(FrameworkElement element) {

            if (MouseWheelHelper.worker == null)
                MouseWheelHelper.worker = new Worker();

            MouseWheelHelper.worker.Moved += this.HandleMouseWheel;

            element.MouseEnter += this.HandleMouseEnter;
            element.MouseLeave += this.HandleMouseLeave;
            element.MouseMove += this.HandleMouseMove;
        }

        private void HandleMouseWheel(object sender, MouseWheelEventArgs args) {
            if (this.isMouseOver)
                this.Moved(this, args);
        }

        private void HandleMouseEnter(object sender, EventArgs e) {
            this.isMouseOver = true;
        }

        private void HandleMouseLeave(object sender, EventArgs e) {
            this.isMouseOver = false;
        }

        private void HandleMouseMove(object sender, EventArgs e) {
            this.isMouseOver = true;
        }

        private class Worker {

            public event EventHandler<MouseWheelEventArgs> Moved;

            public Worker() {

                if (HtmlPage.IsEnabled) {
                    HtmlPage.Window.AttachEvent("DOMMouseScroll", this.HandleMouseWheel);
                    HtmlPage.Window.AttachEvent("onmousewheel", this.HandleMouseWheel);
                    HtmlPage.Document.AttachEvent("onmousewheel", this.HandleMouseWheel);
                }

            }

            private void HandleMouseWheel(object sender, HtmlEventArgs args) {
                double delta = 0;

                ScriptObject eventObj = args.EventObject;

                if (eventObj.GetProperty("wheelDelta") != null) {
                    delta = ((double)eventObj.GetProperty("wheelDelta")) /120;


                    if (HtmlPage.Window.GetProperty("opera") != null)
                        delta = -delta;
                }
                else if (eventObj.GetProperty("detail") != null) {
                    delta = -((double)eventObj.GetProperty("detail"))/3;

                    if (HtmlPage.BrowserInformation.UserAgent.IndexOf("Macintosh") != -1)
                        delta = delta * 3;
                }

                if (delta != 0 && this.Moved != null) {
                    MouseWheelEventArgs wheelArgs = new MouseWheelEventArgs(delta);
                    this.Moved(this, wheelArgs);

                    if (wheelArgs.Handled)
                        args.PreventDefault();
                }
            }
        }
    }
}

More to follow ....................

Friday, March 21, 2008       7:10:41 AM

Silverlight 2 Beta 1 Downloads

Silverlight 2 Beta 1 was released at the opening of the MIX 08 conference on March 5, 2008. In my opinion, the technology is awesome although there are still many things that developers need to do by hand since the software is still in beta. At the MIX 08 site, there are videos of all of the conference presentations.

The Silverlight home page is at:

Silverlight Logo

The Silverlight home page URL is: http://silverlight.net/

To develop Silverlight 2 in Visual Studio 2008, you should download the tools:

Microsoft Silverlight 2 Beta 1 Tools for Visual Studio 2008

This installer download includes:

  • Silverlight 2 Beta 1
  • Silverlight 2 SDK Beta 1
  • Update KB949325 for Visual Studio 2008
  • Silverlight Tools Beta 1 for Visual Studio 2008

This installer may fail for one of six reasons which I quote:

An Error Has Occurred:
Silverlight Tools cannot be installed because one or more of the following conditions is true:

1. Visual Studio 2008 RTM is not installed.
2. The Web Authoring feature of Visual Studio is not installed.
3. A previous version of the Silverlight Runtime is installed.
4. A previous version of the Silverlight SDK is installed.
5. The Visual Studio Update KB949325 is installed.
6. A previous version of Silverlight Tools is installed.

To continue, please install or uninstall the appropriate products and run this installer again.

Item 1 should not be a problem since the MSDN-AA copy of Visual Studio 2008 is “Release To Market”.

Item 2 may be a problem if you did not do a complete install of VS 2008. To check if “Web Authoring” is installed use the About menu item from the Help menu. If need be, use the install disc to add this feature.

Item 5 should not be a problem since update KB949325 is the one added with this installer and should not have been installed previously.

Items 3,4,6 will definitely be a problem if you have installed an earlier version of Silverlight. Go to Add/Remove Programs and remove any items with Silverlight in the name before doing the install of Silverlight 2 Beta 1.

On the same page as Microsoft Silverlight 2 Beta 1 Tools for Visual Studio 2008, there are links to additional tools. The link to Silverlight 2 SDK Beta 1 is unnecessary since it already included above. Here are the useful links:

Silverlight 2 SDK Beta 1 Documentation

Source Code and Unit Tests for Silverlight 2 Beta 1 Controls

Microsoft Expression Studio 2 Beta

One of the awesome features of Silverlight 2 is the ability to pan and zoom into a single image or a collage of images. There is a tool available to create the image tiles that assist this process. See:

Deep Zoom Composer

In the next blog, I will post about sample Deep Zoom code I downloaded from Microsoft and about related sites. Nevertheless, I wanted to collect the link to the tool here.

Deep Zoom is an instance of the Sea Dragon project. See:

Sea Dragon

Monday, February 25, 2008       6:14:39 PM

The Concentration Game and its Creation

I have posted a version of the Concentration game that is based on HTML, CSS, and Javascipt together with the use of a simple permutation web service. Here are the critical links.

The Concentration Discussion is an extended blog on the creation of the Concentration game so there is no point in detailed comments in this general blog entry.

Wednesday, February 13, 2008       11:05:02 PM

LINQ & The Northwind Database

I have been building three sites related to LINQ and the sample Northwind database that is provided by Microsoft in SQL Server and which is read-only by every user of our server. The sites may be reached from my home page using the site map and clicking on the title above. The three sites and their purposes are:

  • Scott Guthrie's Blogs on LINQ
    This page collects 20 links to blogs by Scott Guthrie on the C# language features that support LINQ, on making a LINQ to SQL connection, and on interesting related topics. I have found having these links in one place to be very helpful.
  • The Northwind Tables
    This sub-site provides links to the 11 tables in Northwind that are actually populated with data. If you plan to use Northwind to make experimental LINQ queries, it can be helpful to see the full data tables to verify the validity of the queries.
  • LINQ Queries on the Northwind Database
    This site shows examples of LINQ to SQL with Northwind as the target database. The examples are based on ideas in the Scott Guthrie blogs but I have taken liberties to do variations.

Let me discuss LINQ Queries on the Northwind Database further. At present, there are 6 examples posted.

Example 1: Northwind Products: Technique #1

Example 2: Northwind Products: Technique #2

Example 3: Northwind Products In A Category: Technique #1

Example 4: Northwind Products In A Category: Technique #2

Example 5: Northwind Products By Category

Example 6: Northwind Orders By Category and By Product

Examples 1 and 2 do the same task, namely, provide an alphabetical list of products in Northwind. Example 2 is more efficient since it queries only the product names and not the full table of products. The CSS uses:

  • List items <li> which styled as inline so that the items do not use bullets and do not necessarily move to a new line for each item.
  • The style float:left that places as many <li> items as will fit on one line given the current browser window width.

Examples 3 and 4 provide a dropdown list of alphabetized category names and then for the selected category the corresponding products are alphabetized. The two techniques are similar in efficiency but use slightly different approaches.

Example 5 provides an alphabetized list of category names and under each name is an alphabetized list of product names.

Example 6 provides a complex example of cross-table manipulation. There is an alphabetized list of category names. Under each category name, there is an alphabetized list of product names restricted to those products for which an order exists in the database. Under each such product name, there is a long list ordered by company name of those companies that have placed an order that is unfilled. This list includes the order ID, the number of products ordered, and the revenue generated taking into account the price and any discount.

These examples all use ListView objects which are new in ASP.NET 3.5. These widgets are described in the Scott Guthrie blog Building a Product Listing Page with Clean CSS UI. I used this widget to learn how it works while also learning LINQ.

Many examples in Guthrie's blogs use the older and very powerful table widget GridView. I strongly recommend that you look at his code. Also, the Browsers example mentioned in the previous blog uses a ListView for constructing the user input GUI and a GridView for presenting the results of the updated Browsers table.