Saturday, 1 December 2007

Silverlight 2.0

Most developers with an interest in Silverlight will already know that 1.1 is being rebranded 2.0 and that we have more details about what it will include thanks to ScottGu's announcement.

The change of version number makes a lot of sense. The inclusion of the .NET framework is hardly a minor upgrade. It was the announcement of Silverlight 1.1 that got me interested in Silverlight in the first place. I had no great desire to learn how to do everything in JavaScript that I already knew how to do in C#.

It looks like they're planning to add quite a lot more features in the next beta drop. Most importantly, they are providing controls like textboxes, checkboxes, radio buttons. Also we get more than just plain old Canvas for layout - we get grids and stackpanels. There are even progress bars, sliders and a GridView. This is great news. We will be able to focus on the behaviour of our applications rather than writing custom controls.

Even more impressively, it looks like they are bringing a lot more of the core functionality from WPF such as data binding and control templates in. This means that Silverlight could actually become a serious competitor to WPF in its own right.

The networking support has been enhanced, with the most notable improvement from my perspective being the ability to access resources from other domains, removing a rather awkward limitation  from the alpha release.

Finally, Scott hints at even more of the FCL being made available, with a particular improvement to the DOM integration. I'm guessing that all this means that the size of the installer for Silverlight 2.0 will have grown a bit. I want to go on record as saying I don't care. I think its absolutely amazing how much they have crammed into such a small space, but the more they put in there the better as far as I am concerned. 

There are only two disappointments for me. First: apparently we have to wait for this until the new year. Fair enough I suppose, as there is a lot of new stuff coming. But it does mean that if like me you have projects that use controls, then you may want to hold off until the beta is released to save you wasting time re-inventing the wheel.

Second, although they are planning to support Silverlight development in the Express editions, we must wait until next summer. Which is annoying for me, as I like to experiment with Silverlight at home. Still, I can't complain too much, as Silverlight 2.0 isn't even in beta yet, so I suppose I should cut them a bit of slack.

Anyway, credit to whoever at Microsoft thought up Silverlight 1.1/2.0. I think it's one of their best innovations since they came up with the idea of .NET in the first place.

Thursday, 29 November 2007

Productivity and Enthusiasm

During my holidays in my first year at university I did some warehouse work to earn some extra cash. My job involved offloading pallets of food from lorries using hydraulic pallet jacks and depositing them down a ramp from which the fork-lift truck drivers would collect them to stack in the warehouse.

Now when a lorry came in to be offloaded, there was nowhere to hide. Whether I was feeling full of enthusiasm for doing my job or bored witless, I still had to get to work. The net result was that whether my "enthusiasm" (E on the graph below) was low or very high, my "productivity" (P on the graph below) didn't vary that much.

Productivity versus Enthusiasm for manual labour

What's this got to do with software? Well, the following years I started doing programming as a holiday job, and went on to become a full-time computer programmer. But I soon noticed that in the world of software development, my Productivity versus Enthusiasm curve was radically different.

Suppose I come into work feeling tired and the task I have been given to do is not an enjoyable one (e.g. fixing elusive bugs on a legacy product, or adding a pointless feature to please marketing, writing documentation). In that case I find that a day can easily go past where productivity is close to zero.

On the other hand there are days when I am feeling very enthusiastic about my work. Perhaps it is because I am learning and using some new and interesting technology, or perhaps I am refactoring some badly designed class into a really elegant and simple structure. In this case I find that my productivity increases exponentially. As long as I am not interrupted by distractions, in this state I can become super-productive, fixing bugs and churning out new features at an incredible rate.

Productivity versus enthusiasm for software development

Why the difference between the two graphs? Why could I get work done at the warehouse on low enthusiasm days, but not in the world of software development? The answer is simply that to write software requires concentration and concentration requires interest. At the warehouse I could offload lorries while thinking about all kinds of unrelated subjects. But to write code, my mental focus must be entirely on the task at hand.

If this is true, then the way to maximise productivity is to minimise boredom. If we recognise the things that capture our interest and enthusiasm, we can design development strategies that promote productivity.

This is I think part of the genius of the "agile" approach to software development. Running tests manually is boring, but writing automated tests is much more intellectually stimulating. Bug-fixing is boring, but refactoring code to improve its design while you fix a bug gives a real sense of achievement for a developer who takes pride in their work.

I don't expect boredom can ever be completely eliminated, and so there will always be the need to grit your teeth and show some professionalism. But the route to increased productivity in software development is more closely tied to motivation and enthusiasm than many of our managers seem to appreciate.

Wednesday, 28 November 2007

LINQPad

Somehow I missed this one when it was first featured on .NET Kicks, but for those of you who haven't come across it yet, this little utility is highly recommended. LINQPad is a simple LINQ / SQL editing and executing environment and is great for learning LINQ. It also doubles up as a lite version of SQL management studio, allowing you to easily run your LINQ or SQL statements.

What's really great about it is that it comes with a load of example scripts. For those who learn by trying things themselves, this is an invaluable tool. It does for LINQ what XAMLPad does for WPF and SilverlightPad does for Silverlight.

LINQPad

Highly recommended.

Tuesday, 27 November 2007

Time Zones and .NET 3.5

Finally with the new .NET 3.5 framework release, Microsoft has given us the ability to convert to arbitrary time zones, and take into account historic changes to daylight savings rules. Before, this had to be done with interop, and even then you ran into all sorts of problems when trying to convert using time zones whose daylight savings information specified exact dates rather than nth Sunday of the month (e.g. the Israel / Jerusalem Standard time does this).

I did some testing today with the new TimeZoneInfo class, and all seems to be good. It works on both XP and Vista, and seems to be making use of the Dynamic DST registry keys under HKLM\Software\Microsoft\Windows NT\CurrentVersion\Time Zones (based on how many years back in time I can go before it starts getting it wrong).

One quirk compared to the old way I was converting local times back into UTC using the TzSpecificLocalTimeToSystemTime Windows API was that it now seems to guess the other way to before when given an ambiguous local time. A bit odd, but not a big deal.

Another thing I noticed is that the result of TimeZoneInfo.ConvertTimeFromUtc comes back with its DateTimeKind as Unspecified rather than as Local. I suppose this is because although it is a local time, it may not be the local time in the current locale. ConvertTimeToUtc does set the DateTimeKind to Utc as expected though.

On top of the TimeZoneInfo class, Microsoft have also given us a new DateTime structure - DateTimeOffset, which effectively allows you to store a local time with an explicit offset from UTC, giving you the best of both worlds. Switching to this from DateTime actually removes the need for TimeZoneInfo in most cases as you have the ability to get both the original LocalTime and the UTC time out of the same variable.

Monday, 26 November 2007

Unit Testing is Not About Testing

