Register Now | Sign In
FeedGhost Logo

RSS feed Stu Smith: Making It Up As I Go Along

My life working for BinaryComponents, coding, design, and other stuff.

Live Writer Crapware


Posted on 31 May 2007 13:45

I use Windows Live Writer to compose these blog posts: it works nicely for what I want to use it for, although it's a little quirky. Unfortunately it's been in beta for absolutely ages, so when I read that a new version was available I went to download... and here's what I was confronted with:

What the (expletive) is that? Aren't we past all that yet? I may not use Internet Explorer for anything other than broken sites that require it, but I still don't want my homepage changed. My homepage and search preferences have nothing to do with Live Writer; this is simply a blatant attempt at taking over my machine with crapware.

 

So; can I install this software? Even if I untick those choices can I trust it not to do change anything else? For now I feel I'm better off sticking with the old version.

FeedGhost - Professional RSS Reading

WPF Clocks, Part 4


Posted on 25 May 2007 15:53

In previous articles I got the basic clock working (part one and part two), and also covered a really easy was of loading and saving configuration files (part three). Today I'm going to cover making the application a bit more glossy and animated, and make a shaped, borderless window.

 

Firstly, despite the shading, the clock's looking pretty flat and two-dimensional. I want the clock to look like it's actually made of something, with the frame casting an inner shadow at the top, and the plastic cover reflecting a bit of light. We can do this by overlaying a couple of semi-transparent ellipses over the top of the clock (since we want to change the lightness of everything in the clock). For these effects radial fills can be used to produce all kinds of curved shading.

<!-- Clock.xaml, * Highlights -->

<Ellipse Canvas.Left="3" Canvas.Top="3" Width="94" Height="94">

  <Ellipse.Fill>

    <RadialGradientBrush Center="0.51,0.52" SpreadMethod="Pad">

      <GradientStop Offset="0.0" Color="Transparent" />

      <GradientStop Offset="0.9" Color="Transparent" />

      <GradientStop Offset="1.0" Color="#a0000000" />

    </RadialGradientBrush>

  </Ellipse.Fill>

</Ellipse>

<Ellipse Canvas.Left="3" Canvas.Top="3" Width="94" Height="94">

  <Ellipse.Fill>

    <RadialGradientBrush Center="0.7,0.8" SpreadMethod="Pad" RadiusX="1.3" RadiusY="1.2">

      <GradientStop Offset="0.0" Color="Transparent" />

      <GradientStop Offset="0.4" Color="#30ffffff" />

      <GradientStop Offset="0.5" Color="#60ffffff" />

      <GradientStop Offset="0.6" Color="#3fffffff" />

      <GradientStop Offset="1.0" Color="Transparent" />

    </RadialGradientBrush>

  </Ellipse.Fill>

</Ellipse>

The various centers and radii I produced by experimentation - with effects like these, I use vivid primary colours to get the shape of the effect right, then change to using the faint light and dark colours I actually want.

That's an improvement, but we can go one better than that. Let's make the hands stand out from the face a little. In real life, they'd cast a shadow on the clock face, and we can achieve a similar effect in WPF using a DropShadowBitmapEffect.

<!-- Clock.xaml, * HourAndMinuteHandsEffect -->

<Canvas.BitmapEffect>

  <DropShadowBitmapEffect ShadowDepth="1" Softness="0.1" Opacity="0.5" />

</Canvas.BitmapEffect>

Just as before, I'm only going to show the effects for the hour and minute hands; the second hand shadow is pretty much the same, except I wanted a slightly different shadow.

Bitmap effects are one of those areas in WPF where we need to apply a little care. As the name suggests, they are bitmaps as opposed vectors like most of the elements in WPF. There's a few things we need to take care with:

  • Bitmap effects are rendered in software and thus performance needs to be considered. Sprinkling bitmap effects liberally around your application is a recipe for slowness.
  • The bitmaps produced shouldn't be transformed if at all possible. Rotating or scaling a bitmap will cause visual oddities. For this reason you'll see I haven't applied the effects to the hands themselves, since the hands rotate. Instead I've added the rotating hands to a canvas, and then applied the effect to that.
  • Bitmap effects have degrading effect on text. Applying a bitmap effect to a container that contains text will cause the text-rendering to drop down to using normal anti-aliasing, instead of clear-type.

