Scott Hanselman

Learning WPF with BabySmash - MVC or MVP and the Benefits of a Designer

July 11, 2008 Comment on this post [14] Posted in BabySmash | Windows Client | WPF
Sponsored By

NOTE: If you haven't read the first post in this series, I would encourage you do to that first, or check out the BabySmash category. Also check out http://windowsclient.net/ for more developer info on WPF.

BACKGROUND: This is one of a series of posts on learning WPF. I wrote an application for my 2 year old using WPF, but as I'm a Win32-minded programmer, my working app is full of Win32-isms. It's not a good example of a WPF application even though it uses the technology. I'm calling on community (that's you, Dear Reader) to blog about your solutions to different (horrible) selections of my code. You can get the code http://www.codeplex.com/babysmash. Post your solutions on your blog, in the comments, or in the Issue Tracker and we'll all learn WPF together. Pick a single line, a section, or subsystem or the whole app!

The sheer breadth and depth of WPF is really overwhelming some times. I find myself finding three or four different ways to accomplish basically the same thing and not knowing which one to choose. One wants to balance maintainability and a sense of code aesthetic (Code Smell) as well as focusing on extensibility. All this combined with my own "hacker's impatience" for new features has made messing with BabySmash! a very interesting exercise.

Last week I did a podcast with Felix Corke and Richard Griffin from Conchango on the Developer/Designer relationship and how they navigate their way through the development process and the tools that enable them or hamper their progress.

So far BabySmash has been just basic shapes drawn by me with some help from you, Dear Reader. And let me just say, few of you are designers from the looks of the XAML you've been sending me. ;)

Here's BabySmash before Felix got to the XAML and before I refactored it to support working with him.

image

Here it is now (and it's got animations and interactions, but you'll have to run it to see):

babysmashv2

There's also a bunch of new features that have been made possible by refactoring the project to an MVP (Model-View-Presenter) pattern. But let's start at the beginning.

The Benefits of a Designer and Separated Concerns

I opened my email yesterday and discovered a file sent by Flex called "allshapes1.xaml."

ASIDE: I'm starting to realize that just like regular non-WPF .NET folks can't do everything in Visual Studio, this is especially true when coding in WPF. I'm regularly bouncing between these tools:

I busted out KaXAML and was greeted with this!

default.xaml - Kaxaml

imageAfter I finished crying and wallowing in self pity over my own lack of design skills, I set to figuring out how to exploit benefit from Felix's abilities. When I showed Felix and Richard my initial version of BabySmash last month in Olso, I was using Figures and Shapes and Geometries as my Units of Work. They both suggested I consider UserControls as a better way to work, especially when a Designer gets involved. I mostly ignored them, as I was getting along OK.

However, when Felix's art showed up, it clicked for me. I took each of his shapes and made them UserControls, then modified my factory (FigureGenerator) to make UserControls instead.

UserControls as Unit of Work

The UserControls are self-contained, easy to make from Blend, and as soon as I made one Visual Studio prompted me to reload the project (as Blend and Visual Studio share the SAME csproj/vbproj/sln files) and there it was.

The UserControls compartmentalized everything (duh) nicely so I could start thinking about animations without messing up my business logic or cluttering up my (already cluttered) code.

Adding Animation

Felix drew faces on the shapes, so I asked if he'd include animation. Minutes later he had markup in the XAML to make the eyes blink. Animation in WPF is declarative and time-based (not frame-based). He inserted some key frames and set the animation to repeat forever. Now, the shapes blink occasionally and I didn't have to write any code or worry about threading.

Even better, when I do another animations from code, his animations continue! This means, the shape's faces blink, the shapes fade away after a way, and if you click on them they'll jiggle. Three different animations done in different ways, all happening simultaneously.

Take the Square for example. You start with the basic shape. Notice is has an "x:Name." We can refer to anything with a name later, either in Code or XAML.

<Rectangle x:Name="Body" StrokeThickness="10" Stroke="#ff000000" Width="207" Height="207">
</Rectangle>