When I started drafting this post, I was under the illusion that I had just stumbled across an amazing discovery, never before articulated by any programmers before me. But over the past few weeks, I have found out that what I am about to say isn't even slightly original. Everyone is saying it. But it took me quite some time to realize, so here it is anyway, for the two people out there who are as slow as me...

The concept of automated unit testing is appealing to most developers, even if the idea of writing all tests before writing code seems a bit over the top. It is certainly attractive to think that I might never have to perform another boring manual test of my software, because I can check it is working correctly simply by launching NUnit (or similar) and running through a suite of tests.

Unautomatable Tests

The trouble is, automated testing only works for a certain subset of classes - those whose outputs are clearly defined and easily measurable. And of course, those are exactly the type of classes I keep finding myself working on.

For example, I spent many months writing a library of custom Windows Forms controls. They had to be tested with keyboard and mouse entry, and at a variety of screen resolutions and font sizes. Unit testing was sadly out of the question...

Assert.LooksReallyCool(button); // not possible

Then there was the audio related code I worked on. Passing audio to the Windows MME APIs and checking that the sound played back correctly without stuttering. Or passing audio through a DSP algorithm to speed it up to double speed and automatically increase the volume in quiet bits.

Assert.DoesntStutter(audio); // not possible    
Assert.SoundsGood(audio); // not possible

I could go on. How does an automated test check that a webpage renders correctly on IE and FireFox (or harder still, on Mac and Linux)?

And here lies the trouble with the documentation for Unit Testing frameworks. It conveniently ignores these awkward cases and gives us an example of a BankAccount class that has a Withdraw method. Classes that add numbers or sort lists are ideal candidates for automated testing.

Why Bother?

The observation that a large proportion of your classes cannot be meaningfully tested with an automated test therefore puts off many developers from writing unit tests at all.

But what if the main benefit of unit testing was not actually testing? Here's my grand (but unoriginal) idea: the main benefit of unit testing is not testing, but design principles.

Design Benefits

Having a unit test for each and every class in your project means that each and every class is used at least more than once. It forces you to design classes that are loosely coupled. It will highlight hidden static dependencies. It will also enforce separation of GUI and business logic.

The trouble with thinking that Unit Testing is about testing is that "untestable" classes won't have unit tests written for them, which in turn means more badly designed classes. In short, if all your unit tests ever do is create instances of classes and call their methods without doing any checking of what they return at all, you will still benefit from greatly improved design.

Friday, 23 November 2007

Pair Programming for Loners

I first heard about pair programming years ago while reading some articles on Extreme Programming. My initial reaction was "That sounds like an interesting idea. It might just work" followed very quickly by "I can think of a few people I would not like to work in a pair with".

The most obvious benefits are:

  • Having someone to bounce ideas off
  • Having someone watch you while you code and make constructive suggestions
  • Watching someone else code and learning from their approach

Obviously if you develop alone, the first two benefits are largely unavailable. The best you can hope for is to get some feedback from people on user forums.

But the third option is perhaps not as out of reach for the lone developer as it may seem. Where can you go to watch other developers at work on real projects and learn from them?

The answer is open source websites like CodePlex or SourceForge. The value of looking at real code (as opposed to sample applications) should not be underestimated.

As I have been slowly learning ASP.NET over the last year I have been frustrated with the numerous options for doing things - different project types, different data access approaches, different theming mechanisms etc etc. Before I could even start a project I was left with a thousand questions about whether I should use Web Site or Web Application, or whether to use DataGrid or GridView, or whether to use custom business objects or SqlDataSource.

So the great thing about downloading some open source projects is that it gives a great chance to see how other people have chosen to do things. The main three I have been looking at to help me with ASP.NET are BlogEngine.NET, SubText and DotNetKicks. Just spending a few hours browsing the code can teach you a lot about how to structure a project, as well as demonstrating various real-world examples of usage of the different approaches to data access.

The developers of each of these projects are all people who are not only experienced ASP.NET projects but clearly have a passion for best-practices in the way they design and structure their code. These are people who I would want to be pair programming with if I could, asking them questions about why they did things certain ways. But for now, I'll have to content myself with silently looking over their shoulders and working out what's going on for myself.

Tuesday, 20 November 2007

Setting up the CodePlex Client

Microsoft's open source project hosting site CodePlex seems to be gaining a lot of momentum. I now have six projects hosted there (NAudio, MIDI File Mapper, MIDI File Splitter, SilverNibbles, Asterisk.NET, Silverlight Audio Player).

For source control, CodePlex offers Visual Studio Team System. The trouble with this is for those who work with the Visual Studio Express SKUs, the source control plugin is not available. The CodePlex development team have promised a server side bridge allowing Subversion clients such as TortoiseSVN to be used, but for the time being, there is a CodePlex source control client that can be used. Here is a brief guide to getting it up and running.

First, you need to download the CodePlex Source Control Client and extract the binaries (cpc.exe and tfs.exe). I saved mine to C:\Program Files\CodePlex Client. You can also take this opportunity to customise the cpc.exe.config file. I like to turn on the gui for status and commit dialogs:

<add key='DefaultToGuiForCommit' value='true'/> 
<add key='DefaultToGuiForStatus' value='true'/>

And I make use of the differencing tool that comes with TortoiseSVN:

<add key='DiffTool' value='C:\Program Files\TortoiseSVN\bin\TortoiseMerge.exe'/> 
<add key='DiffArgs' value='/base:{basepath} /mine:{mypath} /basename:{basename} /minename:{myname}'/> 
<add key='MergeTool' value='C:\Program Files\TortoiseSVN\bin\TortoiseMerge.exe'/>
<add key='MergeArgs' value='/base:{basepath} /mine:{mypath} /theirs:{theirpath} /basename:{basename} /minename:{myname} /theirsname:{theirname} /merged:{mergepath} /mergedname:{mergename}'/>

I then create a folder to keep my CodePlex projects in (e.g. C:\Code\CodePlex). In that folder I put a batch file that sets up my environment:

@set PATH=%PATH%;C:\Windows\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\CodePlex Client

Now I can create a desktop shortcut that opens a command window pointing to that batch file. The target looks like this:

%windir%\system32\cmd.exe /k ""C:\Code\CodePlex\CodePlexDevEnv.bat""

And it should be set to start in C:\Code\CodePlex (or wherever your codeplex projects live).

Once you have done that, working with your codeplex projects is very simple. Create a subfolder, navigate into that subfolder, and perform a checkout on any project. It doesn't even have to be a project you are a member of - you can anonymously get. For example, here's how to get the code for the excellent BlogEngine.NET project.

md blogengine
cd blogengine
cpc checkout blogengine

To update to the latest files, simply navigate to the project folder and type

cpc update

To see whether you have anything that needs checking in, type

cpc status

