Sometimes it’s fun to do something that any self-respecting developer would cringe if they saw someone else do it. That’s kind of how I feel about abusing the capabilities of C# 4′s dynamic binding capabilities. Even though I stubbornly resisted the idea of adding dynamic binding to C#, I find myself playing around with it every now and then to make really ugly code look nicer.

For example, let’s consider the case where you’re matching a US telephone number (with optional extension) against a regular expression and separating the individual components into separate variables. In the US telecom industry it’s very common to deal with 10 digit numbers without having to worry about international phone formats, but the regular expression is still ugly. Unfortunately there’s nothing I can do about that, but this is about making code pretty, not making regular expressions pretty.

(?<npa>\d{3})-?(?<nxx>\d{3})\-?(?<line>\d{4})( x(?<ext>\d+))?

Let’s look at the implementation of such a task in PowerShell vs. C#.

Here is how the code would look in PowerShell. It’s elegance gets me sexually aroused.

if ('555-123-4567' -match $PhoneRegex) {
    $npa = $matches.npa
    $nxx = $matches.nxx
    $line = $matches.line
    $ext = $matches.ext
}

Here is how the equivalent code would look in C#.

var match = Regex.Match("555-123-4567", PhoneRegex);
if (match.Success) {
    string npa = match.Groups["npa"].Value;
    string nxx = match.Groups["nxx"].Value;
    string line = match.Groups["line"].Value;
    string ext = match.Groups["ext"].Success ?
                     match.Groups["ext"].Value :
                     null;
}

It’s not the end of the world, but it still feels like it could be so much more concise. Well with C# 4 I can create an object that derives from DynamicObject and wraps a System.Text.RegularExpressions.Match object to provide a much more “scripty” feel to the above code.

var match = DynamicRegex.Match("555-123-4567", PhoneRegex);
if (match) {
    string npa = match.npa;
    string nxx = match.nxx;
    string line = match.line;
    string ext = match.ext;
}

Note that a static method called DynamicRegex.Match is returning an object of type dynamic. The actual implementation is my wrapper class called DynamicMatch. It overrides the TryGetMember and TryConvert calls that make the above possible. It simply directs property and indexer calls into the Match.Groups collection and has some special logic for conversion to Boolean.

Anyhow, to get into detail about how C# does dynamic binding at runtime would take a series of posts in itself. I just thought this was a pretty interesting use of the dynamic binding that wasn’t yet another dynamic XML wrapper. Yeah I know it defeats the purpose of using a strongly-typed language like C# but oh well I thought it was interesting. Source is below.

public sealed class DynamicMatch : DynamicObject
{

    private readonly Regex _Regex;
    private readonly Match _Match;

    public DynamicMatch( Regex regex, Match match )
    {

        Contract.Requires( regex != null, "Regex cannot be null." );
        Contract.Requires( match != null, "Match cannot be null." );

        _Regex = regex;
        _Match = match;

    }

    public override IEnumerable GetDynamicMemberNames( )
    {
        // i honestly don't know where the hell this is used
        return _Regex.GetGroupNames( );
    }

    public override bool TryConvert( ConvertBinder binder, out object result )
    {

        // supports casting back to the original Match object
        if ( binder.Type == typeof( Match ) ) {
            result = _Match;
            return true;
        }

        // supports casting the match to its string representation
        // is the complete match result
        if ( binder.Type == typeof( String ) ) {
            if ( _Match.Success ) {
                result = _Match.Value;
                return true;
            }
            else {
                result = null;
                return true;
            }
        }

        // supports casting the match to a boolean indicating success
        if ( binder.Type == typeof( Boolean ) || binder.Type == typeof(Boolean?) ) {
            result = _Match.Success;
            return true;
        }

        return base.TryConvert( binder, out result );

    }

    public override bool TryGetIndex( GetIndexBinder binder, object[] indexes, out object result )
    {

        // supports 'match[x]' where x is a named capture group
        // or 'match[n]' where n is an implicit capture group index

        if ( indexes.Length == 1 ) {
            Group group = _Match.Groups[Convert.ToString( indexes[0] )];
            if ( group != null && group.Success ) {
                result = group.Value;
                return true;
            }
            else {
                result = null;
                return true;
            }
        }

        return base.TryGetIndex( binder, indexes, out result );

    }