Then, he gives it a nice radial fill. Note that he's doing all this work in Blend. I find the XAML building up interesting, myself.

<Rectangle x:Name="Body" StrokeThickness="10" Stroke="#ff000000" Width="207" Height="207">
<Rectangle.Fill>
<RadialGradientBrush MappingMode="Absolute" GradientOrigin="110.185547,455" Center="110.185547,455" RadiusX="98.5" RadiusY="98.5">
<RadialGradientBrush.Transform>
<MatrixTransform Matrix="1,0,-0,-1,-6.685547,558.5" />
</RadialGradientBrush.Transform>
<GradientStop Offset="0" Color="#ffff00ff"/>
<GradientStop Offset="1" Color="#ff9d005c"/>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>

Then he added a face. Note the the Face and Eyes are named.

<Canvas x:Name="Face">
<Canvas x:Name="Eyes">
<Path Fill="#ff000000" Data="...snip..."/>
<Path Fill="#ff000000" Data="...snip..."/>
</Canvas>
<Path Fill="#ff000000" Data="...snip..."/>
</Canvas>

Then he makes the animation a Resource, and sets a Trigger that starts the animation when we're loaded:

<UserControl.Resources>
<Storyboard x:Key="Eyes">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="Eyes"
Storyboard.TargetProperty="(UIElement.Opacity)"
RepeatBehavior="Forever">
<SplineDoubleKeyFrame KeyTime="00:00:02.1000000" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:02.1000000" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:02.300000" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:02.300000" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:10.300000" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource Eyes}"/>
</EventTrigger>
</UserControl.Triggers>

This background animation all lives in the XAML and works without my code having to do anything.

I wanted an interaction animation. I could have probably done it in XAML with a trigger, but in code it was just this. I took the animations from the WPF AnimationBehaviors project on CodePlex but did them with code rather than in XAML so they can apply to any UserControl including ones that I haven't added yet.

void AllControls_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UserControl f = sender as UserControl;
if (f != null && f.Opacity > 0.1) //can it be seen?
{
Animation.ApplyRandomAnimationEffect(f, Duration.Automatic);
PlayLaughter();
}
}

Still there are a half-dozen different ways to do things, so I'm still trying to find balance. I can see one going too far left or right and doing everything in XAML or Code even when it's uncomfortable. BabySmash is a hybrid until someone can help me understand better when to do one over the other.

MVP and MultiMonitor

Both Ian Griffiths and Karl Shifflett said I needed to refactor things such that I wasn't putting all the logic in the MainWindow's code behind file. They said that WPF lends itself to an MVP (Model-View-Presenter) pattern, even if you're not really strict about it.

I realized I was going to need to do this soon as my first attempt at Multi-Monitor sucked so bad I ended up yanking it. Initial revisions of BabySmash! had a MainWindow class and all the logic in the code-behind, just like I would have in a WinForms application. The application start up and the Application class would spin through all the Monitors making a window for each one. This had a number of problems:

  • The shapes would only appear on the monitor whose Window had focus. You had to change focus with the mouse.
  • You could close a Window or two on secondary monitors without closing all of them.

Now, there's a single Controller class that manages as many Windows as it needs to. The app starts up like this:

private void Application_Startup(object sender, StartupEventArgs e)
{
Controller c = new Controller();
c.Launch();
}

And Controller.Launch looks like:

public void Launch()
{
foreach (WinForms.Screen s in WinForms.Screen.AllScreens)
{
MainWindow m = new MainWindow(this)
{
WindowStartupLocation = WindowStartupLocation.Manual,
Left = s.WorkingArea.Left,
Top = s.WorkingArea.Top,
Width = s.WorkingArea.Width,
Height = s.WorkingArea.Height,
WindowStyle = WindowStyle.None,
Topmost = true
};
m.Show();
m.WindowState = WindowState.Maximized;
windows.Add(m);
}
}

Pretty simple to start. I should have smelt that something was wrong with the initial plan as I felt like I was "chasing my tail" in code, trying to get things to work in the original pattern. When I switched to this pattern things just became easy.