Having mentioned and considered those points, let's see the difference:

It's a subtle effect, but I think it's an improvement, and it will make the clocks look even better when we start to zoom them in and out in an animation.

 

The first animation I want is to have each clock "zoom in" as the mouse passes over it, and zoom out again as the mouse leaves. Each individual clock is displayed within a "ClockDisplay" control, which include the clock itself, plus the time and timezone text.

WPF can animate any dependency property (we discussed those earlier), either an existing one such as "Opacity" if you want your animated control to fade in and out, or one you define yourself. In this case I've chosen to define one myself; please note however that there are alternate ways of achieving the following effect without defining your own property.

// ClockDisplay.xaml.cs

static ClockDisplay()

{

  ZoomProperty = DependencyProperty.Register

    ( "Zoom", typeof( double ), typeof( ClockDisplay )

    , new FrameworkPropertyMetadata( 1.0, FrameworkPropertyMetadataOptions.AffectsRender ) );

}

Next we need to hook the size of the clock to this property:

<!-- ClockDisplay.xaml -->

<cx:Clock ...snipped... RenderTransformOrigin="0.5,1.3">

  <cx:Clock.RenderTransform>

    <ScaleTransform ScaleX="{Binding Zoom}" ScaleY="{Binding Zoom}" />

  </cx:Clock.RenderTransform>

</cx:Clock>

Finally of course we need to set up an animation. Animations in WPF are declarative as opposed to procedural - we set up the "intent" of the animation, and WPF takes care of applying it and rendering it. An advantage of using the built-in animation features is that WPF will also handle incomplete animations - if the user moves the mouse off the clock before the zoom animation has completed, the clock will animate back again correctly without the animations conflicting.

<!-- ClockDisplay.xaml -->

<UserControl.Triggers>

  <EventTrigger RoutedEvent="Control.MouseEnter">

    <BeginStoryboard>

      <Storyboard>

        <DoubleAnimation Storyboard.TargetProperty="Zoom" To="2" Duration="0:0:0.5" FillBehavior="HoldEnd" />

      </Storyboard>

    </BeginStoryboard>

  </EventTrigger>

  <EventTrigger RoutedEvent="Control.MouseLeave">

    <BeginStoryboard>

      <Storyboard TargetProperty="Zoom">

        <DoubleAnimation To="1" Duration="0:0:0.5" FillBehavior="HoldEnd" />

      </Storyboard>

    </BeginStoryboard>

  </EventTrigger>

</UserControl.Triggers>

Simple. Essentially we trigger the first animation on MouseEnter, and the second on MouseLeave. Each affects the "Zoom" property, takes half a second to complete, and moves the zoom value either to x2 or x1 respectively.

Here we can see one of the clock's animations triggered by the mouse entering the ClockDisplay control:

Notice that CPU indicator - the clocks typically only consume around 2% of the CPU time, even when running as a shaped window. As you can see I also applied the same zoom factor to the time text.

 

And finally, to get that anti-aliased, per-pixel-alpha, shaped window effect - lots of code? Not in the slightest:

<Window ...snipped... Background="#00000000" WindowStyle="None" AllowsTransparency="True" ShowInTaskbar="False">

 

  <!-- ...content... -->

 

</Window>

 

So there we go, a fairly attractive, animated clocks display with very little effort. I've still got plenty of work to do though - in particular the options dialog - but once it's all to a decent standard I'll release this as a little application anyone can use.

FeedGhost - Professional RSS Reading

WPF Clocks, Part 3


Posted on 24 May 2007 16:55

I have the basic clock working (part one and part two), but I'm going to take a little diversion from WPF into LINQ now.

 