    public override bool TryGetMember( GetMemberBinder binder, out object result )
    {

        // supports 'match.x' where x is a named capture group

        Group group = _Match.Groups[binder.Name];
        if ( group != null && group.Success ) {
            result = group.Value;
            return true;
        }
        else {
            result = null;
            return true;
        }

    }

    public override bool TryUnaryOperation( UnaryOperationBinder binder, out object result )
    {

        // supports 'if (match) {...}'
        if ( binder.Operation == ExpressionType.IsTrue ) {
            result = _Match.Success;
            return true;
        }

        // supports 'if (!match) {...}'
        if ( binder.Operation == ExpressionType.IsFalse || binder.Operation == ExpressionType.Not ) {
            result = !_Match.Success;
            return true;
        }

        return base.TryUnaryOperation( binder, out result );

    }

    public static bool operator ==( DynamicMatch x, bool y )
    {
        // supports 'if (match == true) {...}'
        return x._Match.Success == y;
    }

    public static bool operator !=( DynamicMatch x, bool y )
    {
        // supports 'if (match != true) {...}'
        return x._Match.Success != y;
    }

    public static bool operator ==( DynamicMatch x, string y )
    {
        // supports 'if (match == "foo") {...}'
        return x._Match.Value == y;
    }

    public static bool operator !=( DynamicMatch x, string y )
    {
        // supports 'if (match != "foo") {...}'
        return x._Match.Value != y;
    }

}

public static class DynamicRegex
{

    public static dynamic Match( string input, string pattern )
    {
        var regex = new Regex( pattern );
        var match = regex.Match( input );
        return new DynamicMatch( regex, match );
    }

}
[TestClass]
public class DynamicRegexTests
{

    [TestMethod]
    public void DynamicMatchConvertsToMatch( )
    {

        var dynamicMatch = DynamicRegex.Match( "Hello World", @"H[A-Za-z]+" );
        Match regularMatch = dynamicMatch;

        Assert.IsTrue( regularMatch.Success );
        Assert.AreEqual( "Hello", regularMatch.Value );

    }

    [TestMethod]
    public void SuccessfulMatchConvertsToTrue( )
    {

        var match = DynamicRegex.Match( "Hello World", @"H[A-Za-z]+" );

        // Convert To Boolean
        Assert.IsTrue( (bool)match, "Successful match should convert to true." );

        // Compare To Boolean
        if ( !match ) { Assert.Fail( "Successful match should convert to true." ); }
        if ( match == false ) { Assert.Fail( "Successful match should convert to true." ); }
        if ( match != true ) { Assert.Fail( "Successful match should convert to true." ); }

    }

    [TestMethod]
    public void UnsuccessfulMatchConvertsToFalse( )
    {

        var match = DynamicRegex.Match( "Hello World", @"^H[A-Za-z]+$" );

        // Convert To Boolean
        Assert.IsFalse( (bool)match, "Unsuccessful match should convert to false." );

        // Compare To Boolean
        if ( match ) { Assert.Fail( "Unsuccessful match should convert to false." ); }
        if ( match == true ) { Assert.Fail( "Unsuccessful match should convert to false." ); }
        if ( match != false ) { Assert.Fail( "Unsuccessful match should convert to false." ); }

    }

    [TestMethod]
    public void SuccessfulMatchConvertsToString( )
    {

        var match = DynamicRegex.Match( "Hello World", @"H[A-Za-z]+" );

        // Convert To String
        Assert.AreEqual( "Hello", (string)match, "Successful match should convert to Match.Value." );

        // Compare To String
        if ( match != "Hello" ) { Assert.Fail( "Successful match should convert to Match.Value." ); }

    }

    [TestMethod]
    public void UnsuccessfulMatchConvertsToNull( )
    {

        var match = DynamicRegex.Match( "Hello World", @"^H[A-Za-z]+$" );

        // Convert To String
        Assert.IsNull( (string)match, "Unsuccessful match should convert to null." );

        // The following will always fail - C# does the null check without converting
        // if (match != null) { Assert.Fail("..."); }

    }

    [TestMethod]
    public void SuccessfulCapturesAccessedAsProperties( )
    {

        var match = DynamicRegex.Match( "123-456-7890", @"(?\d{3})-?(?\d{3})-?(\d{4})" );

        Assert.AreEqual( "123", match.npa, "Capture group npa could not be accessed by name." );
        Assert.AreEqual( "456", match.nxx, "Capture group nxx could not be accessed by name." );

    }