Now, Why is this Model View Presenter and not Model View Controller (especially considering I called the class Controller)? Well, Phil does a good job answering this:

With MVC, it’s always the controller’s responsibility to handle mouse and keyboard events. With MVP, GUI components themselves initially handle the user’s input, but delegate to the interpretation of that input to the presenter. This has often been called “Twisting the Triad”, which refers to rotating the three elements of the MVC triangle and replacing the “C” with “P” in order to get MVP.

Now I need to go learn more about Supervising Controller and Passive View as Martin Fowler suggested retiring the MVP pattern in favor of those two variants. The code is still in a sloppy stage (up on CodePlex) but I'd love to have someone (Phil?) who is familiar with pure instances of these patterns to help me tidy it up. I didn't take testing into consideration before (mistake) and I need to get back on the Righteous Path otherwise the technical debt is going to crush me. That's what I get for not going TDD from the start.

The MainWindow's code-behind is just a bunch of small methods that delegate off to the Controller. If there are n MainWindows there's still just the single Controller. MainWindow is full of these kinds of things:

protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
e.Handled = true;
controller.ProcessKey(this, e);
}

Every method is less than 20 lines, and most are really simple and boring, which is good.

private void AddFigure(FrameworkElement uie, string s)
{
FigureTemplate template = FigureGenerator.GenerateFigureTemplate(s);
foreach (MainWindow m in this.windows)
{
UserControl f = FigureGenerator.NewUserControlFrom(template);
m.AddUserControl(f);

f.Width = 300;
f.Height = 300;
Canvas.SetLeft(f, Utils.RandomBetweenTwoNumbers(0, Convert.ToInt32(m.ActualWidth - f.Width)));
Canvas.SetTop(f, Utils.RandomBetweenTwoNumbers(0, Convert.ToInt32(m.ActualHeight - f.Height)));

Storyboard storyboard = Animation.CreateDPAnimation(uie, f,
UIElement.OpacityProperty,
new Duration(TimeSpan.FromSeconds(Settings.Default.FadeAfter)));
if (Settings.Default.FadeAway) storyboard.Begin(uie);

IHasFace face = f as IHasFace;
if (face != null)
{
face.FaceVisible = Settings.Default.FacesOnShapes ? Visibility.Visible : Visibility.Hidden;
}
f.MouseLeftButtonDown += HandleMouseLeftButtonDown;
}
FiguresCount++;
PlaySound(template);
}

So far my biggest problems are moving things around, trying to decide "who is responsible for what." Given Animations, Sounds, Shapes, Faces, and all that, where and who is responsible for what, while keeping an eye open for extensibility.

The Little Niceties - Enabling a TextBox if a CheckBox IsChecked

One little aside to end on. Just when I'm getting really pissed at WPF and I'm ready to give up, something simple and cool happens where I realize I'm starting to "get" it.

For example. I've got this Options Dialog that you might remember Jason Kemp refactored. All the controls live inside a Grid and that Grid has a "DataContext" that is my Settings object. All the controls get bound to the object and I don't have to do any loading of values or pulling of values. It just works.

I added that last checkbox and a new option where I wanted to Fade Shapes Away in x seconds. I wanted to disable the TextBox if the Checkbox was not checked. This is the kind of typical operation you might find yourself writing code for in WinForms. You'd hook up events to watch if it's Checked or not, then set the Enabled property of the TextBox, and you also have to watch for the initial load of state. It's not hard in WinForms, but it's irritating, tedious and it's in two places in the code behind.

 Baby Smash! - Options (2)

Even though the DataContext (the thing we are data-binding to) is the Settings object, I can bind objects together by using the ElementName. Check this out. Look at the TextBox's IsEnabled property.

<StackPanel Orientation="Horizontal" 
Grid.Row="6" Grid.ColumnSpan="2" HorizontalAlignment="Stretch">
<CheckBox x:Name="FadeChecked" Margin="15,0,0,0"
IsChecked="{Binding Path=Default.FadeAway,Mode=TwoWay}" >
Fade Shapes Away in</CheckBox>
<TextBox Margin="5,0,0,0"
Text="{Binding Path=Default.FadeAfter}"
IsEnabled="{Binding ElementName=FadeChecked,Path=IsChecked,Mode=TwoWay}"
Height="20" Width="25" />
<TextBlock>secs.</TextBlock>
</StackPanel>