Almost all applications need to store data somewhere, even if it's just where the user left their windows the last time they used it (you do store that, don't you?). My world clocks application is sufficiently simple that a little configuration file in the user's settings directory will suffice. For this article, I want to read in a list of timezones when the application starts, and write it out again on exit, and we'll assume the user has been able to modify that list by means of some sort of options dialog.

 

Firstly, a little aside: what I'm going to present here is suitable for my application. You might come back and tell me it's too slow, or unsuitable for large data files. That's fine; choose your method based on profiling if you need raw speed, or switch to a database (LINQ-to-SQL might help you there...), or whatever. There's no single technique or tool that is suitable for all situations, but I'm reading and writing a tiny file, so I think this is an acceptable technique.

Secondly, another little aside: everything I've covered so far with regards WPF is available in .NET 3, which is a fully released component. LINQ is contained within .NET 3.5, which is still in alpha. That would have been a factor that decided against LINQ (since ideally I would have wanted my application to be released soon), BUT I wanted to use the TimeZoneInfo class, and that's only available in .NET 3.5, so I figured I might as well use whichever .NET 3.5 features I wanted to.

 

OK, on with the code. Here's the format I'm going to use for my data file. Feel free to skip this section and the next if you're reading quickly.

<?xml version="1.0" encoding="utf-8"?>

<Settings>

  <TimeInfos>

    <TimeInfo Zone="...serialized gobbledygook..." />

    <TimeInfo Zone="...serialized gobbledygook..." />

    <TimeInfo Zone="...serialized gobbledygook..." />

  </TimeInfos>

</Settings>

Nice and simple, and I can add other stuff later if I need to.

I'm storing my zone information in a wrapper class called TimeInfo, the important bits for this example are two static methods:

public sealed class TimeInfo

{

  public static string Write( TimeInfo ti )

  {

    // ...

  }

 

  public static TimeInfo Read( string s )

  {

    // ...

  }

}

If you've used XPath before, the new features in LINQ-to-XML won't be too problematic. Here's how I read the config file:

XDocument xDoc = XDocument.Load( filename );

 

_timeInfos.AddRange( from XElement t in xDoc.Descendants( "TimeInfo" )

                     select TimeInfo.Read( t.Attribute( "Zone" ).Value ) );

The nice thing here is the lack of loops (or at least, the lack of explicit loops). C# 1.0 gave us the foreach statement, which abstracted away the workings of a collection enumeration. C# 3.0 now lets us abstract away the loop itself, letting us select and transform elements from various data sources.

Writing the config file is almost as easy, and makes use of the new XElement classes. Previously you'd either have to create an XmlDocument, which was rather unwieldy, or make use of an XmlTextWriter and forgo automatic creation of the document structure (that is, forget to write out a closing tag and you end up with invalid XML).

XDocument xDoc = new XDocument(

                   new XElement( "Settings",

                     new XElement( "TimeInfos",

                       from TimeInfo ti in _timeInfos

                       select new XElement( "TimeInfo", new XAttribute( "Zone", TimeInfo.Write( ti ) ) ) ) ) );

 

xDoc.Save( filename );

Those brackets on the end are a bit ugly though. I can use a temporary variable if I want to:

var timeInfos = from TimeInfo ti in _timeInfos

                select new XElement( "TimeInfo", new XAttribute( "Zone", TimeInfo.Write( ti ) ) );

 

XDocument xDoc = new XDocument(

                   new XElement( "Settings",

                     new XElement( "TimeInfos", timeInfos ) ) );

 

xDoc.Save( filename );

The "var" keyword is new too - and let's clear up that confusion that everyone starts with - it's not weak typing. "Var" simply means "infer the type from the following expression", and in this case that type is:

System.Linq.Enumerable.SelectIterator<TimeInfo,System.Xml.Linq.XElement>

Whew! I'm not going to explain that because I don't understand it well enough, but the basic idea is that LINQ creates expression trees, which can be used in a variety of ways. LINQ-to-SQL, for example, translates those expressions into efficient SQL commands with WHEREs and JOINs.

 