    [TestMethod]
    public void SuccessfulCapturesAccessedAsIndexer( )
    {

        var match = DynamicRegex.Match( "123-456-7890", @"(?\d{3})-?(?\d{3})-?(\d{4})" );

        Assert.AreEqual( "123", match["npa"], "Capture group npa could not be accessed by name." );
        Assert.AreEqual( "456", match["nxx"], "Capture group nxx could not be accessed by name." );
        Assert.AreEqual( "7890", match[1], "Unnamed capture group could not be accessed by number." );
        Assert.AreEqual( "123-456-7890", match[0], "Capture group 0 should return entire match." );

    }

    [TestMethod]
    public void UnsuccessfulMatchReturnsPropertiesAsNull( )
    {

        // Must match 123-456-7890
        // Last 4 digits will cause unsuccessful match
        var match = DynamicRegex.Match( "123-456-XXXX", @"(?\d{3})-?(?\d{3})-?(\d{4})" );

        Assert.IsNull( match.npa, "Unsuccessful match must return all properties as null." );
        Assert.IsNull( match["npa"], "Unsuccessful match must return all properties as null." );
        Assert.IsNull( match["1"], "Unsuccessful match must return all properties as null." );
        Assert.IsNull( match["0"], "Unsuccessful match must return all properties as null." );

    }

    [TestMethod]
    public void SuccessfulMatchReturnsOptionalGroupAsNull( )
    {

        // Must match at least 123-456-
        // Last 4 digits are optional
        var match = DynamicRegex.Match( "123-456-XXXX", @"(?\d{3})-?(?\d{3})-?(\d{4})?" );

        Assert.AreEqual( "123", match["npa"], "Successful match should return captured groups as string." );
        Assert.AreEqual( "456", match["nxx"], "Successful match should return captured groups as string." );
        Assert.AreEqual( "123-456-", match[0], "Successful match should return entire match as string." );

        Assert.IsNull( match[1], "Successful match should return missing optional groups as null." );

    }

}

Windows Tablet

It’s been a while since I used a Windows Tablet PC with an external monitor. One thing that used to drive me nuts (I think as recently as Vista) was the fact that if you set the external monitor as your “main” display, the pen would all of a sudden control the cursor on the external monitor, not the tablet display.

No longer! I just plugged in an external monitor and the pen still controls the tablet display. But it gets better.

My HP 2710P tablet has a very odd shaped hinge. So it doesn’t fit very well into the tablet stand I have. It fits much better when the side opposite the hinge is facing down into the stand. This requires me to change the screen orientation 180 degrees (upside down.)

Before, in XP (and I’m pretty sure Vista too) this would also change the orientation of the external monitor! But now the two have separate orientations.

In other words, Windows 7 Tablet with an external monitor is working exactly the way I expect and want. So while some people (cough… Rob… cough…) may think Windows 7 sucks on a tablet1, I’d still take a Windows 7 tablet over an iPad any day!


  1. Granted, Rob is referring to touch capabilities in Windows, where I am primarily a pen-based tablet user. 

Here’s a quickie. I had an issue where I have a ListBox that will (should) never scroll but I need its item selection capability. It is inside of another ScrollViewer (along with some other controls) and that outer ScrollViewer should be what scrolls. Unfortunately ListBox was eating the MouseWheel events, preventing the outer ScrollViewer from scrolling.

A quick Google search didn’t turn up much. I didn’t want to set IsHitTestVisible=False because like I said, I need the selection ability. I also thought changing the template or subclassing ScrollViewer was a bit drastic.

So instead I came up with the following IgnoreMouseWheelBehavior. Technically it’s not ignoring the MouseWheel, but it is “forwarding” the event back up and out of the ListBox. Check it.

/// <summary>
/// Captures and eats MouseWheel events so that a nested ListBox does not
/// prevent an outer scrollable control from scrolling.
/// </summary>
public sealed class IgnoreMouseWheelBehavior : Behavior<UIElement>
{

  protected override void OnAttached( )
  {
      base.OnAttached( );
      AssociatedObject.PreviewMouseWheel += AssociatedObject_PreviewMouseWheel ;
  }

  protected override void OnDetaching( )
  {
      AssociatedObject.PreviewMouseWheel -= AssociatedObject_PreviewMouseWheel;
      base.OnDetaching( );
  }

  void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
  {

      e.Handled = true;

      var e2 = new MouseWheelEventArgs(e.MouseDevice,e.Timestamp,e.Delta);
      e2.RoutedEvent = UIElement.MouseWheelEvent;

      AssociatedObject.RaiseEvent(e2);

  }

}