It's a tiny victory, but it made me happy.

Related Links

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Deploying ASP.NET MVC on ASP.NET 2.0

July 08, 2008 Comment on this post [24] Posted in ASP.NET | ASP.NET MVC
Sponsored By

minefieldThis post will be filled with disclaimers and warnings. They are not just to CYA, but also to C my A and avoid my getting sacked.

I've been working with Shawn Burke and Rob Conery and the magical Eilon Lipton to try to figure out a way to get ASP.NET MVC running under ASP.NET 2.0 SP1. Shawn had this idea and told Rob "go figure this out." Rob was really busy doing the MVC Storefront so he told me "go figure this out, you like this kind of freaky stuff."

Take a moment to look at my post on How to set an IIS Application or AppPool to use ASP.NET 3.5 rather than 2.0 for background on the physical relationship between .NET 2.0 and .NET 3.5.  There's a lot of stuff going on between 2.0 and 3.5, even though the CLR is more or less the same.

I was brainstorming ways to get ASP.NET MVC running on a .NET 2.0 machine and there were a couple of ways. I could recompile/fork all the stuff at http://www.codeplex.net/aspnet and compile via #ifdefs or manually an ASP.NET 2.0  version. However, then I'd have to maintain multiple versions, it could get really messy really quick.

I ended up speaking to Brad Abrams and spent the last few weeks, on and off, talking to all the different groups that owned different parts of the framework. I expressed that I could get a Hello World ASP.NET MVC application working if I:

  • Developed on Visual Studio 2008
  • Targeted .NET 2.0 in Project Properties
  • Deployed the Application to a .NET 2.0 SP1 machine
  • Copied System.Core.dll local to the web apps /bin folder

It's that final step that we're not technically allowed to do, but I'm showing you how to do it in case you real;y want to ASP.NET MVC on a machine that you just can't put .NET 3.5 on yet.

Here's the disclaimers:

  1. This workaround is offered with exactly zero warranty or support. It's as-is, just an FYI on the blog. If this hack deletes files or kills your cat, you have been warned. No whining.
  2. In practice, no one really knows what might break.  Microsoft didn't test this.
  3. You must not redistribute System.Core.dll to a 3rd party. You mustn't bundle it or ship it with your application.
  4. When you update to .NET 3.5, remove your local copy of System.Core.dll
  5. Don't GAC System.Core.dll.
  6. Don't taunt System.Core.dll.
  7. Take a moment and read #3 again.
  8. This just flat might not work for you. Sorry.
  9. Don't do this to any machine that you don't own.
  10. I wanted to have an even ten, so this is a placeholder disclaimer in case I forgot one.

After all those sunny disclaimers, I do hope that this helps someone in some small way to get a development site running on .NET 2.0 while you prepare for an upgrade to .NET 3.5.

What To Do

From an ASP.NET MVC Web Application, you'll need to set your Project Target Framework to 2.0 in Project Properties:

image

NOTE: This doesn't enable all of 3.5, or even much of .NET 3.5. But it does enable System.Web.Mvc and it's supporting assemblies to run as they are the ones with the reference to System.Core. However, remember, at this point, you and your project are living in .NET 2.0 world.

Running 2.0 means you don't have LINQ to SQL or Entity Framework or anything. You'll need to use something else. In my example code I'm using Davy Brion's NHibernate Northwind Sample as it's a .NET 2.0 compiled solution and it'll be acting as my Model.

VS 2008 Will Try to Stop You

When you add references to System.Web.Routing, System.Web.Abstractions and System.Web.MVC you'll get warnings like this one. Click Yes. This is VS trying (rightfully) to protect you from confusion and assembly errors as we'll see, and if you click "yes" you're saying you really know what you're doing. From this point on, we're on our own.

image