This brings up a dialog, from which you can launch your differencing tool on files that have changed, and add unversioned files or folders to source control. When you are ready to commit your changes, then the commit command allows you to upload them with a comment.

cpc commit

Anyway, the command line client is definitely worth checking out if you use Codeplex, or even if you are simply interested in taking a look at the source for some projects hosted there.

Monday, 19 November 2007

Visual Studio Beta 2

I wrote a post a while ago criticising Orcas Beta 1 so I suppose its only fair that I write about Beta 2. I have been using it for a few months now, and have found it to be excellent. I have only discovered one crash so far (to do with intellisense, and apparently fixed already for RTM).

The multi-targetting feature is superb, and the WPF designer has improved a lot (you can create a click handler for a button by double-clicking), although I can't seem to find any way of browsing other available events and adding handlers, short of using the intellisense in the XAML file. Some of my other criticisms still hold true - there is no help in setting FontFamily and Ctrl-Click copying of controls doesn't work.

I was able to attend a presentation by Daniel Moth last Thursday, who demoed some of the new features for web development. I am not an expert in ASP.NET, but have been doing a bit of learning in the last year, and the new features look excellent, particularly getting better intellisense (even in Javascript) and help with CSS. These features should be very helpful for me getting up to speed.

Silverlight support is sadly not going to be built-in, although to be fair, Silverlight 1.1 has still not been officially released. Having said that, the Silverlight development story is very good in VS2008 compared to the pain of working on it in VS2005.

Overall though, I'm a lot more enthusiastic about VS2008 having used the Beta 2. What is even more exciting is the possibility of using the new C# 3 language features against .NET 2.0 projects. This is a really compelling reason to upgrade even if you have no intention of using the .NET 3.0 or 3.5 libraries.

Friday, 16 November 2007

InvokeRequired for WPF

Those of you who develop multi-threaded Windows Forms client applications will be well aware of the Control.InvokeRequired  method needed every time you access a control from a thread other than the one on which it was created. Code such as the lines seen below, which seemed so arcane when you first encountered them, have now been committed to memory:

void finder_FoundFile(object sender, FoundFileEventArgs e)
{
   if (this.InvokeRequired)
   {
      this.BeginInvoke(new EventHandler<FoundFileEventArgs>(finder_FoundFile), new object[] { sender, e });
   }
   else
   {
      listBoxFiles.Items.Add(e.File);
   }
}

But what about the brave new world of WPF? Will we have to write the same code, or will WPF controls let us set their properties from any thread? Sadly not. We have to do a similar trick, this time using the Dispatcher object to invoke the method call on the correct thread:

void finder_FileFound(object sender, FileFoundEventArgs e)
{
    if (listBoxFiles.Dispatcher.Thread == Thread.CurrentThread)
    {
        listBoxFiles.Items.Add(e.File);
    }
    else
    {
        listBoxFiles.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
            new EventHandler<FileFoundEventArgs>(finder_FileFound), sender, new object[] { e } );
    }
}

In fact, its even worse than for Windows Forms, as the thread signature for Dispatcher.BeginInvoke is even more perplexing than the one for Control.BeginInvoke. You not only have to specify a priority, but also you have to split your parameters up into "first parameter" and "array of all other parameters". Very odd.

But what about data binding in WPF? Surely that would be the easy way round this mess. Bind the list box to an observable collection, and let your background thread change the collection. Unfortunately, the same problem awaits you. As soon as you add something to the observable collection an InvalidOperationException fires warning you that you are not on the correct Dispatcher thread.

Does that rule out all hope of databinding in a multi-threaded context? Thankfully not, as I discovered a helpful article from Beatriz Costa explaining a way to make an observable collection that performs the context switch for you. Its not exactly ideal, but at least it will get you up and running. Apparently MS are considering improving the situation for the next drop of WPF.

Friday, 26 October 2007

Not worth the paper...

I've been reading a bit on "Agile" development recently, and I like what I am hearing. I didn't pay much attention to it at first, as it all seemed to be about "SCRUM", a project management strategy which is realistically never going to be introduced at the company I am working at (or indeed any of the companies I have worked for).

But one thing that I really like is the willingness to question the usefulness of documentation. Whenever I have been in discussion meetings about software quality, someone always suggests that the answer is for us to write more documentation. If only there were documents describing exactly how things worked, then all would be great. Any piece of code you were about to work on, you could simply look up the relevant document and immediately understand it. After you finish your work, you update the document with your changes. Job done!

There are a number of problems with viewing documentation as the answer to improving software quality.

First of all, at best these documents capture intention - what we planned for the software to do. As we all know, features get cut, added and modified. And the documentation ... well let's just say it never quite gets to the top of the priority pile to update it. Gradually it drifts further and further from reality until it is nothing more than an "elaborate lie".

Second, these documents rarely contain the type of detail a developer wants. Often I find myself writing down the convoluted path of events, function calls and thread signals that lead from an action being initiated to the part of the code that I actually want to change. If only there was a nice diagram I could consult that shows me. But there isn't. Or if there is, it is too simple or simply wrong.

Third, even if there is a great document, that has exactly what you need to know, it is not at all obvious how you know it exists and where to find it. They end up in obscure locations on network drives or internal websites. The person who wrote it acts shocked that you didn't look at it before you asked them, yet quite how you were supposed to become aware of its existence is never considered. The end result is that even when documentation is done well, it is seldom read by those who would benefit most from reading it.

So if documentation is not the answer, what is?

Ultimately, there are two main resources that a developer uses when trying to understand code they are about to modify. First is the person who wrote the code in the first place. Hopefully they are still around, and hopefully they are helpful. They can often save you large amounts of time. Amazingly, many developers will dive right into modifying code they haven't written without thinking of asking the original author whether there is an easy way to accomplish what they want. The end result is often overcomplicated add-ons that introduce subtle bugs, decrease cohesion and tighten coupling.

The second resource is of course the source code itself. The strongest reason for needing to write smaller classes and functions is the need for other developers to understand the code. Code that works but is impenetrably hard to comprehend is bad code. Bug reports should be filed against it. The "if it ain't broke don't fix it" maxim does have a certain wisdom to it, but I would argue that if code is so complex that no one can modify it without breaking it, then it is already broken.

Given a choice between a week of refactoring a complicated class and a week of writing about how it works, I know which I would choose.

Friday, 3 August 2007

New Version of MIDI File Mapper

I've updated MIDI File Mapper to a new version, which is mainly a bug-fix release. I'm building it now with Visual Studio 2008 beta 2, which is looking much more like a finished product than beta 1. I'm still building against .NET 2.0 of course. The multi-targetting feature in VS2008 is superb.

