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.
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.
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_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_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
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 ....................
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:
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
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.