After this, you can develop your now .NET 2.0 application as you like, using 2.0 technologies. Some of the cooler ASP.NET MVC stuff won't work, particularly in the Views if you try to use lambdas with HtmlHelpers or use the var keyword. This is because while your development machine's compiler is 3.5 since you're using VS2008, when you deploy your ASPX views to the server side, that machine has only .NET 2.0 SP1 and will compile those views with the 2.0 compilers.

For example, this line of code in a view that showed a list of products...

<% foreach (var product in ViewData.Model) { %>

...gave me this error.

CS0246: The type or namespace name 'var' could not be found (are you missing a using directive or an assembly reference?)

If you see errors like this, you'll know you're using 3.5isms that's not understood by the 2.0 compiler in your Views.

Deploying to an ASP.NET Machine

Since I'll be deploying on a machine with .NET 2.0 SP1, I'll use an XP SP2 Virtual Machine running IIS6. You can find a fantastic troubleshooting blog post by Omar Al Zabir on Deploying ASP.NET MVC on IIS 6 here. Suffice it to say, my routes use the .mvc extension, and I've associated .mvc with aspnet_isapi in the IIS6 management console.

I've also updated my routes to include the .mvc extension so IIS6 can "see" the requests:

routes.MapRoute("mvcroute", "{controller}.mvc/{action}/{id}"
    , new { controller="products", action = "Index", id = "" }
    , new { controller=@"[^\.]*"});

ASIDE: If you like, you can certainly enable pretty URLs using ISAPI_Rewrite and remapping requests to extensionless URLs to .mvc or you can configure IIS6 with a wildcard map. I prefer the former.

image

After I deploy my ASP.NET MVC application over to my .NET 2.0 SP1 machine, again, in this case running IIS6, I might see this YSOD indicating I don't have System.Core on that machine. This makes sense because this machine doesn't have .NET 3.5 installed.

Here's the moment of truth, and the moment we step from supported to unsupported. You can copy System.Core from your .NET 3.5 development machine (this is the machine running VS2008 that you're developing on) to the /bin folder on your .NET 2.0 SP1 machine. It's gotta be running .NET Framework 2.0 SP1 or this won't work.

image

System.Core is probably somewhere around "C:\windows\assembly\GAC_MSIL\System.Core\3.5.0.0__b77a5c561934e089" on your machine, but you're a bad person for even asking.

image

Copy that file into your bin folder on your deployment machine and you should be able to Refresh past this error page.

Things You Don't Get With This Hack

  • Support from Me or Microsoft (in that order)
  • Any .NET 3.5 feature like LINQ to SQL or LINQ to Entities
  • Many of the HtmlHelpers and cool things that work with Lambdas won't work with inline script in Views because those pages will be compiled on the server using the 2.0 compiler.
  • Good karma

Things You Appear to Get With This Hack

  • ASP.NET MVC seems to work
  • Anything you can do in ordinarily in .NET 2.0 works
  • Bragging rights
  • A spot on your immortal soul
  • The ability to show your boss that ASP.NET MVC is a good thing, and maybe get him/her to let you upgrade the server to ASP.NET 3.5 with the promise of even cooler stuff.

I was able to get a full Northwind Sample Site up using ASP.NET MVC Preview 3 with NHibernate against the Northwind Database in about two hours. This was my first NHibernate application of any size and I'm sure I did it wrong, but it works on my machine! Thanks again to Davy Brion's most excellent and thoughtful example, it was a great way for me to learn NHibernate (which has a bit of a learning curve).

image

Navigating Microsoft Legal

One thing that I wanted to add was that this was my first time navigating Microsoft Legal. I was dreading it. However, the LCA (Legal and Corporate Affairs) guy that helped me through it was exceedingly cool. Rather than what I expected - here's reasons why you CAN'T do this - he was more like, "what can we do to make this happen." I don't know if it's representative of Microsoft Legal in general, our division, or just this nice guy, but either way, it was cool and he's my go-to guy the next time I try something crazy.

Related Links

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Hanselminutes Podcast 120 - The Odd Couple - A Developer and a Designer talk about working with XAML