And here’s how you would use it in XAML.

<ScrollViewer Name="IScroll">
    <ListBox Name="IDont">
        <i:Interaction.Behaviors>
            <local:IgnoreMouseWheelBehavior />
        </i:Interaction.Behaviors>
    </ListBox>
</ScrollViewer>

Ran into a bit of an issue today when an application I developed stopped working. Fortunately I built a pretty awesome tracing framework that I often drop into all my applications that lets me get at important runtime information from within the application. In this case I was able to see the WCF Data Services query that was being sent. It looked fine. The problem was that the response couldn’t be deserialized.

So I pasted the query into IE and saw this.

2010-07-16_1101

Ahh great so the XML is malformed. That explains why the WCF Data Services client library can’t give me any useful information. Now I could go to the trouble of using Fiddler or FiddlerCap but in this case there’s a much simpler way.

Start Notepad » File » Open » Paste the URL

2010-07-16_1027

I admit I only found this out recently that our old friend Notepad could be used to open HTTP URL’s.

In this case the error was due to the fact that the database I am working with is a train wreck that lacks any constraints so the integrity of the data was violated. That’s a different issue altogether and one that’s out of my hands, unfortunately.

Colin Eberhardt has posted a neat multi-purpose value converter that tries various conventional conversion methods to convert from one type to another. This overcomes a frustrating limitation in WPF/Silverlight data binding where the XAML parser is seemingly more intelligent at converting literal values to target types than values sourced from binding expressions.

Unfortunately, the TypeConverter usage will not work in Silverlight but perhaps using the XamlReader technique there’s something that could be done?

Also, a couple of the readers in the comments had a great suggestion to derive the converter from MarkupExtension to simplify the XAML. I think I’m going to have to go back and do that with all of my converters now.

imageI have been using this Visual Studio extension called VS10x Code Map by Michael Kiss and I love it. It sits on the left or right of the Visual Studio text editor and provides an attractive outline view of the properties, methods, regions, etc in the current code file and lets you quickly navigate to a particular member.

I have tried other add-ins in the past that provided a code outline but they were usually slow, unattractive, or used a tree view style interface that I found to be more cumbersome than scrolling. The other nice thing about this one is that if you use regions (sorry Jeff, but to each his own) it will group your members by those regions.

It’s probably only useful if you have a wide-screen monitor since it does take up valuable screen real-estate. But according to the Visual Studio Extension Manager (Tools -> Extension Manager) it’s pretty popular.

Silverlight 3 introduced a very useful control called a ChildWindow. This is a "modal" dialog box that can contain whatever content you give it and it floats above the rest of the application inside a Popup control. I say "modal" because unlike ShowDialog in WPF or Windows Forms, showing a ChildWindow via the Show method returns immediately, but it does disable the application’s RootVisual which prevents the user from interacting with the rest of the application while the window is showing.

As cool as this control is, I have always been pretty disappointed in the default behavior and look and feel of the ChildWindow class. For example, the animation as it opens and closes is very unnatural and distracting. The overlay that "dims" the rest of the application successfully indicates the modal nature of the control, but it’s too strong I think. In fact I’m fine with just the normal "grey out" that happens when the RootVisual is disabled.

ChildWindow-Normal

A while back I had created a pretty lame ChildWindow template that kinda sorta looked like a Windows 7 Aero window. It was a half-hearted attempt but it looked a lot better than the above. But then I saw a question on StackOverflow asking about creating an Aero glass style window and I decided to revisit the problem and try to come up with something decent.

The first thing to note when trying to customize the ChildWindow template is that you probably ought to start from scratch. The default template is like an onion of nested grids and borders and panels, none of which have any particular significance to the control other than the border named Chrome. This is what ChildWindow uses to handle dragging. Although as I’ll discuss shortly, I think there’s some bugs there.

ChildWindow-Windows7Aero

My ChildWindow template looks like the one in the above screen shot. I tried very hard to make the template "cooperate" with the style. That is whenever possible, I use values from the style or directly applied to the control instead of hard coding colors, paddings, etc in the template which are hard to tweak.

For example, if you want to create more glass area around the white content area, just increase the Padding of the ChildWindow. Padding, of course, does not have to be uniform. You could give it a larger padding on the bottom to make room for Windows Media Player-style controls.