LINQ has certainly saved me a lot of drudge work here. The reading code feels like a query, and my writing code is even indented like the XML file I want to produce, and feels more like a "template" for that file than a process for creating it. .NET 3.5 certainly has that feel about it - the feel that I declare the structures and form of what I'm trying to achieve, and the system takes care of the processing based on that form.

 

Tomorrow will mark the end of this series of articles - I'll add some "depth" to my clocks, a bit of animation, and turn the application into a shaped, borderless window that overlays the desktop, ready for launching from the system tray. Stay tuned!

FeedGhost - Professional RSS Reading

WPF Clocks, Part 2


Posted on 23 May 2007 13:18

Last article I got the basic background of the clock drawing nicely, but it's about time it actually showed the time.

 

There are three hands to the clock; hour, minute and seconds. I wanted the second hand to have a slightly different effect, so that's in a separate canvas. I'll talk more about that later.

<!-- Clock.xaml, * Hands -->

<Canvas>

  <!-- + HourAndMinuteHandsEffect -->

  <!-- + HourHand -->

  <!-- + MinuteHand -->

</Canvas>

 

<Canvas>

  <!-- + SecondHandEffect -->

  <!-- + SecondHand -->

</Canvas>

Since each hand of the clock is basically the same, I'll just present the hour hand.

<!-- Clock.xaml, * HourHand -->

<Rectangle Width="8" Height="36" Fill="White" Stroke="#333333" StrokeThickness="0.6" RadiusX="2" RadiusY="2">

  <Rectangle.RenderTransform>

    <TransformGroup>

      <TranslateTransform X="-4" Y="-32" />

      <RotateTransform Angle="{Binding HourAngle}" />

      <TranslateTransform X="50" Y="50" />

    </TransformGroup>

  </Rectangle.RenderTransform>

</Rectangle>

A hand is basically a rectangle, rounded off a little on the corners. Each rectangle starts off positioned at (0, 0), so we move it so that the zero point is at the "spindle", rotate it by the correct angle, then move it again to put it in the middle of the canvas. (There are alternative methods of positioning a rectangle, notably using the Left and Top attached properties of the canvas, but this is how I prefer to deal with it).

The rotation angle for each hand needs to be defined of course; I've bound the rotation to a property that doesn't exist yet, which is an error. This is an area I found myself repeatedly stumbling on; I kept writing my properties as simple getters and setters backed by a member variable, and then had to keep changing them to be WPF dependency properties.

// Don't do this!!

public double HourAngle

{

  get

  {

    return _hourAngle;

  }

  set

  {

    _hourAngle = value;

  }

}

 

private double _hourAngle;

It seems to be a rule of thumb that if you're going to bind to something in WPF, it needs to be a dependency property, so save yourself some time and bite the bullet and code them that way from the start.

// Clock.xaml.cs

static Clock()

{

  HourAngleProperty = DependencyProperty.Register

    ( "HourAngle", typeof( double ), typeof( Clock )

    , new FrameworkPropertyMetadata( 0.0, FrameworkPropertyMetadataOptions.AffectsRender ) );

 

  // Repeat for minutes and seconds...

}

 

public double HourAngle

{

  get

  {

    return (double) GetValue( HourAngleProperty );

  }

  set

  {

    SetValue( HourAngleProperty, value );

  }

}

It seems like more typing, it seems like it could be inefficient, it seems type-un-safe -- but it pays dividends later. To re-iterate: if you want to bind a property in your WPF element tree, you need a dependency property.

Of course, we want the hands of the clock to move, and as it stands they're all pointing to twelve noon. There's two basic ways of achieving this: via animations, or via property assignments.

WPF has a very powerful animation system, so I tried this first to make the hands move. Essentially, each hand angle had a storyboard that changed the value from 0 to 360 over the course of twelve hours, one hour, or one minute. The initial angles were set to the time of day that the control was created.

Unfortunately, while the clock hands turned nicely, when I created a row of clocks the second hands were all at slightly different positions. I couldn't control the starting positions sufficiently accurately to make this a viable method.