July 07, 2008 Comment on this post [4] Posted in Podcast | Silverlight | Windows Client | WPF
Sponsored By

XAML Icon My one-hundred-and-twentieth podcast is up. In this episode, I talk to Felix, a Designer, and Richard, a Developer about their Odd Couple relationship as they create Silverlight and WPF applications. They also speak frankly about their opinions as full time users of Blend and VS2008.

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.

Telerik's new stuff is pretty sweet, check out the ONLINE DEMO of their new ASP.NET AJAX suite. RadGrid handles sorting, filtering, and paging of hundreds of thousands of records in milliseconds, and the RadEditor loads up to 4 times faster and the navigation controls now support binding to web services on the client.

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Back to Basics - Do namespace using directives affect Assembly Loading?

July 04, 2008 Comment on this post [29] Posted in Back to Basics
Sponsored By

Jeff Atwood had a great post a while back called "Strong Opinions, Weakly Held." It's good to feel strongly about something, but important to be open to changing your opinion if you're faced with new evidence.

Last week a reader pointed me to this post at the Microsoft StyleCop blog that shows some interesting examples of using directives outside and inside the namespace declaration.

For example, this compiles fine:

using Guid = System.Guid;

namespace Microsoft.Sample
{
public class Guid
{
public Guid(string s){}
}

public class Program
{
public static void Main(string[] args)
{
Guid g = new Guid("hello");
}
}

}

However this one with the using moved inside the namespace doesn't compile:

namespace Microsoft.Sample
{
using Guid = System.Guid;

public class Guid
{
public Guid(string s){ }
}

public class Program
{
public static void Main(string[] args)
{
Guid g = new Guid("hello");
}
}
}

The code fails on the following compiler error, found on the line containing Guid g = new Guid("hello");

CS0576: Namespace 'Microsoft.Sample' contains a definition conflicting with alias 'Guid'

In the first example, there's an alias created, but it doesn't matter because the second Guid class is in local scope (there's no scope conflict) and the compiler chooses the inner Guid class.

In the second example, there are two "Guids" declared in the same scope and that's a conflict that the compiler can't resolve automatically. The style rule/argument the post makes is that you will only see these kinds of conflicts if you put your using directives inside your namespaces. To this, I say, "meh." Sure, if it makes you happy and you use lots of namespace aliases, sure, but it's an edge case. I simply prefer to have my namespaces outside.

Read Twice, Test Once

However, the second rule in the post said:

"However, placing the using statements [Ed. Note: They mean "directives"] within a namespace element allows the framework to lazy load the referenced assemblies at runtime. In some cases, if the referencing code is not actually executed, the framework can avoid having to load one or more of the referenced assemblies completely. This follows general best practice rule about lazy loading for performance."

This stopped me in my tracks. This rocks the very bedrock that my knowledge of the CLR stands on. I'm like, NO WAY, and then I oscillated back and forth between denial and acceptance. Then, I settled on denial. I don't buy it. A using directive is for aliasing and is a kind of syntactic sugar. Ultimately the IL is the same. Assembly loading won't be affected as the assembly manifest doesn't change.

Here's what my experiment showed. I believe it's true until I find out from someone on the CLR Loader team that it's not true. ;)

First Test

using System;
using System.Xml;

namespace Microsoft.Sample
{
public class Program
{
public static void Main(string[] args)
{
Guid g = Guid.NewGuid();
Console.WriteLine("Before XML usage");
Console.ReadLine();
Foo();
Console.WriteLine("After XML usage");
Console.ReadLine();
}

public static void Foo()
{
XmlDocument x = new XmlDocument();
}
}
}

I ran this program outside the debugger but compiled in debug mode. At the point there the first ReadLine() hits, the program pauses and waits for an Enter key. I loaded up Process Explorer and saw:

image

Then, I hit Enter, executing the Foo() method and new'ing up an XmlDocument. You can see that System.Xml just got loaded (specifically the native image) into the process.

image

Second Test

If I do the same thing with the usings INSIDE the namespace I get identical results.

namespace Microsoft.Sample
{
using System;
using System.Xml;

public class Program
{
public static void Main(string[] args)
{
Guid g = Guid.NewGuid();
Console.WriteLine("Before XML usage");
Console.ReadLine();
Foo();
Console.WriteLine("After XML usage");
Console.ReadLine();
}

public static void Foo()
{
XmlDocument x = new XmlDocument();
}
}
}

In fact, the only thing that changed the way the assemblies got loaded was switching to release mode. Running the app in release mode had all the assemblies in my trivial app loaded immediately. I thought it was weird for a second, but then realized it had nothing to do with debug vs. release. It was simply that the Foo() method was either inlined or there was a Tail Call Optimization as I explored in this post: Release IS NOT Debug: 64bit Optimizations and C# Method Inlining in Release Build Call Stacks.

I'm 99.99% sure at this point that using directives can't change your assembly loading behavior and I think I was right to be suspicious. However, I'm going to ask some people on the Fusion (assembly loader) and C# teams who are smarter than I and I'll update this post as I learn more!

However, the Back to Basics Tips here are:

  • Don't believe everything you read, even on a Microsoft Blog.
  • Don't believe this blog, either!
  • Decide for yourself with experiments if you need a tiebreaker!

And be ready to be wrong anyway! It only takes one negative experiment to disprove a theory. Of course, the real question is, what does the specification say? 

UPDATE #1: Ian Griffiths had a similar reaction and a similar test!

What do you think, Dear Reader?

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

The Weekly Source Code 30 - VB.NET with XML Literals as a View Engine for ASP.NET MVC

July 03, 2008 Comment on this post [12] Posted in ASP.NET | ASP.NET MVC | VB | XML
Sponsored By

I was literally in the middle of writing the post when I saw a message from Andrew Davey about how he had implemented the same idea! Of course, his is way better, so I got to code via subtraction. That means subtracting out the crap I had written in a few minutes and dig into his code.

There are no unique ideas, right? ;) Either way, it's fun when the same idea is being thought about simultaneously.