I'm also trying to add unit tests whenever I do a bugfix. MIDI File Mapper is one of those rare projects that lends itself really well to unit testing. I still can't bring myself to go the whole TDD approach though. Mads Kristensen has some good thoughts about when to unit test which I think gets the balance about right.

Monday, 30 July 2007

Silverlight 1.1 Alpha Refresh "Catastrophic Failure"

Microsoft have recently released the second beta of Visual Studio 2008. Along with this there are new "refreshes" of Silverlight 1.1 Alpha and Blend 2. There is a new (and much larger) Silverlight 1.1 SDK, and you will need to also download the new Sliverlight tools to develop projects in Visual Studio 2008. Links to all the downloads are provided here.

I have been updating my SilverNibbles application to run against the new 1.1 Alpha, which was fairly straight-forward. I just needed to replace the Silverlight.js and update the createSilverlight method slightly and everything ran as expected.

There was however one nasty bug resulting in an exception with "Catastrophic Failure" as its error message!

The problem was with the hack I was using (along with many other Silverlight users) to create a timer. Previously, I could simply create a storyboard with a double animation using the following XAML:

<Canvas.Resources>
<Storyboard x:Name="timer">
<DoubleAnimation Duration="00:00:0.08" />
</Storyboard>
</Canvas.Resources>

Unfortunately, when you call timer.Begin() in the new Alpha refresh you will get the "catastrophic failure" exception. The problem is that the DoubleAnimation needs a name and a target. This is easily fixed by creating a hidden rectangle for it to control:

<Canvas.Resources>
   <Storyboard x:Name="timer">
      <DoubleAnimation x:Name="animation" 
         Duration="00:00:0.08"   
         Storyboard.TargetName="InvisibleRect" 
         Storyboard.TargetProperty="Width" />
   </Storyboard>
</Canvas.Resources>
<Rectangle Visibility="Collapsed" x:Name="InvisibleRect" />

Tuesday, 10 July 2007

Creating a Custom WPF Button Template in XAML

In this post I will demonstrate how to create a custom template for a WPF button using XAML. In particular we will look at being able to have complete control over all the visual states, including disabled, mouse over, mouse down and even the appearance of the focus rectangle.

Stage 1: Creating a style

The first thing to do is to create a Style which sets the properties of the Button that we want to customize. We will put this in the Page.Resources section of our XAML file.

<Style x:Key="OrangeButton" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="FontFamily" Value="Verdana"/>
<Setter Property="FontSize" Value="11px"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource MyFocusVisual}" />
<Setter Property="Background" >
   <Setter.Value>
       <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
           <GradientStop Color="#FFFFD190" Offset="0.2"/>
           <GradientStop Color="Orange" Offset="0.85"/>
           <GradientStop Color="#FFFFD190" Offset="1"/>
       </LinearGradientBrush>
   </Setter.Value>
</Setter>

This is all fairly straightforward stuff - I am just setting the font and background gradient. The only complicated bit is that I am overriding the focus rectangle drawing, as I want a smaller rectangle than the one that gets drawn by default. So I need another style in my Page.Resources section:

<Style x:Key="MyFocusVisual">
     <Setter Property="Control.Template">
       <Setter.Value>
         <ControlTemplate TargetType="{x:Type Control}">
             <Grid Margin="3 2">
               <Rectangle Name="r1" StrokeThickness="1" Stroke="Black" StrokeDashArray="2 2"/>
               <Border Name="border" Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}"  CornerRadius="2" BorderThickness="1" />
             </Grid>
         </ControlTemplate>
       </Setter.Value>
     </Setter>
   </Style>

Stage 2: Creating a template

We would already be finished if I just wanted the normal appearance of my button to be changed. But I want complete control - including the appearance in mouse over, mouse down and disabled situations. So a template allows us to completely control what visual elements constitute our control.

Again this goes in Page.Resources. The first part is shown here:

<Setter Property="Template">
   <Setter.Value>
       <ControlTemplate TargetType="Button">
           <Border Name="border"
               BorderThickness="1"
               Padding="4,2"
               BorderBrush="DarkGray"
               CornerRadius="3"
               Background="{TemplateBinding Background}">
               <Grid >
               <ContentPresenter HorizontalAlignment="Center"
                              VerticalAlignment="Center" Name="contentShadow"
                   Style="{StaticResource ShadowStyle}">
                   <ContentPresenter.RenderTransform>
                       <TranslateTransform X="1.0" Y="1.0" />
                   </ContentPresenter.RenderTransform>
               </ContentPresenter>
               <ContentPresenter HorizontalAlignment="Center"
                           VerticalAlignment="Center" Name="content"/>
               </Grid>
           </Border>

Here you can see I have set up a simple border which gives my button rounded corners, a single pixel gray border, and uses the fill from the control's Background property. This means that by default it will use the orange gradient specified in my style, but that users can override this for their own Background.

To draw the content (usually text), we use a ContentPresenter control. I am trying to do something clever here, which is create a bevelled effect on the text by drawing it again in gray. This works fine on text, but for some reason doesn't do anything when the Content is a Shape. I'm not sure why that is. ShadowStyle is defined as follows:

<Style x:Key="ShadowStyle">
   <Setter Property="Control.Foreground" Value="LightGray" />
</Style>

Stage 3: Applying some triggers

Now we need to add some triggers to our Button template so that we can change its appearance on various events.

First is mouse over. When IsMouseOver becomes true, we change the colour of the border and the text colour to blue. Unfortunately, setting the Foreground property does nothing if the content is a shape.

<ControlTemplate.Triggers>
  <Trigger Property="IsMouseOver" Value="True">
     <Setter TargetName="border" Property="BorderBrush" Value="#FF4788c8" />
     <Setter Property="Foreground" Value="#FF4788c8" />
  </Trigger>

Next is mouse down. When IsPressed becomes true, we modify the background gradient so it looks like the button has gone 'down'. I also move the text down one pixel.

<Trigger Property="IsPressed" Value="True">                   
   <Setter Property="Background" >
   <Setter.Value>
       <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
           <GradientStop Color="#FFFFD190" Offset="0.35"/>
           <GradientStop Color="Orange" Offset="0.95"/>
           <GradientStop Color="#FFFFD190" Offset="1"/>
       </LinearGradientBrush>
   </Setter.Value>
   </Setter>
   <Setter TargetName="content" Property="RenderTransform" >
   <Setter.Value>
       <TranslateTransform Y="1.0" />
   </Setter.Value>
   </Setter>
</Trigger>

Third, we draw a dark gray border when a button is focussed or if it is the default button (the button that will be clicked when you press enter).

<Trigger Property="IsDefaulted" Value="True">
   <Setter TargetName="border" Property="BorderBrush" Value="#FF282828" />
</Trigger>
<Trigger Property="IsFocused" Value="True">
   <Setter TargetName="border" Property="BorderBrush" Value="#FF282828" />