There’s a couple of caveats though…

  • There is no blur! I originally wanted to blur the elements underneath the "glass" areas but I found this to be a lot more difficult than I thought. A blur effect only applies the blur to its associated element and visual children. Since the stuff I want it to blur is only showing through as a result of opacity, the blur has no effect. I suspect there’s a way to do this using a WritableBitmap but I haven’t gone down that rabbit hole just yet.
  • It doesn’t have any open/close transition animations. I took those out on purpose but now I think a very subtle transition is needed to look more natural than the jarring animation from the default style.
  • It doesn’t currently have any visual states defined on the close button which I took out for the sake of keeping the XAML clean for the example. But you can pretty much drop in the transitions from the default template to get that back.
  • Dragging the ChildWindow appears to be jerky for some reason. I think this is a bug in the way ChildWindow handles dragging because Jeff Wilcox’s customized template has the same problem when one of his readers enabled dragging on his Zune style template.

Finally, there are a number of blog posts out there about how to hack the ChildWindow to be non-modal. Tim Huer has an excellent post about this. I have not tried but I don’t see any reason why my template could not also be applied in conjunction with those methods.

To grab the ResourceDictionary that contains the template, click the Show Source link below.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                    xmlns:s="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

  <Style x:Key="AeroWindowCloseButton" TargetType="Button">
    <Setter Property="Background">
      <Setter.Value>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
          <GradientStop Color="#FFEEB3AC" Offset="0.009"/>
          <GradientStop Color="#FFDA8578" Offset="0.402"/>
          <GradientStop Color="#FFC64D38" Offset="0.459"/>
          <GradientStop Color="#FFC84934" Offset="0.598"/>
          <GradientStop Color="#FFD48671" Offset="0.885"/>
          <GradientStop Color="#FFE8BBAE" Offset="0.943"/>
        </LinearGradientBrush>
      </Setter.Value>
    </Setter>
    <Setter Property="BorderBrush" Value="#FF5E5E5E"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Foreground" Value="White"/>
    <Setter Property="Padding" Value="3"/>
    <Setter Property="Width" Value="45"/>
    <Setter Property="Height" Value="20"/>
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Button">
          <Border
            Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            CornerRadius="0,0,3,3">
            <Path
              Fill="{TemplateBinding Foreground}"
              Width="11"
              Height="10"
              Stretch="Fill"
              Data="F1 M 6.742,3.852 L 9.110,1.559 L 8.910,0.500 L 6.838,0.500 L 4.902,2.435 L 2.967,0.500 L 0.895,0.500 L 0.694,1.559 L 3.062,3.852 L 0.527,6.351 L 0.689,7.600 L 2.967,7.600 L 4.897,5.575 L 6.854,7.600 L 9.115,7.600 L 9.277,6.351 L 6.742,3.852 Z">
              <Path.Stroke>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                  <GradientStop Color="#FF313131" Offset="1"/>
                  <GradientStop Color="#FF8E9092" Offset="0"/>
                </LinearGradientBrush>
              </Path.Stroke>
            </Path>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
  <Style x:Key="AeroWindow" TargetType="s:ChildWindow">
    <Setter Property="Background" Value="White"/>
    <Setter Property="BorderBrush" Value="#FF5E5E5E"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="OverlayBrush" Value="Transparent"/>
    <Setter Property="OverlayOpacity" Value="0"/>
    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    <Setter Property="Padding" Value="5"/>
    <Setter Property="IsTabStop" Value="false"/>
    <Setter Property="TabNavigation" Value="Cycle"/>
    <Setter Property="UseLayoutRounding" Value="True"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="s:ChildWindow">
          <Grid x:Name="Root">

            <!-- OVERLAY BEHIND CHILDWINDOW -->
            <Grid
              x:Name="Overlay"
              Background="{TemplateBinding OverlayBrush}"
              Opacity="{TemplateBinding OverlayOpacity}"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Top"/>

            <!-- WINDOW CONTAINER -->
            <Grid x:Name="ContentRoot" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
  
              <!-- GLASS BACKGROUND AND BORDER -->
              <!--
              NOTE: This border should not physically contain the rest of the
              window contents because it has an opacity setting that would
              affect all of its children. Instead, a second border is directly
              above it in the z-order and contains the child elements.
              -->
              <Border
                x:Name="Chrome"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}"
                CornerRadius="4"
                Opacity="0.9">
                <Border.Background>
                  <LinearGradientBrush MappingMode="Absolute" StartPoint="0,0" EndPoint="1920,0">
                    <GradientStop Color="#FFADC9E5" Offset="0.010"/>
                    <GradientStop Color="#FFA7C2DC" Offset="0.069"/>
                    <GradientStop Color="#FFB7D2EC" Offset="0.084"/>
                    <GradientStop Color="#FFB7D2EC" Offset="0.146"/>
                    <GradientStop Color="#FFA8C4DE" Offset="0.168"/>
                    <GradientStop Color="#FFB8D3ED" Offset="0.455"/>
                    <GradientStop Color="#FFA6C1DB" Offset="0.518"/>
                    <GradientStop Color="#FFB6D1EB" Offset="0.543"/>
                    <GradientStop Color="#FFA7C2DC" Offset="0.604"/>
                    <GradientStop Color="#FFB7D2EC" Offset="0.618"/>
                    <GradientStop Color="#FFB7D2EC" Offset="0.700"/>
                    <GradientStop Color="#FFABC6E1" Offset="0.722"/>
                    <GradientStop Color="#FFB1CEEA" Offset="0.778"/>
                  </LinearGradientBrush>
                </Border.Background>
              </Border>
  
              <!-- WINDOW CONTENTS -->
              <!-- 
              NOTE: This element will not have a visible border. The Chrome element
              provides the visible border but this element needs to have a matching
              thickness and corner radius so that the contents of the window are
              "pushed in" by the same amount.
              -->
              <Border BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4">

                <Grid>

                  <Grid.RowDefinitions>
                    <RowDefinition Height="25"/>
                    <RowDefinition/>
                  </Grid.RowDefinitions>
                  <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
                  </Grid.ColumnDefinitions>

                  <ContentControl
                    Content="{TemplateBinding Title}"
                    Foreground="#FF393939"
                    FontWeight="Bold"
                    VerticalAlignment="Center"
                    Margin="6,0,6,0"
                    Grid.Row="0"
                    Grid.Column="0"/>

                  <Button
                    x:Name="CloseButton"
                    Style="{StaticResource AeroWindowCloseButton}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="1,0,1,1"
                    VerticalAlignment="Top"
                    Margin="0,0,5,0"
                    Grid.Row="0"
                    Grid.Column="1"/>

                  <Border
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Margin="{TemplateBinding Padding}"
                    Grid.Row="1"
                    Grid.ColumnSpan="2">

                    <ContentPresenter
                      x:Name="ContentPresenter"
                      Content="{TemplateBinding Content}"
                      ContentTemplate="{TemplateBinding ContentTemplate}"
                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>

                  </Border>

                </Grid>
              </Border>
            </Grid>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