Here's the general concept. A few weeks back I was talking with Avner Aharoni, a Language Program Manager, and he had been kicking around the idea of VB9's XML Literals making friendlier Views within ASP.NET MVC.

I've blogged about VB9's rocking sweet XML support before. It lets you create XML like this. Note the lack of strings...the XML is there in the language and the compiler and intellisense are all aware of it.

 Dim books = <bookstore xmlns="http://examples.books.com">
<book publicationdate=<%= publicationdate %> ISBN=<%= isbn %>>
<title>ASP.NET Book</title>
<price><%= price %></price>
<author>
<first-name><%= a.FirstName %></first-name>
<last-name><%= a.LastName %></last-name>
</author>
</book>
</bookstore>

Views in ASP.NET MVC

Starting with the Northwind ASP.NET MVC Sample Code for Preview 3 that Phil updated, let's look at the List View:

<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<h2><%=ViewData.CategoryName %></h2>
<ul>
<% foreach (Product product in ViewData.Model.Products.Model) { %>
<li id="prod<%= product.ProductID %>">
<%= product.ProductName %>
<span class="editlink"">
(<%= Html.ActionLink("Edit", new { Action="Edit", ID=product.ProductID })%>)
</span>
</li>
<% } %>
</ul>
<%= Html.ActionLink("Add New Product", new { Action="New" }) %>
</asp:Content>

This is straight from the ASPX page with inline C#.

ASP.NET MVC Views using VB9's XML Literal Support

imageNow, we thought it'd be cool/interesting/potentially-something if we could use the XML Literal support to get, as Andrew puts it "compiled, strongly typed, intellisense friendly views." Sure, we mostly get that with the ASPX pages, but perhaps this would be better/easier/something? Keep in mind here that we're playing.

Your opinions on if this is a good idea or something to move forward would be useful. Leave comments and I'll compile and give them directly on to the VB and MVC team(s)! Remember, it can look like however you think it should, so don't be constrained by my spike or Andrew's.

