Scott Hanselman

Hanselminutes Podcast 103 - Quetzal Bradley on Testing after Unit Tests and the Myth of Code Coverage

March 8, '08 Comments [3] Posted in Podcast
Sponsored By

oopsMy one-hundred-and-third podcast is up. On the recommendation of Chris Sells, I gave Quetzal (ket-zal) Bradley a call to talk about Code Coverage. Quetzal is a Developer in the Connected Systems Division and has some interesting ideas on testing after unit testing and code coverage. Think 100% Code Coverage is enough?

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

If you have trouble downloading, or your download is slow, do try the torrent with µtorrent or another BitTorrent Downloader.

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show.

Check out their UI Suite of controls for ASP.NET. It's very hardcore stuff. One of the things I appreciate about Telerik is their commitment to completeness. For example, they have a page about their Right-to-Left support while some vendors have zero support, or don't bother testing. They also are committed to XHTML compliance and publish their roadmap. It's nice when your controls vendor is very transparent.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

ASP.NET MVC Session at Mix08, TDD and MvcMockHelpers

March 8, '08 Comments [38] Posted in ASP.NET | ASP.NET MVC | Mix | Programming | Source Code | Speaking
Sponsored By

NOTE: This post is based on an older preview version of ASP.NET MVC and details have very likely CHANGED. Go to http://www.asp.net/mvc or http://www.asp.net/forums for updated details and the latest version of ASP.NET MVC.

image All the sessions from Mix are up on http://sessions.visitmix.com/ for your viewing pleasure. I had a total blast giving the ASP.NET MVC Talk. The energy was really good and the crowd (of around 600, I hear) was really cool.

You can download the MVC talk in these formats:

I think the sound is a little quiet, so I had to turn it up some. It's better turned up a bit so you can hear the interaction with the crowd.

Here's some of the code from the talk you might be interested in. I'll post the rest very soon.

MvcMockHelpers

The first are the MVCMockHelpers used in the Test Driven Development part of the talk, and also in the 4th ASP.NET MVC Testing Video up at www.asp.net/mvc.