Download the example project which includes the source for SwitchConverter and SwitchedContent.

I figured I would write some more about value converters since I think there’s a lot of cool things you can do with them, especially when you put a little extra effort into generalizing them.

One thing I find I need to do often is to show content conditionally depending on a certain data binding condition. For example, let’s say we had a WeatherReport object that had a Condition property that had the following enumeration values: Sunny, Cloudy, Rain, Snow. We want to have a different image for each condition.

In WPF you can do this using a DataTrigger but even that is a bit verbose for my tastes. In Silverlight it’s a lot more difficult. The Expression Blend SDK gives you a behavior called a DataTrigger that can achieve this. But I think a value converter would do this job quite well and the markup will be the same for WPF and Silverlight. Check out the following snippet:

<Grid>
    <Grid.Resources>
        <e:SwitchConverter x:Key="WeatherIcons">
            <e:SwitchCase When="Sunny" Then="Sunny.png" />
            <e:SwitchCase When="Cloudy" Then="Cloudy.png" />
            <e:SwitchCase When="Rain" Then="Rain.png" />
            <e:SwitchCase When="Snow" Then="Snow.png" />
        </e:SwitchConverter>
    </Grid.Resources>
    <Image Source="{Binding Condition, Converter={StaticResource WeatherIcons}}" />
</Grid>

SwitchConverterDemo

The only thing that was a little bit tricky about this converter was making sure that the value being bound to could be compared against the strings in the When attribute. At first I naively just figured I could "ToString" the input value to the converter and compare that against the When values. But this is very fragile. For example, if the input were a DateTime, the values "6/25/2010" and "6/25/2010 12:00:00 AM" would not be considered equal. That’s not even considering the fact that regional settings would cause even bigger headaches.