Here's why Andrew thinks it's cool, quoted from a post on the ALT.NET mailing list:

Some key features I've used.
- <%= From ... Select ... %> to create repeated elements
- VB's ternary "If" operator for conditional output
- X-linq to post process the HTML before sending it
- choose between indented and compressed XML output
- modules of functions as "controls" - it's so simple :)

Here's what my solution looks like in Visual Studio. See how the ListVB.aspx has no "+" sign. There's no code-behind .cs file there even though this is a C# project. The meat of the View is in another assembly (although you could conceivably do something freaky and get VB and C# to live in the same assembly (be sure to read the comments)).

Actually, the ListVB.aspx file is VB, not C# and refers not to to a code-behind, but another class in another DLL, specifically the VBViews assembly.

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" 
AutoEventWireup="true" Inherits="VBViews.ListVB" Title="Products" %>
<%@ Import Namespace="NorthwindModel" %>
<%@ Import Namespace="System.Collections.Generic" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<h2><%=ViewData.Model.CategoryName%></h2>
<% = GetList() %>
</asp:Content>

Here's the Visual Basic code in the other assembly.

Imports System.Xml.Linq
Imports NorthwindDemo.Models

Partial Public Class ListVB
Inherits System.Web.Mvc.ViewPage

Function GetList() As XElement
Dim c As Category = CType(ViewData.Model, Category)
Return <ul><%= From product In c.Products _
Select _
<li id=<%= "prod" & product.ProductID %>>
<span class="editlink">
<a href=<%= "/Products/Edit/" & product.ProductID %>>
<%= product.ProductName %>
</a>
</span>
</li> %>
</ul>
End Function
End Class

This won't really be clear without some syntax highlighting to make the point, so here it is again, but this time as a screenshot. See now the VB code, XML and <% %> blocks are all together in same line? VB is just generating XElement's which in turn will turn into a string when "rendered" by the ASPX page.

The same VB9 code as before, except this time in color.

Andrew's Take #2 on the VB9 XML Literals as ASP.NET MVC Views

Andrew Davey's NRest project is more than just VB9 Views. It's a REST web framework for ASP.NET using the Nemerle programming language (using the May CTP). You can browser or GET his code with SVN here: http://svn.assembla.com/svn/nrest/trunk/. It's also a nicely laid out solution that uses the Ninject IOC but I'll cover that later. Do check out Andrew's screencast about his NRest project.

His code is a mix of C#, Nemerle and VB. The Website, Tests and Services are in C# and the Ninject modules are in Nemerle, along with the meat of the main NRest project. I think he could have used more of System.MVC, specifically the View Engines, that he did, but I'm still grokking his intent.

He's got a hierarchy in VB with a MainPageBase, Page, in order to achieve a kind of Master Pages:

Public Class MainPageBase(Of TChrome As MainChrome, TContent)
Inherits Page(Of TChrome, TContent)

Public Overrides Function GetHtml() As XElement
Return _
<html>
<head>
<title><%= Chrome.Title %></title>
<link href="/styles/demo.css" type="text/css" rel="Stylesheet"/>
<%= GetHeadContents().Elements() %>
</head>
<body>
<h1><%= Chrome.Title %></h1>
<%= GetBodyContents().Elements() %>
</body>
</html>
End Function

Public Overridable Function GetHeadContents() As XElement
Return <_></_>
End Function

Public Overridable Function GetBodyContents() As XElement
Return <_></_>
End Function

End Class

So a Hello World page in VB would be very simple, just this:

Public Class CustomerPage
Inherits MainPageBase(Of CustomerPageData)

Public Overrides Function GetBodyContents() As XElement
Return _
<_>
<p>Hello <%= Content.FirstName & " " & Content.LastName %></p>
</_>
End Function
End Class

All of this is a work in progress, but it's really cool that we're all trying to push the envelope and not afraid to try crazy stuff in order to make things better. It'll be cool for me to read this post in a year and either say "ew" or "cool!" depending on what direction we all went.

Have you done anything cool or crazy with Views and ViewEngines, Dear Reader?

Related Posts

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

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