The second working solution was simply to update the angles in a timer. Note that this most definitely isn't the normal way of specifying animations in WPF -- animations should be declarative, just like your element tree -- but I think you can divide data values into two kinds:

  • Those that are the substance of the display, i.e. actual data values -- these need not be animation-based, and
  • Those that are the style of the display, eye-candy or look-and-feel -- these should be animation-based.

 

I think the time is a real substance of a clock display, so I don't feel too bad about updating the angles in a timer.

// Clock.xaml.cs

private void _timer_Tick( object sender, EventArgs e )

{

  DateTime date = GetDate(); // You need to supply this function.

 

  double hour = date.Hour;

  double minute = date.Minute;

  double second = date.Second;

 

  double hourAngle = 30 * hour + minute / 2 + second / 120;

  double minuteAngle = 6 * minute + second / 10;

  double secondAngle = 6 * second;

 

  HourAngle = hourAngle;

  MinuteAngle = minuteAngle;

  SecondAngle = secondAngle;

}

(That's a DispatcherTimer if you're interested).

 

We now have a working clock. Now I'm aware that this article wasn't half as interesting as the last one (which probably wasn't all that hot to begin with anyway), but there's always less interesting bits of any program, and calculating the angles of the hands of a clock is dull in any language.

One thing that was pretty neat is that the presentation of the clock hands (rounded rectangles in this case) is neatly separated from the substance of the hands' angles - so if I want my clock to be one of those with Mickey Mouse hands, I can hand the XAML over to a graphic designer with a copy of Expression Blend and it will all still work. (Or more likely, when people start asking for different themes, I can switch to using WPF's styling mechanism).

 

I'm going to take a brief diversion and look at some LINQ in the next article, and then I'll move onto adding some gloss and depth to the clock, and take a quick peek at some proper WPF animations.

FeedGhost - Professional RSS Reading

WPF Clocks, Part 1


Posted on 22 May 2007 13:16

I've been trying to get myself up to speed with .NET 3 and 3.5 using Orcas recently, and rather than just "play" I've set myself a little application to write: a "world time" application that sits in the system tray and displays clocks for various timezones around the world. (It's a sufficiently simple application that I should be able to complete it in my spare time, such as I have any, but one that will also be useful to me and hopefully other MicroISVs). I'm writing this as hopefully a little taster for any MFC or WinForms developers who haven't had much of a look at WPF yet. (I'll do some LINQ articles shortly too). I'm going to write this in a kind of "literate programming" style - not complete code, but snippets that could be connected together.

 

Here's what I've produced so far, so you can see where the articles lead:

 

Not great, but a starting point.

 

Each clock is a graphical object (as opposed to say a flow-layout dialog), so we use a canvas:

<!-- Clock.xaml -->

<Canvas Width="100" Height="100" x:Name="_canvas">

 

  <!-- + Background -->

  <!-- + Markers -->

  <!-- + Hands -->

  <!-- + Highlights -->

 

</Canvas>

The width and height I've set don't really matter since we can scale the clock to whatever size we like, but it means that my measurements inside the clock can be in percentages.