So this implementation simply tries to use the IConvertible interface to normalize the values before comparing them. My actual implementation uses a very complex series of checks that take into account available Parse methods, TypeConverter, etc. But it’s a lot of code that I didn’t want to junk up the example with.

The only other thing worth mentioning is that the SwitchConverter has a ContentProperty attribute that allows us to specify the SwitchCase elements inline, making the resulting XAML pretty clean if you ask me. ContentPropertyAttribute is a little known and underused attribute that lets you specify which property will be set by content in between the element tags. There are a lot of places where this should probably be applied such as DataGrid.Columns, Setter.Value, etc. But now I’m going off on a tangent.

So while this solution is in no way a replacement for a good ViewModel, it does let you cleanly represent conditional content in XAML which really sits well with the OCD part of my brain that hates ugly markup.

As a bonus, I’ve also included a SwitchedContent control that makes it much easier to swap out entire elements based upon the conditional input. An example of that is shown below.

<Button Command="{Binding IncrementCommand}">
    <e:SwitchedContent Binding="{Binding Count}" Else="Overflow!">
        <e:SwitchCase When="0" Then="Zero" />
        <e:SwitchCase When="1" Then="One" />
        <e:SwitchCase When="2" Then="Two" />
        <e:SwitchCase When="3" Then="Three" />
        <e:SwitchCase When="4" Then="Four" />
        <e:SwitchCase When="5" Then="Five" />
        <e:SwitchCase When="6" Then="Six" />
        <e:SwitchCase When="7" Then="Seven" />
        <e:SwitchCase When="8" Then="Eight" />
        <e:SwitchCase When="9" Then="Nine" />
        <e:SwitchCase When="10" Then="Ten" />
    </e:SwitchedContent>
</Button>

Download the example project which includes the source for SwitchConverter and SwitchedContent.

I came across a question on StackOverflow that is a very frequently asked question regarding data binding in XAML. Given a boolean, how do you bind the opposite value to the target? The answer of course is to use a value converter (a class implementing IValueConverter) and invert the boolean in code.

I do this a lot though so I have a NegateConverter in "Josh’s Toolbox". My NegateConverter can negate a lot of things, not just booleans. For example, numeric values, Visibility, Thickness, Point, etc. Now I’ll probably never need to negate a Point but I figured what the hell. The code isn’t very pretty and if you don’t like if statements, just stop reading. It’s utilitarian, get over it.

Can you think of anything else that can be easily negated? (Click Show Source below.)

/// <summary>
/// Produces an output value that is the negative of the input.
/// </summary>
/// <remarks>
/// The built-in signed types are supported as well as a handful of other
/// commonly used types such as <see cref="T:Point"/>, <see cref="T:TimeSpan"/>,
/// <see cref="T:Thickness"/>, etc.
/// </remarks>
public sealed class NegateConverter : IValueConverter
{

    #region Fields

    /// <summary>
    /// The default singleton instance of this converter.
    /// </summary>
    public static readonly NegateConverter Default = new NegateConverter( );

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="T:NegateConverter"/> class.
    /// </summary>
    public NegateConverter( )
    {
    }

    #endregion

    #region Methods

    /// <summary>
    /// Converts a value.
    /// </summary>
    /// <param name="value">The value produced by the binding source.</param>
    /// <param name="targetType">The type of the binding target property.</param>
    /// <param name="parameter">The converter parameter to use.</param>
    /// <param name="culture">The culture to use in the converter.</param>
    /// <returns>
    /// A converted value. If the method returns null, the valid null value is used.
    /// </returns>
    public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
    {

        if ( value == null ) {
            return null;
        }

        if ( value is double ) {
            return Negate( (double)value );
        }

        if ( value is int ) {
            return Negate( (int)value );
        }

        if ( value is bool ) {
            return Negate( (bool)value );
        }

        if ( value is long ) {
            return Negate( (long)value );
        }

        if ( value is IConvertible ) {
            return Negate( (IConvertible)value, culture );
        }

        if ( value is TimeSpan ) {
            return Negate( (TimeSpan)value );
        }

        if ( value is Point ) {
            return Negate( (Point)value );
        }

        if ( value is Thickness ) {
            return Negate( (Thickness)value );
        }

        throw new ArgumentException( "Cannot negate " + value.GetType( ) + ".", "value" );

    }