</Trigger>

Finally, the when the button is disabled, we gray out the text and wash out the background a little by reducing its opacity.

<Trigger Property="IsEnabled" Value="False">
       <Setter TargetName="border" Property="Opacity" Value="0.7" />
       <Setter Property="Foreground" Value="Gray" />
   </Trigger>
</ControlTemplate.Triggers>

Stage 4: Using the control

Now we are ready to use our control. All we have to do is set the Style property of our button. We can override any of the settings such as font size if we like. The button will automatically resize itself to fit the content.

<StackPanel HorizontalAlignment="Center">
<Button Style="{StaticResource OrangeButton}">Hello</Button>
<Button Style="{StaticResource OrangeButton}">World</Button>
<Button Style="{StaticResource OrangeButton}" FontSize="20">Big Button</Button>
<Button Style="{StaticResource OrangeButton}" IsDefault="True">Default</Button>
<Button Style="{StaticResource OrangeButton}" IsEnabled="False">Disabled</Button>
<Button Style="{StaticResource OrangeButton}" Width="70" Height="30">70 x 30</Button>
<TextBox />
<Button Style="{StaticResource OrangeButton}" Width="30" Height="30">
<Path Fill="Black" Data="M 3,3 l 9,9 l -9,9 Z" />
</Button>
</StackPanel>

Here's what it looks like:

image

Download an example XAML file here.

Monday, 9 July 2007

XAML Gel Buttons in WPF and Silverlight

I found a nice tutorial on how to create gel buttons using Inkscape, and converted the technique to XAML.

The basic idea is that you create three rounded rectangles. The main one goes from a dark colour at the top to a bright colour at the bottom. In this case, dark green to bright green.

Then there is a transparent highlight rectangle, which goes from white at the top to transparent black at the bottom. This rectangle is marginally smaller than the main rectangle, which gives us a small border. You can make it smaller using a ScaleTransform, but I found it easier to simply modify the size and corner radius.

Finally there is a drop shadow which is a transparent black rectangle (so it appears grey on a white background). Unfortunately, because Silverlight does not support the BitmapEffect property from WPF, we cannot blur this rectangle, so the effect isn't quite as nice as it could be.

Here's the code:

<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  Width="400" Height="200" Background="White">

<!-- drop shadow - needs to be blurred for correct effect -->
<Rectangle Fill="#80000000" Width="200" Height="50" Canvas.Top="52" Canvas.Left="52" RadiusX="15" RadiusY="15">
</Rectangle>


<!-- main rect -->
<Rectangle Width="200" Height="50" Canvas.Top="50" Canvas.Left="50" RadiusX="15" RadiusY="15">
<Rectangle.Fill>
   <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
     <GradientStop Offset="0" Color="#006700"/>
     <GradientStop Offset="1" Color="#00ef00"/>
   </LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>

<!-- transparent rect -->
<Rectangle Width="198" Height="48" Canvas.Top="51" Canvas.Left="51" RadiusX="14" RadiusY="14">
<Rectangle.Fill>
   <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
     <GradientStop Offset="0" Color="#FFFFFFFF"/>
     <GradientStop Offset="1" Color="#00000000"/>
   </LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Canvas>

Here's what it looks like in Silverlight:

silverlight-gel-button

Very nice! But now look what exactly the same XAML looks like in WPF:

wpf-gel-button

Yuck! I spent a while trying to work out how come the same XAML rendered differently in Silverlight and WPF. I eventually found that if I changed the bottom gradient stop in the WPF XAML to transparent white instead of transparent black, I got the same appearance as Silverlight. In fact, it looks to me like it may be the Silverlight rendering that is at fault here rather than WPF.

Anyway, if we combine this information with the blurring of the drop shadow in WPF, we can get a nice gel button in WPF as well.

<Rectangle.BitmapEffect>
<BlurBitmapEffect Radius="3" />
</Rectangle.BitmapEffect>

Here's what it looks like:

wpf-gel-button-drop-shadow

Thursday, 5 July 2007

SubSonic GridView and ObjectDataSource

I have been experimenting with using SubSonic to convert my commentaries web page from PHP to ASP.NET. This is a very simple web page allows you to look at a list of books, sort them and filter them - nothing complicated. And Subsonic offers an automatic DAL layer which requires you simply to point it at your database and let it create the classes for you.

So one of the first tasks I approached was to work out how to get a asp:GridView to work with my SubSonic data objects. My first approach was to do it in code, with the following code in my Page_Load event:

CommentaryController c = new CommentaryController();
CommentaryCollection cc = c.FetchAll();            
gridCommentaries.DataSource = cc;
gridCommentaries.DataBind();

This worked nicely, but I wanted to know if there was a better way using the asp:ObjectDataSource. The SubSonic documentation is still a little sparse in this area, but I eventually worked out the syntax. You use the auto-generated "Controller" object that SubSonic has made for your table:

<asp:ObjectDataSource ID="odsCommentaries" runat="server" 
TypeName="CommentariesDB.CommentaryController"
SelectMethod="FetchAll" />

Simple when you know how! Now you just need to set the DataSourceID of the grid. Paging can be also turned on, but sorting doesn't. I'll perhaps post again when I have gone a bit deeper.

Wednesday, 4 July 2007

ASPNETDB.MDF?

One of the first things I tried to get my head around while learning ASP.NET 2.0 recently was the user and membership system. Its a nice idea - Visual Studio can go off and create a database for you with all the tables ready to go. It certainly saves a lot of design and implementation time by providing a solution for a common problem.

The only strange thing is where it puts this data. As far as I can tell, it never asks you where you want to store the data. It simply creates a new SQL Express file called ASPNETDB.MDF. In fact, it uses a connection string that is hidden away in your machine.config file, which I found quite confusing at first, as I couldn't work out how my web application knew how to connect to this database.

The trouble with this is obvious. If you already have a database, then you now have two databases. This not only is a problem for those whose web hosting provider charges them for multiple databases, but more seriously doesn't allow you to have a foreign key in one of your data tables pointing at the user table.

Now you might say that I am making a fuss about nothing. Surely I can just move these tables into my existing database. And I probably will. But I noticed that the SubSonic starter site has its data still separated into two databases. Why is this? The CMS database simply stores "user name" and has no hard link to the ASPNETDB database. Of course, you could write some code to look up user details (such as display name, email etc) from the user name, but you have lost the ability to get all your data with one request. What's worse, if a user is allowed to change their user name, all relationships will be broken.

So my question is, why are people doing this? Is there any benefit to having the databases separate? Or is it just because that's what Visual Studio does by default and it is a pain to change?

(not that I'm expecting an answer. I don't think this blog actually has any readers yet!)

Thursday, 28 June 2007

Adding a .NET kicks link to a Blogger blog