Starting with the background, I want a circle with a graded fill from top to bottom, surrounded by a white "glow". (Eventually this is going to pop-up as a desktop widget, so I want the clocks to have a border to distinguish them from the user's desktop).

<!-- Clock.xaml, * Background -->

<Ellipse Canvas.Left="0" Canvas.Top="0" Width="100" Height="100">

  <Ellipse.Fill>

    <RadialGradientBrush>

      <GradientStop Offset="0.0" Color="White" />

      <GradientStop Offset="0.95" Color="White" />

      <GradientStop Offset="1.0" Color="Transparent" />

    </RadialGradientBrush>

  </Ellipse.Fill>

</Ellipse>

<Ellipse Canvas.Left="3" Canvas.Top="3" Width="94" Height="94">

  <Ellipse.Fill>

    <LinearGradientBrush StartPoint="0.4,0.1" EndPoint="0.6,0.9">

      <LinearGradientBrush.GradientStops>

        <GradientStop Offset="0.0" Color="#888888" />

        <GradientStop Offset="1.0" Color="#111111" />

      </LinearGradientBrush.GradientStops>

    </LinearGradientBrush>

  </Ellipse.Fill>

</Ellipse>

I originally used an "outer glow" bitmap effect, but under animation is wobbled a bit. So we now have:

That's the easy bit done. Markers next. Although the idea of WPF is to include the graphical elements in the XAML, for the little markers around the edge that would be silly - dozens of nearly identical elements says "loop" to me and for that we need code. The XAML is just a placeholder:

<!-- Clock.xaml, * Markers -->

<Canvas x:Name="_markersCanvas" />

And the actual elements are added in code:

// Clock.xaml.cs

protected override void OnInitialized( EventArgs e )

{

  base.OnInitialized( e );

 

  for( int i = 0; i < 60; ++i )

  {

    Rectangle marker = new Rectangle();

 

    if( ( i % 5 ) == 0 )

    {

      marker.Width = 3;

      marker.Height = 8;

      marker.Fill = new SolidColorBrush( Color.FromArgb( 0xe0, 0xff, 0xff, 0xff ) );

      marker.Stroke = new SolidColorBrush( Color.FromArgb( 0x80, 0x33, 0x33, 0x33 ) );

      marker.StrokeThickness = 0.5;

    }

    else

    {

      marker.Width = 0.5;

      marker.Height = 3;

      marker.Fill = new SolidColorBrush( Color.FromArgb( 0x80, 0xff, 0xff, 0xff ) );

      marker.Stroke = null;

      marker.StrokeThickness = 0;

    }

 

    TransformGroup transforms = new TransformGroup();

 

    transforms.Children.Add( new TranslateTransform( -( marker.Width / 2 ), marker.Width / 2 - 40 - marker.Height ) );

    transforms.Children.Add( new RotateTransform( i * 6 ) );

    transforms.Children.Add( new TranslateTransform( 50, 50 ) );

 

    marker.RenderTransform = transforms;

 

    _markersCanvas.Children.Add( marker );

  }

 

  for( int i = 1; i <= 12; ++i )

  {

    TextBlock tb = new TextBlock();

 

    tb.Text = i.ToString();

    tb.TextAlignment = TextAlignment.Center;

    tb.RenderTransformOrigin = new Point( 1, 1 );

    tb.Foreground = Brushes.White;

    tb.FontSize = 4;

 

    tb.RenderTransform = new ScaleTransform( 2, 2 );

 

    double r = 34;

    double angle = Math.PI * i * 30.0 / 180.0;

    double x = Math.Sin( angle ) * r + 50, y = -Math.Cos( angle ) * r + 50;

 

    Canvas.SetLeft( tb, x );

    Canvas.SetTop( tb, y );

 

    _markersCanvas.Children.Add( tb );

  }

}

That's a fair bit of code, but it goes to show that there's nothing magical about XAML - it's just a convenient way of creating elements, and we can do the same in code, albeit in a slightly long-winded way. The markers are just rectangles; to position them I just position them at the top-center of the canvas and rotate around the center. I couldn't find a way to exactly position centered text on a canvas, so in the end I used the following technique:

  1. Set the text size to be half what you actually want;
  2. Position the top-left of the text where you want it centered;
  3. Set the transform origin to the the bottom-right;
  4. Scale by a factor of two.

 

We have the basic background now.

 

I'll cover the hands and the "highlights" in a separate article since this is getting to be a bit long, but hopefully you can see how things are starting to fit together. For me the most important thing in WPF compared to WinForms or MFC is something that isn't in this article, and indeed won't be because we just don't need it: there's no WM_PAINT or OnPaint handler. Everything I've done so far is done once -- the XAML just "sits there", the OnInitialized method is called once -- and thereafter WPF takes over.

FeedGhost - Professional RSS Reading

Debugging XAML Runtime Exceptions


Posted on 15 May 2007 20:07

Now this helpful hint may seem a bit obvious to you, but bear in mind I do my WPF experimentation in the evenings after working on FeedGhost during the day, which invariably means I have a glass of wine in my hand. My brain isn't at its sharpest is what I'm trying to say.

Let's say we've done something silly but we just can't see it:

  static Clock()
  {
    HourAngleProperty = DependencyProperty.Register
      ( "HourAngle", typeof(double), typeof(Clock)
      , new FrameworkPropertyMetadata( 0, FrameworkPropertyMetadataOptions.AffectsRender ) );
  }

D'oh! Really obvious but it caught me out. On running it we get:

System.Windows.Markup.XamlParseException

 

...which is really unhelpful. The hint is to look through the InnerExceptions:

InnerException

Ahhhh of course. It all becomes clear. Silly me.

FeedGhost - Professional RSS Reading

Hackety Hack - More Ruby, More Why


Posted on 01 May 2007 19:26

I was Stumbling the other day (come one, I can't work for seven hours solid), and I chanced upon Hackety Hack.

Now I was about to pass on by because it looked like a project aimed at schools (and in fact it may be aimed at schools, but nevermind), until I recognized the author.

It's "why the lucky stiff" (silly name...), famous for being included in The Best Software Writing I (which really should be on your bookshelf if it isn't already).

Anyway, to get to the point, Hackety Hack is a "programming starter kit". Mr Why can explain it much better than me, so just go here and read the wiki.

The writing style is somewhere between conversational and dramatic; personally I love it but you may differ. I guess the aim is twofold; two teach programming, and to teach programming Ruby. I've been wanting to explore Ruby a little for a while so having fired up a VM I've dived in.

Like the other work I've seen by the author, this one is simple, elegant, and presented in bitesize chunks. If you're a coder by trade you'll probably find the tutorial goes a little slow, but let's face it, this project probably isn't aimed at you. (Skip to the cheat sheet if you want your facts fired at you fast).

In concept the program isn't anything novel, it's basically a tutorial with embedded interpreter (I think later on I'll be able to build my own web application - oooh oooh - but I haven't got there yet). What's great about this is the presentation, the thought, the care; in short all the stuff that usually gets left until last in most homebrew (and dare I say it, open-source) programming projects.

Let's end with my deep, profound thoughts:

 

Ruby is a really nice language

The idea of being able to knock up a powerful application with a minimum amount of code ("code yourself a blog in 6 lines" screams the headline) is very seductive. Microsoft desperately needs something to fill the gap left by VB6; spit on it though we may have done, it was popular. Mr Why, a definite minority in the Ruby camp, understands this; Hackety Hack is available for Windows first, Linux next.

 

There's more to a nice language than being a nice language

Here's the defining point for me: I'm following the tutorial and I've written this line:

save_as = ask("Save the file as:")

Now I'm told, for extra credit... "see if you can change ... your program to use this command [ask_save_file]". OK let me try:

save_as = ask_save_file("Save the file as:")

Run the program again, all going fine, until I get:

[ArgumentError] wrong number of arguments (1 for 0)

So what's the problem? No help. No Intellisense. No syntax highlighting. No environment. And without those things, no deal.

(OK, stop shouting, I know this a beta project, a demo project, not an environment. Doesn't matter to me. A modern language is about far, far more than what you type into Notepad/Emacs. Anyway I tried "Ruby Installer for Windows" and what I basically got was a crapload of... stuff. Nothing I could imagine working with).

 

Somebody should hire or at least contract this person

Hearts and minds are there to be won and the battle lines (to be slightly melodramatic) are being drawn thus:

 - vs - 

I want to see the definitive guide to Silverlight, or Linq, or whatever the next exciting technology is, written by why. I want more dancing foxes and fewer bearded men.

 

You should try it

Yes really, Mr C# developer. Right now. Install it and be enlightened. Not just by the Ruby language, but by the English one too.

FeedGhost - Professional RSS Reading