    /// <summary>
    /// Converts a value.
    /// </summary>
    /// <param name="value">The value that is produced by the binding target.</param>
    /// <param name="targetType">The type to convert to.</param>
    /// <param name="parameter">The converter parameter to use.</param>
    /// <param name="culture">The culture to use in the converter.</param>
    /// <returns>
    /// A converted value. If the method returns null, the valid null value is used.
    /// </returns>
    public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
    {
        return Convert(value, targetType, parameter, culture);
    }

    /// <summary>
    /// Negates a <see cref="T:TimeSpan"/> value.
    /// </summary>
    /// <param name="value">The value to negate.</param>
    /// <returns>The negated value.</returns>
    private static TimeSpan Negate( TimeSpan value )
    {
        return value.Negate( );
    }

    /// <summary>
    /// Negates a <see cref="T:Point"/> value.
    /// </summary>
    /// <param name="value">The value to negate.</param>
    /// <returns>The negated value.</returns>
    private static Point Negate( Point value )
    {
        return new Point(
            -value.X,
            -value.Y
        );
    }

    /// <summary>
    /// Negates a <see cref="T:Thickness"/> value.
    /// </summary>
    /// <param name="value">The value to negate.</param>
    /// <returns>The negated value.</returns>
    private static Thickness Negate( Thickness value )
    {
        return new Thickness(
            -value.Left,
            -value.Top,
            -value.Right,
            -value.Bottom
        );
    }

    /// <summary>
    /// Negates a <see cref="T:Boolean"/> value.
    /// </summary>
    /// <param name="value">The value to negate.</param>
    /// <returns>The negated value.</returns>
    private static bool Negate( bool value )
    {
        return !value;
    }

    /// <summary>
    /// Negates a <see cref="T:Int32"/> value.
    /// </summary>
    /// <param name="value">The value to negate.</param>
    /// <returns>The negated value.</returns>
    private static int Negate( int value )
    {
        return -value;
    }

    /// <summary>
    /// Negates a <see cref="T:Int64"/> value.
    /// </summary>
    /// <param name="value">The value to negate.</param>
    /// <returns>The negated value.</returns>
    private static long Negate( long value )
    {
        return -value;
    }

    /// <summary>
    /// Negates a <see cref="T:Double"/> value.
    /// </summary>
    /// <param name="value">The value to negate.</param>
    /// <returns>The negated value.</returns>
    private static double Negate( double value )
    {
        return -value;
    }

    /// <summary>
    /// Negates a <see cref="T:IConvertible"/> value by round tripping through
    /// the System.Decimal type.
    /// </summary>
    /// <param name="value">The value to negate.</param>
    /// <param name="formatProvider">The culture information.</param>
    /// <returns>The negated value as the original input type.</returns>
    private static object Negate( IConvertible value, IFormatProvider formatProvider )
    {

        TypeCode inputType = value.GetTypeCode( );

        decimal input = value.ToDecimal( formatProvider );
        decimal output = Decimal.Negate( input );

        return System.Convert.ChangeType( output, inputType, formatProvider );

    }

    #endregion

}   // class

I saw in my feed reader a posting that announced the release of the Visual Studio 2010 Pro Power Tools extension that was available in the Visual Studio 2010 Extension Manager. At first glance I thought it was the Power Tools extension that I’ve been using for a while. However, this is a totally separate extension that adds some very awesome features.

Note that they go into a lot of detail on the enhanced document tab feature but although this is neat and useful, it’s not really in my top 3 features. I’ve emphasized my 3 favorite features.

  • Document Well 2010 Plus
    • Tab Well UI
      • Scrollable tabs
      • Vertical tabs
      • Pinned tabs
      • Show close button in tab well
    • Tab Behavior
      • Remove tabs by usage order (LRU)
      • Show pinned tabs in a separate row/column
    • Sorting
      • Sort tabs by project
      • Sort tabs alphabetically
      • Sort tab well dropdown alphabetically
    • Tab UI
      • Color tabs according to their project or according to regular expressions
      • Miscellaneous options that modify tab UI
        • Show document/toolwindow icon in tab
        • Show close button in tab
        • Modify dirty indicator style
        • Modify minimum and maximum tab size
  • Searchable Add Reference Dialog
  • Highlight Current Line
  • HTML Copy
  • Triple Click
  • Fix Mixed Tabs
  • Ctrl + Click Go To Definition
  • Align Assignments
  • Colorized Parameter Help
  • Move Line Up/Down Commands
  • Column Guides