Here's how you can add a link to .NET kicks on a blogger hosted blog:

Go into your blog settings and select to edit the template. You will need to check the checkbox to expand custom widgets first. Then find the <data:post-body/> section and insert the following code afterwards:

<div class='post-body'>
<p><data:post.body/></p>
<div style='clear: both;'/> <!-- clear for photos floats -->
<!-- dotnetkicks BEGIN -->
<p><a expr:href='"http://www.dotnetkicks.com/kick/?url=" + data:post.url'><img border='0' expr:src='"http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=" + data:post.url'/></a></p>
<!-- dotnetkicks END -->
</div>

Thursday, 21 June 2007

OpenID in ASP.NET

I found a helpful ASP.NET OpenID control that lets you implement an OpenID login on your website. The instructions are a little weak so I thought I'd post a quick how-to here.

First you need to download the binaries and copy them into the bin folder of your web application.

  • Boo.Lang.dll (because JanRain is written in Boo)
  • JanRain.dll (an OpenID library for .NET)
  • Mono.Security.dll
  • NerdBank.Tools.dll (contains the ASP.NET control)

Then you simply register the assembly at the top of your aspx page:

<%@ Register Assembly="NerdBank.Tools" Namespace="NerdBank.Tools.WebControls" TagPrefix="nb" %> 

And then insert the control:

<nb:OpenIdLogin ID="openIdLogin" runat="server" 
        RequestEmail="Request" RequestTimeZone="Request" RequestNickname="Request" 
        PolicyUrl="http://mysite.com/PrivacyPolicy.aspx" />

Here I have requested the email, timezone and nickname. I could also say Require for these if I absolutely must have them. You should also provide a privacy policy URL, or else the user will be warned that it is missing.

The control itself provides a Register link that is customizable, allowing users to sign up with an Open ID provider such as www.myopenid.com.

When you login, if you have entered a valid OpenID URL, you will go to your OpenID provider who will ask for your password if necessary, confirm whether you want to allow that site to log you in once, always or never, and ask you what persona you want to use.

On a successful login, you can retrieve the OpenID URL as well as anything from the persona that has been made available. Here's some sample code-behind.

protected void Page_Load(object sender, EventArgs e)
{
    Uri openIdUri = Session["OpenIdUri"] as Uri;
    if (openIdUri == null)
    {
        openIdLogin.LoggedIn += new EventHandler<NerdBank.Tools.WebControls.OpenIdTextBox.OpenIdEventArgs>(openIdLogin_LoggedIn);
        openIdLogin.Visible = true;
    }
    else
    {
        openIdLogin.Visible = false;
        Response.Write("Welcome " + openIdUri.ToString() + "<br />");
        Response.Write("Email: " + Session["openIdEmail"] + "<br/>");
        Response.Write("Nickname: " + Session["openIdNickname"] + "<br/>");
        Response.Write("TimeZone: " + Session["openIdTimeZone"] + "<br/>");            
    }
}

void openIdLogin_LoggedIn(object sender, NerdBank.Tools.WebControls.OpenIdTextBox.OpenIdEventArgs e)
{
    Session["OpenIdUri"] = e.OpenIdUri;
    Session["OpenIdEmail"] = e.ProfileFields.Email;
    Session["OpenIdNickname"] = e.ProfileFields.Nickname;
    Session["OpenIdTimeZone"] = e.ProfileFields.TimeZone;
}

As you can see, it is pretty simple. I still need to work out how I am then going to integrate the OpenID login with my own site login database. The control itself also has a few limitations. In particular, entering an invalid URL causes an unhandled exception. It also doesn't give you an event for login failure (although there is a "logging in" event which I am not sure what it is for).

Identity or UniqueIdentifier (GUID) for Database Key?

Jeff Atwood posted an interesting article on whether to use GUIDs or integers for primary keys in databases. Its been a while since I did any database design, and this issue has come up in a new ASP.NET project I have recently begun.

I'm not particularly bothered about the disk space / performance issues. I suspect the difference is not all that great and in any case, my database is not likely to be huge.

As I see it the reasons for me to use GUIDs are:

1) The auto-generated ASP.NET 2 user database already uses uniqueidentifier, thus my instinct is to use it in all my tables as well for consistency.

2) Easier to copy records to other tables / import into other databases without breaking relationships. (however, there appears to be a SET IDENTITY_INSERT command in SQL Server which means that this is also possible with IDENTITY fields)

There is however one significant drawback, which may move me back to using integers. The issue is that of URLs. I want users of my site to be able to generate links to individual records in the format:

www.mysite.com/View.aspx?id=104

With a GUID, this becomes a lot more unwieldy and much more likely to encounter problems with line-wrapping truncating the URL.

www.mywebsite.com/View.aspx?id=015B34D5-A301-4543-AE1A-16708B19F602 (yuck!)

I did consider adding another integer column alongside the GUID which would act as the URL key, but this seems ridiculous to have two keys in the table.

Some people however have complained that IDs should never be present in a URL, as it allows people to randomly choose other IDs and perhaps get to pages they shouldn't. For my purposes this isn't a big deal. I have a 'private' field in my database that I will be checking before displaying an item to a user.

Constructing a URL out of an items textual description can make for nicer URLs in some cases:

www.mysite.com/View.aspx?an-example-item

But this introduces an artificial constraint on the description being unique (e.g. why can't I have two blog entries with the same title?), and also can result in messy URLs if the description is long and contains punctuation or unusual characters.

So it looks like I may be heading back to integers, unless someone has a clever idea of how to make friendly URLs when the key is a GUID.

Wednesday, 20 June 2007

CSS Layout - Columns, Headers and Footers

Most websites these days seem to have a standard layout. There is a header at the top, a footer at the bottom, and then one or two thin bars of links or adverts down either side. Using tables, this is relatively simple to do - it's a table with three rows and three columns:

<table border="1">
<tr>
    <td colspan="3"><b>Header</b></td>
</tr>
<tr>
    <td width="20%">Left Column</td>
    <td width="60%">Centre Column</td>
    <td width="20%">Right Column</td>
</tr>
<tr>
    <td colspan="3"><i>Footer</i></td>
</tr>
</table>
    <td colspan="3">Footer</td>
</tr>
</table>

But this is seriously out of fashion these days. You are now expected to use CSS, which can be a bit frustrating to learn at first. Here's how to generate a three column layout in CSS, which will expand in width to use your whole browser window.

The first thing to do is ditch the table and use div tags:

<div id="header">Header</div>
<div id="left-col">left column</div>
<div id="right-col">right column<br/>link</div>
<div id="centre-col">centre column</div>
<div id="clear"></div>
<div id="footer">footer</div>

This is all fairly straight-forward, except the need for the clear div, which ensures the footer stays at the bottom of the page.

Now we need to write the styles. For the most part, they are simple to understand.

#header {
border:solid thin gray;
text-align:center;
font-size:x-large;
font-weight:bold;
margin:5px;
}