NOTE AND DISCLAIMER: This is just a little chunks of helper methods, and I happened to use Rhino Mocks, an Open Source Mocking Framework, the talk at Mix. At my last company I introduced TypeMock and we bought it and lately I've been digging on Moq also. I'm not qualified yet to have a dogmatic opinion about which one is better, because they all do similar things. Use the one that makes you happy. I hope to see folks (that's YOU Dear Reader) repost and rewrite these helpers (and better, more complete ones) using all the different mocking tools. Don't consider this to be any kind of "stamp of approval" for one mocking framework over another. Cool?

Anyway, here's the mocking stuff I used in the demo. This is similar to the stuff PhilHa did last year but slightly more complete. Still, this is just the beginning. We'll be hopefully releasing ASP.NET MVC bits on CodePlex maybe monthly. The goal is to release early and often. Eilon and the team have a lot more planned around testing, so remember, this is Preview 2 not Preview 18.

MvcMockHelpers - RhinoMocks

using System;
using System.Web;
using Rhino.Mocks;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections.Specialized;
using System.Web.Mvc;
using System.Web.Routing;

namespace UnitTests
{
    public static class MvcMockHelpers
    {
        public static HttpContextBase FakeHttpContext(this MockRepository mocks)
        {
            HttpContextBase context = mocks.PartialMock<httpcontextbase>();
            HttpRequestBase request = mocks.PartialMock<httprequestbase>();
            HttpResponseBase response = mocks.PartialMock<httpresponsebase>();
            HttpSessionStateBase session = mocks.PartialMock<httpsessionstatebase>();
            HttpServerUtilityBase server = mocks.PartialMock<httpserverutilitybase>();

            SetupResult.For(context.Request).Return(request);
            SetupResult.For(context.Response).Return(response);
            SetupResult.For(context.Session).Return(session);
            SetupResult.For(context.Server).Return(server);

            mocks.Replay(context);
            return context;
        }

        public static HttpContextBase FakeHttpContext(this MockRepository mocks, string url)
        {
            HttpContextBase context = FakeHttpContext(mocks);
            context.Request.SetupRequestUrl(url);
            return context;
        }

        public static void SetFakeControllerContext(this MockRepository mocks, Controller controller)
        {
            var httpContext = mocks.FakeHttpContext();
            ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
            controller.ControllerContext = context;
        }

        static string GetUrlFileName(string url)
        {
            if (url.Contains("?"))
                return url.Substring(0, url.IndexOf("?"));
            else
                return url;
        }

        static NameValueCollection GetQueryStringParameters(string url)
        {
            if (url.Contains("?"))
            {
                NameValueCollection parameters = new NameValueCollection();

                string[] parts = url.Split("?".ToCharArray());
                string[] keys = parts[1].Split("&".ToCharArray());

                foreach (string key in keys)
                {
                    string[] part = key.Split("=".ToCharArray());
                    parameters.Add(part[0], part[1]);
                }

                return parameters;
            }
            else
            {
                return null;
            }
        }

        public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
        {
            SetupResult.For(request.HttpMethod).Return(httpMethod);
        }

        public static void SetupRequestUrl(this HttpRequestBase request, string url)
        {
            if (url == null)
                throw new ArgumentNullException("url");

            if (!url.StartsWith("~/"))
                throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");

            SetupResult.For(request.QueryString).Return(GetQueryStringParameters(url));
            SetupResult.For(request.AppRelativeCurrentExecutionFilePath).Return(GetUrlFileName(url));
            SetupResult.For(request.PathInfo).Return(string.Empty);
        }
       
    }
}

MvcMockHelpers - Moq

Here's the same thing in Moq. Muchas gracias, Kzu.

using System;
using System.Web;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections.Specialized;
using System.Web.Mvc;
using System.Web.Routing;
using Moq;

namespace UnitTests
{
	public static class MvcMockHelpers
	{
		public static HttpContextBase FakeHttpContext()
		{
			var context = new Mock<httpcontextbase>();
			var request = new Mock<httprequestbase>();
			var response = new Mock<httpresponsebase>();
			var session = new Mock<httpsessionstatebase>();
			var server = new Mock<httpserverutilitybase>();

			context.Expect(ctx => ctx.Request).Returns(request.Object);
			context.Expect(ctx => ctx.Response).Returns(response.Object);
			context.Expect(ctx => ctx.Session).Returns(session.Object);
			context.Expect(ctx => ctx.Server).Returns(server.Object);

			return context.Object;
		}

		public static HttpContextBase FakeHttpContext(string url)
		{
			HttpContextBase context = FakeHttpContext();
			context.Request.SetupRequestUrl(url);
			return context;
		} 

		public static void SetFakeControllerContext(this Controller controller)
		{
			var httpContext = FakeHttpContext();
			ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
			controller.ControllerContext = context;
		}

		static string GetUrlFileName(string url)
		{
			if (url.Contains("?"))
				return url.Substring(0, url.IndexOf("?"));
			else
				return url;
		}

		static NameValueCollection GetQueryStringParameters(string url)
		{
			if (url.Contains("?"))
			{
				NameValueCollection parameters = new NameValueCollection();

				string[] parts = url.Split("?".ToCharArray());
				string[] keys = parts[1].Split("&".ToCharArray());

				foreach (string key in keys)
				{
					string[] part = key.Split("=".ToCharArray());
					parameters.Add(part[0], part[1]);
				}

				return parameters;
			}
			else
			{
				return null;
			}
		}

		public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
		{
			Mock.Get(request)
				.Expect(req => req.HttpMethod)
				.Returns(httpMethod);
		}

		public static void SetupRequestUrl(this HttpRequestBase request, string url)
		{
			if (url == null)
				throw new ArgumentNullException("url");

			if (!url.StartsWith("~/"))
				throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");

			var mock = Mock.Get(request);

			mock.Expect(req => req.QueryString)
				.Returns(GetQueryStringParameters(url));
			mock.Expect(req => req.AppRelativeCurrentExecutionFilePath)
				.Returns(GetUrlFileName(url));
			mock.Expect(req => req.PathInfo)
				.Returns(string.Empty);
		}
	}
}

Maybe RoyO will do the same thing in TypeMock in the next few hours and I'll copy/paste it here. ;)

MvcMockHelpers - TypeMock

Thanks to Roy at TypeMock.

using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using TypeMock;

namespace Typemock.Mvc
{
    static class MvcMockHelpers
    {
        public static void SetFakeContextOn(Controller controller)
        {
            HttpContextBase context = MvcMockHelpers.FakeHttpContext();
            controller.ControllerContext = new ControllerContext(new RequestContext(context, new RouteData()), controller);
        }

        public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
        {
            
            using (var r = new RecordExpectations())
            {
                r.ExpectAndReturn(request.HttpMethod, httpMethod);
            }
        }

        public static void SetupRequestUrl(this HttpRequestBase request, string url)
        {
            if (url == null)
                throw new ArgumentNullException("url");

            if (!url.StartsWith("~/"))
                throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");
            var parameters = GetQueryStringParameters(url);
            var fileName = GetUrlFileName(url);
            using (var r = new RecordExpectations())
            {
                r.ExpectAndReturn(request.QueryString, parameters);
                r.ExpectAndReturn(request.AppRelativeCurrentExecutionFilePath, fileName);
                r.ExpectAndReturn(request.PathInfo, string.Empty);
            }
        }

        static string GetUrlFileName(string url)
        {
            if (url.Contains("?"))
                return url.Substring(0, url.IndexOf("?"));
            else
                return url;
        }
        static NameValueCollection GetQueryStringParameters(string url)
        {
            if (url.Contains("?"))
            {
                NameValueCollection parameters = new NameValueCollection();

                string[] parts = url.Split("?".ToCharArray());
                string[] keys = parts[1].Split("&".ToCharArray());

                foreach (string key in keys)
                {
                    string[] part = key.Split("=".ToCharArray());
                    parameters.Add(part[0], part[1]);
                }

                return parameters;
            }
            else
            {
                return null;
            }
        }
        public static HttpContextBase FakeHttpContext(string url)
        {
            HttpContextBase context = FakeHttpContext();
            context.Request.SetupRequestUrl(url);
            return context;
        }
        public static HttpContextBase FakeHttpContext()
        {
            HttpContextBase context = MockManager.MockObject<HttpContextBase>().Object;
            HttpRequestBase request = MockManager.MockObject<HttpRequestBase>().Object;
            HttpResponseBase response = MockManager.MockObject<HttpResponseBase>().Object;
            HttpSessionStateBase sessionState = MockManager.MockObject<HttpSessionStateBase>().Object;
            HttpServerUtilityBase serverUtility = MockManager.MockObject<HttpServerUtilityBase>().Object;
            using (var r = new RecordExpectations())
            {
                r.DefaultBehavior.RepeatAlways();
                r.ExpectAndReturn(context.Response, response);
                r.ExpectAndReturn(context.Request, request);
                r.ExpectAndReturn(context.Session, sessionState);
                r.ExpectAndReturn(context.Server, serverUtility);
            }
            return context;
        }

    }
}
Technorati Tags: ,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

The Weekly Source Code 18 - Deep Zoom (Seadragon) Silverlight 2 MultiScaleImage Mouse Wheel Zooming and Panning Edition

March 7, '08 Comments [25] Posted in Mix | Silverlight | Source Code
Sponsored By

Silverlight Project Test Page - Windows Internet Explorer (4)Dear Reader, I present to you eighteenth in a infinite number of posts of "The Weekly Source Code." Here's some source I was reading - and writing - this week at Mix.

I have been checking out Deep Zoom in Silverlight 2, but I thought it was a bummer that there wasn't (yet) a "Hello DeepZoom World!" example that includes panning, zooming (mouse wheel) support out of the box. The Vertigo example behaves exactly as I'd want my stuff to behave. You can see my working Deep Zoom example here or by clicking the picture at right.

Adding Mouse Wheel support to Silverlight can happen a couple of ways. Mouse Wheel events are sourced by the browser, not by Silverlight itself (which is the way you'd want it as Silverlight lives inside the browser, it shouldn't replace its behaviors, IMHO).

So, you could use the Javascript code from Adomas along with the work that Jeff Prosise did to reach into Silverlight and call methods. The events would be handled in JavaScript and the Zoom method would be called via JavaScript over the bridge into Silverlight managed code.

However, you can also reach out from inside managed code and set managed handlers for DOM (JavaScript events) like Pete Blois does with his Mouse Wheel Helper class. I use this class directly by downloading it from Pete's blog and adding it to my project. This is significant because it doesn't require ANY external JavaScript files. All the events are handled by managed code.

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

I took this along with snippets from Yasser Makram and John posting in Yasser's blog's comments got me mostly what I needed.

I've seen some basic examples using either mouse clicking or key-downs to get the zooming effect, but I wanted to support mouse wheel events as well, just like the stuff shown off at Mix.

This more complete example gives you:

  • Drag to pan
  • Click to zoom in, Shift Click to zoom out
  • Mouse wheel to zoom in and out
  • No JavaScript dependency at all - everything is in managed code.

First, start with the output of the DeepZoom composer (I just used the Windows Wallpapers I had on this machine to compose a DeepZoom image in the editor) and copy the resulting exported folder structure somewhere (I put it under bin/debug for ease, but you can put it wherever as long as the source attribute lines up in your XAML:

<UserControl
	xmlns="http://schemas.microsoft.com/client/2007"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	x:Class="SilverlightApplication1.Page"
	Width="800" Height="600" >
  <Grid
    x:Name="LayoutRoot"
    Background="Gray">
    <MultiScaleImage 
      x:Name="msi"
      ViewportWidth="1.0"
      Source="http://www.yourdomain.com/foo/items.bin" />
  </Grid>
</UserControl>

RANDOM NOTE: Here's a cool switch you can set on MultiScaleImage. It's UseSprings="false" and it'll turn off the zooming animation. Why would you want to do this?  Well, that very zoom-in/zoom-out animation gives DeepZoom an opportunity to perform its "visual slight of hand" and transition between images. When the animation happens, you are less likely to notice the transition between tiles (a good thing). Of course, I want to get my head around how it all works so I liked seeing the transitions.

Keep in mind it's four in the morning, so this code is a little wonky and not at all thought-out and I've only been at it for an hour. I'm REALLY interested in what you, Dear Reader, can do with it and make it better so we all have a canonical example to start from. This is NOT that example, I'm actually kind of reticent to post it here because it's so hacked together, but that's half the fun, right? It works OK, I think.

One thing to point out, note that the name of the control is "msi," set in the XAML above via x:name="msi" so you'll see me referencing properties like msi.thisandthat in the managed XAML code-behind below. Also, the "using akadia" below is Pete's MouseHandler code's namespace referenced from my page.

My code hooks up a bunch of events from the constructor using anonymous delegates, and those work together to call a single Zoom() helper method.

using System; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Documents; 
using System.Windows.Ink; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Animation; 
using System.Windows.Shapes; 
using System.Windows.Threading; 
using akadia; 

namespace SilverlightApplication1 
{ 
    public partial class Page : UserControl 
    { 
        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() 
        { 
            this.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; //back out when shift is down 
                    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); 
        } 
    } 
}

Three One thing I am having trouble with, but I haven't run this under a debugger yet as I haven't installed the single Silverlight 2 Beta 1 Tools Installer (Troubleshooting Silverlight 2 Beta 1 Tools Installer). I just built it in Expression Blend 2.5 and Notepad2. I was enjoying myself so much I didn't want to stop and install anything. ;)

  • One, if you scroll WHILE the "spring zoom" animation is happening, something goes wrong and you'll continue zooming in, no matter what direction you're scrolling.
  • Second, more subtlety, if you scroll in, stop, then start scrolling out, it'll scroll in a step, then start scrolling out, so there's clearly a state issue I'm goofing up. Stated another way, if a managed event comes in WHILE the animation is happening, the direction it's currently zooming just keeps going. (Maybe a subtle bug, or my code needs to be debounced because it seems like the mouse wheel events are coming in too fast.) In my example, you have to let the animation "settle down" in order to zoom the other way. Of course, if you just F5 to Refresh you can get back to home and reset the zoom.
  • I figured it out, I was using a relative calculated ZoomFactor when I should have used an absolute factor.
  • Third, I want to prevent the image from panning outside the viewable area (meaning, I don't want folks to get lost) and I'd like to stop the zooming in and out at some reasonable max/min.
  • Don't forget to setup .xap files as the mime type application/x-silverlight-app on on your hoster's IIS instance.

Very cool and fairly easy considering all that's going on. I'll look into other ways this can be exploited in the morning.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Deep Zoom (Seadragon) Silverlight 2 MultiScaleImages at Mix

March 6, '08 Comments [22] Posted in Mix | Silverlight
Sponsored By

outrun One of the cool things seen at Mix 08 today was the Deep Zoom technology. This was originally called Seadragon and came out of the Live Labs research.

You can see it live at the Hard Rock Cafe Memorabilia site. This Silverlight application includes a tiled montage of 512 many-megapixel images, some 10+megapixels, while some are stitched together to sizes upwards of 40-50 megapixels, it seems.

Remember the first 3D games like Sega's OutRun? They seemed 3D, but basically bitmaps were being scaled as you "drove forward" and each graphical asset, like a house, for example, would have a number of pre-rendered versions. Another example are .ico files that have 16x16, 32x32, etc, versions. As the viewer there is one image, but there's multiple resolutions depending on how far in you want to zoom. This is the basic idea behind "multi-scale images."

You can see examples of exceedingly large images, often called "giga-pixel" images all over the web. A great example of zooming using Flash is at Harlem 13-Gigapixels.com. The large Harlem image is 2,045 individual images of 12 megapixels each. The rendering when zooming in is a lot like Google Earth. You zoom into an image of a specific resolution then that first image transitions into a tile of a second image rendered another resolution. You can create these kinds of images with Panorama stitching software like AutoPano.net, and PanoramaFactory. I took a large 50+ megapixel panorama of Ngorongoro Crater in Tanzania that I think would make a good example for this kind of thing. I'll dig it up and make a Deep Zoom example project.

DeepZoom includes an optimized local Silverlight Renderer and XAML control that makes creating these fairly easy. Try it out over at the HardRock Memorabilia site and take specific notice of this guitar I've circled in the picture below.

Hardrock Memorabilia

Zoom in...

image

And more...into the screw knob in the lower left corner...

image

You can actually see a reflection of the bench and the camera and Scott Stanfield from Vertigo who's taking the shot of this guitar, reflected in the shiny knob. Vertigo wrote the Hardrock site and they also have a great Deep Zoom Gallery.

Project Name

You can go download the Preview of the Deep Zoom Composer now. You can see its heritage when you make a new Project and it puts it in a folder called "Seadragon Projects." Here's the Deep Zoom Composer User Guide.

You place a number of pictures (or a large one) on the surface and export the collection. There will be a scene graph XML file created indicating location, size, z-order and aspect ratio of the images.

SparseImageSceneGraph.xml - Notepad

Then an output directory will be created with folders for each image and, depending on the relative largeness of the source images, a series of tiles generated a number of zoom levels. Here's an example directory structure:

11

Silverlight Project Test Page - Windows Internet Explorer (2)See how that image at zoom level 11 has 20 different tiles? They've got to balance many factors to make it all seamless. They're considering how many tiles are needed for that resolution as it relates to a zoom level's need for memory, as well as considering a reasonable number of requests to the server. There's the extreme need for a good user-experience so you want the images to come fast and furious, be easily cached and quickly shown to give the perception of speed. You don't have to sweat any of this stuff, as the multi-resolution tiles stitched together automatically by the client rendered.

After you've exported a project into these files, you create a MultiScale image in XAML like this, referencing a bin file manifest that knows contains info about the directory structure and files:

<MultiScaleImage x:Name="deepZoomObject" Source="foo/info.bin"/>

This gets you the image rendered. To zoom a MultiScaleImage, you call ZoomAboutLogicalPoint:

public void ZoomAboutLogicalPoint(double zoomIncrementFactor, double zoomCenterLogicalX, double zoomCenterLogicalY)

But there's a decent amount of state management that needs to be done as well as deciding how you want things animated, whether or not your image contains sub-images, etc. In the morning I'll take a look at how to hook up JavaScript events like Mouse Wheel Scrolls and do some zooming and panning. Hopefully Vertigo will release their hard-won source code for the canonical example to the community. It's pretty slick.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

ASP.NET MVC Preview 2 Screencast Tutorials

March 5, '08 Comments [44] Posted in ASP.NET | ASP.NET MVC | Learning .NET | Microsoft | Mix | Screencasts
Sponsored By

ASP.NET MVC Preview 2 released today and you can head over to http://www.asp.net/mvc to get the new stuff. I put together four new screencasts for this release.

I really feel that a well-done screencast is a great learning tool. Last month I did a survey of you all, Dear Reader, and got 1000 responses and published the results. I took all the comments, stats and general feedback and tried to improve these screencasts. As a result, I am more judicious in my use of PIP (Picture in Picture) and the screencasts are shorter and more to the point.

If you watched the original MVC Screencast, some of the content in videos #1 and #2 will be repeated as it's been updated for Preview 2, but I hope it doesn't waste your time. However, there's new things sprinkled around and a number of changes are called out. Additionally, videos 3 and 4 are completely new content. Video #4 talks about how to use a Mocking Framework to to TDD with MVC and #3 touches on a number of advanced techniques that you can practice at the Mix HOL (Hands-On-Labs).


#1 | MVC Preview - Part 1 - Displaying Data

16 minutes, 41 seconds

#2 | MVC Preview - Part 2 - Editing Data and Making Forms

18 minutes, 38 seconds

#3 | MVC Preview - Part 3 - Advanced Techniques

12 minutes, 56 seconds


#4 | MVC Preview - Part 4 - Testing

23 minutes, 20 seconds

UPDATE: There's only a template for MSTest in the new File | New MVC Project wizard right now because we're reaching out to the big Unit Testing libraries to get them to help create templates. If you have a Unit Testing Library, you can get into the MVC File | New Project dialog by packaging up your templates like this. I'd guess/hope that the installers for the major libraries would just include their templates in their next versions.

imageI'll work with Phil to get all the source to the apps in these videos posted soon. I hope you enjoy them as much as I enjoyed making them.

Note that while the player on the http://www.asp.net site is Silverlight, you can download versions on the same page in WMV format, as well as ones customized for the Zune, iPod, PSP, and MP4 and 3GP.

One other important new feature on the ASP.NET site is that Videos now accept comments and those comments include RSS feeds. Do feel free to leave comments on this blog, but you can also comment on videos and screencast tutorials all over the ASP.NET site. We all read these comments (including my boss) so your feedback IS paid attention to.

Technorati Tags: ,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.