Graphic Cell Values in NetAdvantage for WPF

Derek Harmon / Monday, April 30, 2007

There is an old addage that one picture can be worth a thousand words, and nowhere is this advice better applied to the User Experience than showing your data to users graphically.

That is what makes the declarative support in NetAdvantage for WPF (released last week, if you had not heard) for displaying images in xamDataGrid cells so tremendous.  Authoring WPF templates in XAML turns out easier than writing a draw filter or manipulating the rendered HTML to inject an <img> tag.

Motivation.

In media long, long ago, ABC Sports used to broadcast international competitions on their Wide World of Sports program (like bowling, downhill skiing, cycling, track and field, etc.)  Do you remember how the leader boards shown during these broadcasts would be printed on the screen in large boxy-lettering with the athlete’s position, name, country and time (or score)?  The quality of these broadcast television graphics were comparable to any 8-bit microcomputer of the era (Texas Instruments 99/4A, Commodore Vic-20, Atari 400).

Without the video graphics horsepower for more, or the columns to fully-spell out country names, leader boards would display the athletes' countries usually with three-letter codes:  “USA” for the United States of America, “GBR” for Great Britain, and “FRG” for West Germany (if you’re too young to remember West Germany, then I’m afraid you missed out on some really fierce sporting contests between East and West Germany!)

These three-letter codes weren’t just made up, they are a part of an evolving international standard (ISO 3166), but how many people outside of South Africa would have known that “ZAF” stood for South Africa?

The art of video graphics made strides in the late 80’s and early 90’s, and the graphics on broadcast TV improved with better video resolution, color palettes and software.  Today, in place of (or in addition to) the three-letter country codes, you see more widespread depiction of flags in these leaderboards.

This is a common scenario developers encounter everyday, where you are processing codes designed for machines, but that need to be understandable to people.  Fortunately, just as video graphics have made strides on broadcast TV, the graphics of PCs have advanced by leaps and bounds such that today we have a platform such as the Windows Presentation Foundation (WPF).

Easy as 1-2-3 in WPF.

While it has been possible to put pictures into your cells before, it’s perhaps never been easier than it is today.  To illustrate how you can implement this in your solution, and in honor of sports broadcasting history, I’ve chosen as a simple data source the Formula One race winners at Monaco for the past ten years (for the curious: a more complete listing).

I’ll be using an object data provider on GrandPrixRacer .NET objects exposing both a Country and a read-only FlagImageUri property that is derived from the Country, but you can achieve similar results if your data provider provides the URI for an image (for example, a data provider connecting to a database could access the URI property if it were defined as a calculated column, based off of the ISO 3166 alpha-3 Country code).

1.  Expose an Uri-type property (or calculated column).

public Uri FlagUri
{
  get 
  {
    if (this._flagUri == null)
    {
      this._flagUri = Utility.CreateFlagUriFromCountryIso316(this.Country);
    }
    return this._flagUri;
  }
}

CreateFlagUriFromCountryIso3166 is a static utility method that essentially formats the 3-character country code into an image resource URI used to load a flag image, like "GBR.PNG". 

2.  Add a WPF Template in the XAML anywhere in the resource chain.

<Style TargetType="{x:Type igDP:CellValuePresenter}"
       x:Key="CountryFlag">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate
          TargetType="{x:Type igDP:CellValuePresenter}">
        <Grid Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}">
          <Rectangle Visibility="Collapsed"
              Fill="#FFBBBBBB"
              HorizontalAlignment="Left"
              Margin="4,5,4,4"
              x:Name="LeftBorder"
              VerticalAlignment="Center"
              Width="1" />
          <Border Margin="4,5,4,4"
              x:Name="MainBorder"
              Grid.Row="0" 
              Background="{TemplateBinding Background}"  
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}" 
              CornerRadius="{TemplateBinding CornerRadius}"/>
          <Image Margin="{TemplateBinding Padding}"
              HorizontalAlignment="{TemplateBinding
                HorizontalContentAlignment}"
              VerticalAlignment="{TemplateBinding
                VerticalContentAlignment}"
              Source="{Binding RelativeSource={RelativeSource
                TemplatedParent}, Path=Content}" />
        </Grid>
      </
ControlTemplate>
    </
Setter.Value>
  </Setter>
</Style>
 
3. Add a FieldSettings for the Field corresponding to the property from step 1, and that has a CellValuePresenterStyle that references the WPF template from step 2.
 
<igDP:XamDataGrid.FieldLayouts>
  <igDP:FieldLayout>
    <igDP:FieldLayout.Fields>
      <igDP:Field Name="FlagPictureUri" Label="Country">
        <igDP:Field.Settings>
          <igDP:FieldSettings
            CellValuePresenterStyle="{StaticResource CountryFlag}"/>
        </igDP:Field.Settings>
      </igDP:Field>
      <igDP:Field Name="Driver" Label="Driver Name" />
      <igDP:Field Name="CarMaker" Label="Car Maker" />
      <igDP:Field Name="RaceTime" Label="Race Time" />
    </igDP:FieldLayout.Fields>
  </igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>

 

What About Image-type Properties?
 
Sometimes you may point a Field to a property that has the type of Image, that is, if I had used the FlagImage property from the GrandPrixRacer class in this code.  You may ask, “Do I still need the WPF Template if my property type is Image?”  The short answer is “No,” and in one case a xample included with NetAdvantage for WPF shows this implementation, but you must understand what issues you could run into by putting the image directly into the cell
value like this.

 

This shortcut works as long as the control doesn’t need to do anything with the Image, like take its value for use someplace else.  Because the Image can only have one parent element in the Visual Tree rendered for the control by WPF.  If it were to be moved to another location (for example, if the image needed to appear in a scroll tip or separate header) then the Image would now have a new parent element in the Visual Tree.  Since it cannot have two parent elements at the same time, it must be detached from the first parent -- meaning it disappears from the Cell.

The WPF template is important here because it instantiates a new copy of the Image for you without affecting the parent of the original Image reference in the Cell.  The original image won’t disappear on you, and you also gain a lot more flexibility in the dimensions, margins, tool tip, etc.

Get the Code. 

The code demonstrating everything I've described here is available for NetAdvantage for WPF 2007 Volume 1 (Full version) and also NetAdvantage for WPF 2007 Volume 1 Express.

The only difference in the Express version of this example is that the project file references the NetAdvantage for WPF 2007 Volume 1 Express assemblies (i.e., it contains assembly references to Infragistics3.Windows.v7.1.Express.dll instead of Infragistics3.Windows.v7.1.dll).