#left-col {
border:solid thin gray;
margin:5px;
width=150px;
float:left;
}

#right-col {
border:solid thin gray;
margin:5px;
width=150px;
float:right;
}

#centre-col {
border:solid thin gray;
margin:10px 160px 5px 160px;
min-height:400px;
}

#footer {
text-align=center;
border-top:dashed thin gray;
margin:5px;
font-style:italic;
}

#clear {
clear:both;
}

Points of interest:

  • The centre column has to provide left and right margins wide enough not to obscure the left and right columns.
  • The min-height property ought to ensure that the page does not appear artificially squashed if there is no content. However, I am not sure this is working in all instances.
  • Borders and margins can be used together to create a much nicer looking separation of sections than can be achieved simply with tables.
  • You have to put the right column before the centre column for layout to work correctly.

 

Monday, 11 June 2007

XAML Football Pitch

Here's a XAML football pitch, potentially the beginnings of a Silverlight football application. I've used metres as coordinates, and drawn a pitch of the maximum allowed size. For the second penalty area, I just reflected the first one. Unfortunately there is no simple way of reusing the same item twice in the same XAML file.

<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="525" Height="400" Background="#00b000">
    <Canvas x:Name="Pitch" Canvas.Left="20" Canvas.Top="20" >
        <Canvas.RenderTransform>
            <ScaleTransform ScaleX="4" ScaleY="4" />
        </Canvas.RenderTransform>
        <!-- http://en.wikipedia.org/wiki/Football_pitch -->
        <!-- dimensions in metres -->
        <Polygon Points="0,0 120,0, 120,90, 0,90 " Stroke="White" StrokeThickness="1" />

        <Canvas x:Name="PenaltyArea" Canvas.Top="24.85" >
            <Polyline Points="0,0 16.5,0 16.5,40.3, 0,40.3" Stroke="White" StrokeThickness="1" />
            <Polyline Points="0,11 5.5,11 5.5,29.3, 0,29.3" Stroke="White" StrokeThickness="1" />
            <Polyline Points="0,16.5 -2.44,16.5 -2.44,23.8, 0,23.8" Stroke="White" StrokeThickness="1" />
            <Ellipse Canvas.Left="2.15" Width="18.3" Height="18.3" Canvas.Top="11" Stroke="White" StrokeThickness="1" Clip="M 14.35,0 h 10 v 20 h -10 Z" />
            <Ellipse Canvas.Left="10.5" Width="1" Height="1" Canvas.Top="19.65" Fill="White" />
        </Canvas>

        <Canvas x:Name="PenaltyArea2" Canvas.Top="24.85" Canvas.Left="120" >
            <Canvas.RenderTransform>
                <ScaleTransform ScaleX="-1.0" />
            </Canvas.RenderTransform>
            <Polyline Points="0,0 16.5,0 16.5,40.3, 0,40.3" Stroke="White" StrokeThickness="1" />
            <Polyline Points="0,11 5.5,11 5.5,29.3, 0,29.3" Stroke="White" StrokeThickness="1" />
            <Polyline Points="0,16.5 -2.44,16.5 -2.44,23.8, 0,23.8" Stroke="White" StrokeThickness="1" />
            <Ellipse Canvas.Left="2.15" Width="18.3" Height="18.3" Canvas.Top="11" Stroke="White" StrokeThickness="1" Clip="M 14.35,0 h 10 v 20 h -10 Z" />
            <Ellipse Canvas.Left="10.5" Width="1" Height="1" Canvas.Top="19.65" Fill="White" />
        </Canvas>

        <Ellipse Canvas.Left="50.5" Width="19" Height="19" Canvas.Top="35.5" Stroke="White" />

        <Line X1="60" Y1="0" X2="60" Y2="90" Stroke="White" StrokeThickness="1" />
    </Canvas>
</Canvas>

Thursday, 7 June 2007

Basic Web Service Access From C#

I recently wrote a small plugin for Windows Live Writer, which allowed you to enter a Bible reference, and it would either create a link to Bible Gateway, or insert the text in the English Standard Version.

To get the ESV text, I needed to call a web service in C#. It was the first time I had done this, and it turned out to be surprisingly simple. First I needed to construct the URL to call. This was simply a case of constructing the ESV Web Service API documentation. The only slight complication is the need to add a reference to System.Web so you can call HttpUtility.UrlEncode.

StringBuilder url = new StringBuilder();
url.Append("http://www.gnpcb.org/esv/share/get/");
url.Append("?key=IP");
url.Append("&passage=");
url.Append(System.Web.HttpUtility.UrlEncode(reference));
url.Append("&action=doPassageQuery");
url.Append("&include-passage-references=false");
url.Append("&include-audio-reference=false");
url.Append("&include-footnotes=false");
url.Append("&include-subheadings=false");
url.Append("&include-headings=false");

Once you have the URL with the search parameters, then you simply pass it to a web request, get its ResponseStream() and read it to the end.

WebRequest request = WebRequest.Create(url.ToString());
StreamReader responseStream = new StreamReader(request.GetResponse().GetResponseStream());
String response = responseStream.ReadToEnd();
responseStream.Close();
return response.ToString();

Obviously there are more complicated types of web service than this, which require construction and parsing of XML, but I was still impressed with how few lines of code were actually involved in my first web service call.

Creating a Hatched / Patterned Brush in WPF

I recently wanted to create a patterned brush in WPF. Of course, WPF is more than capable of doing hatching patterns, but it took me a little while to work out how to do it, especially since the MSDN2 website seems to have lost all example XAML snippets at the moment.
The approach I chose is to use a VisualBrush, although a DrawingBrush would probably work equally well. In the Visual property of the VisualBrush you add your graphics. So for my examples I have one which just has a circle, allowing you to fill an object with dots, and one with two diagonal lines, to create cross-hatching. Then you need to set some additional properties on the VisualBrush:

  • TileMode would be set to Tile as this is a repeating pattern
  • Stretch is set to None
  • The ViewBox rectangle specifies a window onto the Visual allowing you to select just a part of it. Both my fill pattern visuals were 10x10, but I made the ViewBox for the dot fill 12x12 which effectively added some spacing between the dots.
  • The ViewPort rectangle gives more flexibility by specifying the position and dimensions of the first "tile". Normally you would simply set this to the same as the ViewBox, but you could use it to scale the tiled image in X or Y dimensions, or effectively slide the tiles across.

Here's some example XAML:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:sys="clr-namespace:System;assembly=mscorlib" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
  <VisualBrush 
    x:Key="DotFillBrush" 
    TileMode="Tile" 
    Viewport="0,0,10,10" 
    ViewportUnits="Absolute" 
    Viewbox="0,0,12,12"
    ViewboxUnits="Absolute">
    <VisualBrush.Visual>
      <Ellipse 
        Fill="#00C0FF" 
        Width="10" Height="10" />
      </VisualBrush.Visual>
    </VisualBrush>      
    <VisualBrush 
      x:Key="HatchBrush" 
      TileMode="Tile" Viewport="0,0,10,10" 
      ViewportUnits="Absolute" Viewbox="0,0,10,10"    
      ViewboxUnits="Absolute">
      <VisualBrush.Visual>
        <Canvas>
           <Rectangle Fill="Azure" Width="10" Height="10" />
           <Path Stroke="Purple" Data="M 0 0 l 10 10" />
           <Path Stroke="Purple" Data="M 0 10 l 10 -10" />
        </Canvas>
      </VisualBrush.Visual>
   </VisualBrush>      
</Page.Resources>

<Canvas>
<Rectangle Canvas.Top="20" Canvas.Left="20" 
  Width="80" Height="40" 
  Fill="{StaticResource DotFillBrush}"/>
<Rectangle Canvas.Top="20" Canvas.Left="120" 
  Width="80" Height="40" 
  Fill="{StaticResource HatchBrush}"/>
<TextBlock Canvas.Top="80" Canvas.Left="20" 
  Text="Hello" FontSize="80" 
  Foreground="{StaticResource DotFillBrush}"/>
<TextBlock Canvas.Top="80" Canvas.Left="220" 
  Text="World" FontSize="80" 
  Foreground="{StaticResource HatchBrush}"/>
</Canvas>
</Page>

And here's what it looks like:

WPF Patterned & Hatched Brushes

Tuesday, 5 June 2007

Orcas Beta 1 - Not ready for the Prime time

I've had Visual Studio Orcas Beta 1 installed on a Virtual PC for a while now, and have mainly been using it to do some Silverlight 1.1 development. For the most part it has worked OK, although I've had a few niggles with it:
  • Doesn't let me step through JavaScript (despite turning on Script debugging in IE)
  • Insists on covering up part of the code with a toolbar window whenever I run in debug mode.

But today I tried to develop a WPF application in it, instead of simply using Visual Studio 2005 with the .NET 3.0 extensions. The experience was painful. The designer regularly threw exceptions, and Visual Studio itself crashed a few times.

More concerningly, the WPF designer itself seems very lacking in the basic functionality we have come to expect with Windows Forms. I am very enthusiastic about WPF, but if I am to persuade anyone at my work that using it would be a good idea, we need a much better experience for those who just want to create a Window and add controls and event handlers if they are to be tempted away from plain old Windows Forms.

  • You have to continually ask the designer to show you just XAML - it defaults to a split view with not enough space to see either the layout or XAML
  • Double-clicking a button in the designer doesn't add a handler
  • You can't choose font settings in a nice dialog - you just have to know what you can enter into FontFamily
  • You can't ctrl-click and drag to copy a control

So unfortunately, Orcas Beta 1 is very much a beta, and still a long way from being a release candidate. Hopefully there will be a beta 2 before too long, with a much enhanced WPF designer, and built-in Silverlight support.

Friday, 1 June 2007

HOWTO: Make your Silverlight 1.1 Application Scriptable

Here's how to make your Silverlight application scriptable from JavaScript. I'll use my SilverNibbles application as an example, and we will make it so that you can start a new game by pressing an ordinary HTML button that calls into the Silverlight application.

This requires us to make our Page class scriptable. We will also make the NewGame function accessible to JavaScript. First we must mark both the Page class and the NewGame function with the Scriptable attribute, found in the System.Windows.Browser namespace.

using System.Windows.Browser;
...
namespace SilverNibbles
{
[Scriptable]
public partial class Page : Canvas
{
...

[Scriptable]
public void NewGame(int players)
{
...

The next step is to register a variable that JavaScript can use to access the instance of the Page class. We do this in the Page's Loaded event handler. We have chosen to call our instance, "SilverNibbles".

public void Page_Loaded(object o, EventArgs e)
{
...
WebApplication.Current.RegisterScriptableObject("SilverNibbles", this);

Now we will create some buttons in our main HTML page that will start a new game, along with some JavaScript that handles them. Notice I have set the focus to the Silverlight control which will ensure we have keyboard focus for the new game. Also notice that our registered scriptable object is referenced as a member of the Content property of the Silverlight control.

<script src="Silverlight.js" type="text/javascript"></script>
<script src="SilverNibbles.html.js" type="text/javascript"></script>
<script type="text/javascript">
function onNewGameClick(players) {
var silverlight = document.getElementById('SilverlightControl');
silverlight.focus();
silverlight.Content.SilverNibbles.NewGame(players);
}
</script>
...

<p>New game:
<input onclick="onNewGameClick(1)" type="button" value="One Player">
<input onclick="onNewGameClick(2)" type="button" value="Two Players">
</p>

Visual Studio Regions

Visual Studio has a really nice regions feature which allows you to mark off sections of a file so they can be collapsed in the editor.

#region Painting Functions
... code goes here
#endregion

Now it seems that at Microsoft, there is a growing tendency to group absolutely everything in a class up into groups such as:

  • Public methods
  • Public properties
  • Protected methods
  • Private methods
  • Events
  • Private data

And many developers who see this then go about implementing this in their own code, removing any regions already defined and moving things into new regions based on their accessibility.

But here is the issue I have. How useful an organization is this really? I like it when a class has all of its private data together (I prefer at the top, but some put this at the bottom), so that is fine. And it makes sense for the constructors to be kept together.

But grouping methods by whether they are private, protected or public makes about as much sense as organizing your house by putting all the tables in one room and all the chairs in another. Yes its very organized, and yes you will know exactly where to go when you want a chair, but its not much use when you want to sit down at the table and eat dinner.

Say I'm looking at some code and I see a call to a CalculateOffsets function. How should I know whether I will find it in the public, private or protected methods region?

Surely it makes more sense to group functions into regions by whatever logic the class does. A good example would be a region for the Dispose pattern which includes a public method, a protected method and a finalizer. Another example would be the implementation of an interface such as IComparable. In a Windows forms class, you might have a region that had event handlers for all controls on the form, or a region for just for dealing with one particularly complex control's events.

I'm not saying that the Microsoft way doesn't have benefits or should never be used. But its the one-size-fits-all approach that bothers me. I don't want to expand eight regions every time I open a file only to find most of them are empty or just have one item in each.

Thursday, 1 February 2007

Welcome

Welcome to my new programming blog. This is intended as a place for occasional thoughts on development, often with a .NET focus. I've created this blog, as my existing one is mainly about theology, and I didn't want to scare people away with stuff they have no interest in (works both ways!).