Josh Smith on WPF

This blog was moved to http://joshsmithonwpf.wordpress.com/

Subscriptions

< April 2007 >
Su Mo Tu We Th Fr Sa
25 26 27 28 29 30 31
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 1 2 3 4 5

News

This blog was moved to http://joshsmithonwpf.wordpress.com/

Navigation

Post Categories

[This blog has moved]

My blog has moved!

I decided to resign from Infusion and am moving "Josh Smith on WPF" to a new site. From now on all of my new blog posts will be here: http://joshsmithonwpf.wordpress.com/. Please update your RSS aggregator settings to point to the new address. See you there! :)

posted January 31, 2007 10:03 PM by Josh

Advanced Custom TreeView Layout

I just posted a follow-up article to my "Custom TreeView Layout in WPF" article on CodeProject. The new article dives deeper into how the TreeView can be customized to display its items in a fresh new way. If you're interested in checking it out, here's the link: Advanced Custom TreeView Layout in WPF. Enjoy!

posted January 28, 2007 8:24 PM by Josh

WPF + Super Models

A while back I worked on a WPF application for Elite Model Management. I have not been allowed to mention anything about it until now, so it feels great to finally spread the word.

The application is shown in a video which Microsoft created and is showing at the upcoming 2007 Launch Events for Windows Vista, Office 2007, Exchange Server 2007, etc. That's right folks, a WPF app I worked on will be shown at MSFT Launch Events all over the USA (possibly the world?). I'm thrilled!

You can view the video here. It's the second case study on the page. There are several clips of the WPF app in the video, but the longest one starts at 1min 23sec into the film.

I got to spend about 3-4 weeks working on that app. I worked with Alex Yakhnin on it, with whom it was an absolute pleasure to collaborate and brainstorm. Kudos to you, Alex! :)

posted January 24, 2007 8:03 AM by Josh

Drag and Drop in a ListView

I just posted an article to the CodeProject, which discusses a class that automates drag-and-drop operations in the ListView control. I spent a lot of time making sure that my utility class is flexible and customizable, so it should be useful in many different applications.

If you're interested in checking it out, here's the link: http://www.codeproject.com/useritems/ListViewDragDropManager.asp If you have any questions/comments/suggestions about the article or code, please feel free to leave them on the article's messageboard at the bottom of the page. Enjoy!

posted January 21, 2007 11:56 PM by Josh

Accessing the Outside World From Within a Value Converter

For a while now I've wanted to be able to access the Binding and DataContext which contains a value converter I've created. However, I've read several posts by the WPF team stating that they intentionally designed the value converter system so that within a value converter you cannot access those values. The justification is that it would make the code difficult to maintain, understand, and debug. I'm sure those are valid reasons, but I still wanted to do it anyways.

Fortunately, so did Rockford Lhokta. He posted to the WPF Forum asking how to go about accessing the Binding associated with a value converter, from within that converter. Sam Bent from Microsoft actually found a sneaky way to do it (and then mentioned that this was not supposed to be possible!). Thanks Sam! Here's the link... http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1127019&SiteID=1

posted January 19, 2007 7:57 AM by Josh

Free WPF Chart

A man by the name of John Stewien posted an article to the CodeProject which discusses his WPF chart control, known as the Swordfish chart. It has some very nice features (zooming, panning, etc.) and is 100% absolutely free (yay!). I only played around with it a bit, so I don't have too much to say about it in detail yet. If you're interested in checking it out, here's the link: http://www.codeproject.com/useritems/swordfishcharts.asp

posted January 10, 2007 6:34 PM by Josh

Custom TreeView Layout

I recently posted an article to the CodeProject which discusses how you can customize the layout of TreeViewItems.  Kevin Moore gave me a tip on how to do this during the online WPF chat event a few weeks ago, and then I brought the idea to completion.  Here's the link: http://www.codeproject.com/useritems/CustomTreeViewLayout.asp

Enjoy!

posted January 5, 2007 3:08 PM by Josh

A Very Easy Way to View and Reuse XAML

Today I was experimenting with modifying the control template of TreeViewItems. I wanted to customize their layout and the layout of their child items. I started tinkering with a custom ControlTemplate but was not seeing anything on the screen when I ran my test app. At that point I realized that it would be much easier to just take the default TreeViewItem control template and tweak it until I got the results I was looking for. Expression Blend has a nice feature which will give you a copy of a control’s template, but I don’t have Expression Blend on my laptop (I’m on vacation) and didn’t feel like downloading/installing it just to get the TreeViewItem template.

After a little head scratching I found a very easy way to get the template. In fact, this technique is so easy to use that I think I’ve officially added another tool to my WPF toolbox. The following line of code will put the TreeViewItem’s control template XAML onto the Clipboard (keep in mind, it could put a XAML representation of any object onto the Clipboard):

Clipboard.SetText( System.Windows.Markup.XamlWriter.Save( someTreeViewItem.Template ) );

The XamlWriter.Save method has several overloads. Most of them take an object to serialize to XAML and a stream to which the resultant XAML is written. The one I’m using only takes the object to be serialized, and then returns the XAML as a string. I then copy the XAML text to the Windows Clipboard, so that I can just paste the XAML into an XML editor.

How handy! I’ve used this little trick several times this morning and it already has made my life much easier. I love things like this!!

posted December 28, 2006 12:15 PM by Josh

Toronto Code Camp Presentation on WPF

I'm happy to announce that I'll be giving a presentation at the Toronto Code Camp 2007. I'll be discussing the importance of WPF, how to get started creating applications with it, and (obviously) showing some cool demo applications. If you are going to be in or near Toronto on March 31st 2007, drop by and check it out. There are going to be a bunch of other presentations about various technologies, so it should be an interesting and fun time. Here are the links:

You, Me and WPF  http://www.torontocodecamp.net/Sessions/Futures/tabid/62/Default.aspx

Toronto Code Camp 2007  http://www.torontocodecamp.net/Home/tabid/36/Default.aspx

See you there!

posted December 21, 2006 11:39 AM by Josh

Interactive 2D Elements in a 3D Scene

I'll keep this one short but sweet. Some crafty individuals at Microsoft published a way to use 2D controls in a 3D scene. This post on the WPF Forum has links to the relevant pages.

Now it is possible in v1 to have fully interactive 3D user interfaces. That is very cool!!

posted December 14, 2006 12:53 AM by Josh

Help Us Build Our Empire

I’m a senior consultant with Infusion Development, a consulting company based out of New York City. What really excites me about working here is that, in addition to being a top .Net shop on Wall Street, Infusion wants to become known as experts in the WPF space asap. Having me on-board with my unhealthy amount of WPF geekery helps, but we need other people with a hard-core passion for WPF to help us build our WPF Empire. So if you’re serious about WPF and are looking to be a well-paid consultant in NYC, send your CV to: ResumesViaJosh at InfusionDev dot com

Even if you’re not really into WPF yet, but have some .Net or Java skills and are looking to join a respected consulting firm with high-profile clients, send your CV to ResumesViaJosh at InfusionDev dot com because we’ve got many great opportunities exterior to the WPF space. And, over time as the market develops, we’ll be moving increasingly into the WPF space.

We are currently in the process of building our team of WPF experts and finding more projects which use WPF. As the market for WPF heats up, we will be working on a wide range of cutting edge projects for high-profile clients (many of them are the world’s biggest investment banks). So if you want to join me in becoming a WPF guru while working on exciting applications creating innovative UIs and data visualizations, let us know!

By the way, if don’t live near NYC but want to, Infusion has this deal where they will get you a place in Manhattan for a year (and pay for your moving expenses) to make the transition easier. You can find out more about it here.

posted December 12, 2006 8:31 PM by Josh

Highlighting Rows in a ListView (Revisited)

In my previous post, I demonstrated how to conditionally highlight rows in a ListView.  That post was an answer to a question on the WPF Forum and, since posting it, some other questions have surfaced regarding my implementation.  This blog entry will address those questions.

Someone on the forum wanted to know why I didn’t just use a DataTrigger or DataTemplateSelector to highlight the ListView rows. 

If the rows in the ListView should be highlighted when a row in the DataTable contains a certain (specific) value, then a DataTrigger could be used.  Many scenarios, however, require that a row be highlighted if a row contains a value in a certain range (such as "less than zero").  The DataTrigger falls short in that situation because its Value property cannot express a range.

Setting the ItemTemplateSelector on the ListView to some DataTemplateSelector would work for initially highlighting the rows.  But once the ListView's items have been created and stylized, how would you force them to update their color if a value in the DataTable changes?  The technique I presented in my previous blog entry, which uses the ItemContainerStyle property to apply row colors, allows us to "enforce" the row colors by re-applying the style whenever necessary.

My previous blog entry showed the Window’s XAML scattered about, which requires the reader to manually piece it together.  If I had a way to upload a sample project I would just do that, but since I can’t do that, here’s the Window’s XAML:

<Window x:Class="ListViewWithHighlightRows.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ListViewWithHighlightRows"
    Title="ListView With Highlighted Rows" Height="320" Width="300"
    FontSize="13"
    >
  <Window.Resources>
    <local:CustomerRowToBackgroundColorConverter x:Key="converter" HighlightBrush="Red" />
    <Style x:Key="ItemContStyle" TargetType="{x:Type ListViewItem}">
      <Setter Property="Background" Value="{Binding Converter={StaticResource converter}}" />
      <Setter Property="HorizontalContentAlignment" Value="Stretch" />
    </Style>
  </Window.Resources>
  <StackPanel>
    <ListView
      Name="listView"
      ItemContainerStyle="{StaticResource ItemContStyle}"
      ItemsSource="{Binding}"          
      >
      <ListView.View>
        <GridView>
          <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" />
          <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
          <GridViewColumn Header="Balance" Width="140">
            <GridViewColumn.CellTemplate>
              <DataTemplate>
                <TextBlock Text="{Binding Balance}" TextAlignment="Right" />
              </DataTemplate>
            </GridViewColumn.CellTemplate>
          </GridViewColumn>
        </GridView>
      </ListView.View>
    </ListView>

    <Button Click="OnButtonClick" Margin="4,10">Negate All Balances</Button>

    <TextBlock TextWrapping="Wrap" Margin="4">
      This sample demonstrates how to conditionally color rows
      in a ListView based on a value in the underlying
      data source.  The customers' whose
      balance is negative will have a red row.
      Click on the button to negate every balance,
      and see that the row colors update.
      Note: The selected row(s) will always use the
      default "selected row color."
    </TextBlock>
  </StackPanel>
</Window>

posted December 4, 2006 10:27 PM by Josh

Highlighting Rows in a ListView

This blog entry shows how to change the color of rows in a ListView, based on values in or associated with the rows.  The technique used here binds a ListView to an ADO.NET DataTable, and makes use of a custom value converter to determine what color each row should be.

Here’s a very common scenario: you have a DataTable which needs to be displayed in a ListView, and any row which contains a value in some certain range (say, less than zero) should be “highlighted” with a special color.  Perhaps the value which determines the row’s color is not even displayed in the ListView, but only exists in the DataTable.  How might one implement that functionality in WPF?

There are four steps involved with this task.

  1. Populate a DataTable and bind it to a ListView.
  2. Explain to the ListView how it should display the DataTable (i.e. specify where the items come from, setup the columns, etc.).
  3. Determine what color each row should be, based on values in the DataTable.
  4. When values in the DataTable change, update the color of a row if it either needs to be highlighted or no longer needs to be highlighted.

My demo app creates a simple DataTable, which contains a customer ID, name, and balance.  If the balance is less than zero then that customer’s row should be red, otherwise it should be the default color.

Step 1 - Populate a DataTable and bind it to a ListView.

First we must create the DataTable and set it as the ListView’s DataContext.  Note, all of the code shown in this post is contained within a Window-derived class.

void Window1_Loaded( object sender, RoutedEventArgs e )
{
 this.listView.DataContext = CreateDataTable();
}

DataTable CreateDataTable()
{
 DataTable tbl = new DataTable( "Customers" );

 tbl.Columns.Add( "ID", typeof( int ) );
 tbl.Columns.Add( "Name", typeof( string ) );
 tbl.Columns.Add( "Balance", typeof( decimal ) );

 tbl.Rows.Add( 1, "John Doe", 100.00m );
 tbl.Rows.Add( 2, "Jane Dorkenheimer", -209.00m );
 tbl.Rows.Add( 3, "Fred Porkroomio", 330.00m );
 tbl.Rows.Add( 4, "Mike Spike", 550.00m );   
 tbl.Rows.Add( 5, "Doris Yakovakovich", 8000.00m );
 tbl.Rows.Add( 6, "Boris Zinkwolf", -25.00m );

 tbl.RowChanged += OnDataTableRowChanged;

 return tbl;
}

There isn’t too much of interest here.  You might be wondering why the DataTable’s RowChanged event was hooked, but we’ll get back to that later (in Step 4).

Step 2 - Explain to the ListView how it should display the DataTable.

Now that the DataTable is ready and available to be displayed, let’s see how to show it in a ListView.

<ListView
  Name="listView"
  ItemContainerStyle="{StaticResource ItemContStyle}"
  ItemsSource="{Binding}"
  >
  <ListView.View>
    <GridView>
      <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" />
      <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
      <GridViewColumn Header="Balance" Width="140">
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding Balance}" TextAlignment="Right" />
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>
    </GridView>
  </ListView.View>
</ListView>

Since in the Window’s Loaded event handler we assigned a DataTable to the ListView’s DataContext, setting the ItemsSource property to {Binding} means to simply bind against the DataContext (i.e. the DataTable).  Each column displayed in the ListView is represented by a GridViewColumn.  The Balance column’s CellTemplate is set (as opposed to the DisplayMemberBinding) so that the monetary value can be right-aligned, which is typical for displaying numeric values.

Step 3 - Determine what color each row should be, based on values in the DataTable.

In the previous section, the ListView’s ItemContainerStyle was set to a Style whose Key is ‘ItemContStyle’.  That Style is defined like this:

<Style x:Key="ItemContStyle" TargetType="{x:Type ListViewItem}">
  <Setter Property="Background" Value="{Binding Converter={StaticResource converter}}" />
  <Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>

The Style sets two properties on each ListViewItem: Background and HorizontalContentAlignment.  The latter property is set to ‘Stretch’ so that the elements in the ListView’s cells will occupy the entire surface area of those cells.  That allows us to right-align the text in the Balance column.

The Background property of each ListViewItem is bound by the first Setter in the Style.  That binding passes each row in the DataTable through a value converter, which returns the Brush to be used to paint the corresponding row/item in the ListView.  Let’s take a look at how that value converter works:

[ValueConversion( typeof(DataRowView), typeof(Brush) )]
public class CustomerRowToBackgroundBrushConverter : IValueConverter
{
 private Brush highlightBrush;
 public Brush HighlightBrush
 {
  get { return highlightBrush; }
  set { highlightBrush = value; }
 }

 public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
 {   
  // The value to convert should be a DataRowView.
  DataRowView rowView = value as DataRowView;
  Debug.Assert( rowView != null, "'value' should be a DataRowView" );

  // This is the Brush we will return to paint the customer's row.
  Brush itemBrush = Brushes.Transparent;

  object rawValue = rowView.Row["Balance"];
  if( rawValue != null && rawValue != DBNull.Value )
  {
   // We have a valid value, so cast it to a decimal
   // and check if the customer is owed any money.
   decimal balance = (decimal)rawValue;
   if( balance < 0.0m )
    itemBrush = this.HighlightBrush;
  }

  return itemBrush;
 }

 public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
 {
  throw new NotSupportedException( "ConvertBack not supported" );
 }
}

We then define an instance of that class in XAML, so that it can be referenced by the first Setter in the ‘ItemContStyle’ style.  This line of XAML appears just prior to the Style seen previously:

<local:CustomerRowToBackgroundBrushConverter x:Key="converter" HighlightBrush="Red" />

Notice that the brush used to highlight rows is specified in XAML and not hard-coded in the converter itself.  That makes the class more reusable.

Step 4 - Update the row colors.

After the ListView loads and displays its items, the Style we applied to the ListViewItems will no longer be consulted to determine what color the items should be.  But, it’s possible that as the application is running a customer’s balance could be modified from a negative value to a positive value, or vice versa.  In that situation we must change the color of that customer’s row.

If you look back to Step 1, you’ll see that after we populated the DataTable we hooked its RowChanged event.  That event fires when any value in a DataRow has been altered in any way.  We can use that event to let us know when the ListView needs to re-apply the ‘ItemContStyle’ style to all of its items.  I’m not thrilled about this approach, but I don’t know of any better way to do this:

void OnDataTableRowChanged( object sender, DataRowChangeEventArgs e )
{
 // A value in the DataTable has been altered, so
 // re-apply the ItemContainerStyle.  I hope that
 // there is a more elegant way of "enforcing" the
 // style, but I don't know of any other way.
 Style style = this.listView.ItemContainerStyle;
 this.listView.ItemContainerStyle = null;
 this.listView.ItemContainerStyle = style;
}

In case you’re interested, here’s the code which responds to the Button’s Click event (not seen in the XAML).  It negates all of the customer balances.

void OnButtonClick( object sender, RoutedEventArgs e )
{
 DataTable tbl = this.listView.DataContext as DataTable;

 // Flip the customer balances from positive to
 // negative, or vice versa.  Note: no real application
 // should ever provide a button with this functionality!! :)
 foreach( DataRow row in tbl.Rows )
 {
  decimal balance = (decimal)row["Balance"];
  row["Balance"] = balance * -1;
 }
}

For a more in-depth overview of binding a ListView to ADO.NET data structures, be sure to read what Beatriz Costa had to say on that topic.

posted December 3, 2006 8:07 PM by Josh

Drawing a Line Between Movable Elements

This blog entry shows how to draw a line which connects two visual elements, as well as how to automatically update the start and end points of that line when those visual elements move.  The technique only applies to when the elements are contained within a Canvas.  This code was inspired by a question posted to my article about the DragCanvas on CodeProject.

Let’s suppose that you have some elements in a DragCanvas, and you want some of the elements to be “connected” to each other with thin lines.  Not only should the elements be connected, but when they are dragged around the connecting lines should automatically “follow” them. Below is a very simplistic example of a green Ellipse and an Image in a DragCanvas, connected by a red Line:

After dragging the Image and Ellipse around a bit, the UI should look something like this:

Notice how the red line “moved” along with the other two elements, such that it consistently is aligned with the center of each element.  That is the functionality this blog entry shows how to implement.  Now without further adieu…

There are two fundamental problems to overcome in this scenario. 

First, we need a way to allow the user to drag elements around the screen.  That’s the easy part, since my DragCanvas class does exactly that.  If you don’t already have the DragCanvas, feel free to download it (and lots of other goodies) from here.

Second, we need a way to draw a line between two elements in a DragCanvas.  My requirement was that the line should always extend from the center of one element to the center of the other element.  Since an element has no notion of where its “center point” is, we’ll have to provide that logic and then bind a Line element’s start and end points to those center points.

I decided to provide the notion of an element’s center point by subclassing ContentControl and surfacing two read-only dependency properties; CenterX and CenterY.  I named that class CenteredContentControl.  Since it is a ContentControl you can assign whatever you want to its Content property, so there is no restriction regarding what type of elements can be connected by a Line.  What are really being connected by a Line are instances of CenteredContentControl, not the objects they contain.

When the Canvas’s Left or Top attached properties are modified on a CenteredContentControl, the CenterX or CenterY property is modified so that the Line can update it’s start or end point.

Let’s review the code for this class:

namespace WPF.JoshSmith.Controls
{
  public class CenteredContentControl : ContentControl
  {
    // There’s more to come…
  }
}

The two read-only dependency properties are defined like this:

private static readonly DependencyPropertyKey CenterXPropertyKey =
 DependencyProperty.RegisterReadOnly(
 "CenterX",
 typeof( double ),
 typeof( CenteredContentControl ),
 new UIPropertyMetadata() );

private static readonly DependencyPropertyKey CenterYPropertyKey =
 DependencyProperty.RegisterReadOnly(
 "CenterY",
 typeof( double ),
 typeof( CenteredContentControl ),
 new UIPropertyMetadata() );

public static readonly DependencyProperty CenterXProperty = CenterXPropertyKey.DependencyProperty;
public static readonly DependencyProperty CenterYProperty = CenterYPropertyKey.DependencyProperty;

public double CenterX
{
 get { return (double)GetValue( CenterXProperty ); }
}

public double CenterY
{
 get { return (double)GetValue( CenterYProperty ); }
}

Next we have the logic which updates CenterX and CenterY:

protected override void OnPropertyChanged( DependencyPropertyChangedEventArgs e )
{
 base.OnPropertyChanged( e );

 bool sizeChanged =
  e.Property.Name == "ActualWidth" ||
  e.Property.Name == "ActualHeight";

 // If the property that changed does not affect the element's size
 // and it is not an attached property of Canvas, then there is
 // nothing to do.
 if( !sizeChanged &&
  !typeof( Canvas ).IsAssignableFrom( e.Property.OwnerType ) )
  return;

 if( e.Property.Name == "Left" || e.Property.Name == "ActualWidth" )
 {
  this.UpdateCenterX();
 }
 else if( e.Property.Name == "Top" || e.Property.Name == "ActualHeight" )
 {
  this.UpdateCenterY();
 }
}

void UpdateCenterX()
{
 double left = Canvas.GetLeft( this );
 double offset = left + this.ActualWidth / 2;
 this.SetValue( CenterXPropertyKey, offset );
}

void UpdateCenterY()
{
 double top = Canvas.GetTop( this );
 double offset = top + this.ActualHeight / 2;
 this.SetValue( CenterYPropertyKey, offset );
}

Lastly, the instance constructor:

public CenteredContentControl()
{
 // When the element first loads, initialize
 // the two dependency properties.
 this.Loaded += delegate
 {
  this.UpdateCenterX();
  this.UpdateCenterY();
 };
}

Now that we have an element which exposes a center point, and notifies us when that point changes, connecting two instances of that class with a Line is a breeze.  Observe…

<Window x:Class="BoundLine.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:jas="clr-namespace:WPF.JoshSmith.Controls"
    Title="Connect the Dots" Height="400" Width="400" 
    >
  <jas:DragCanvas>
    <Line
      jas:DragCanvas.CanBeDragged="False"
      Stroke="Red"
      StrokeThickness="3"
      X1="{Binding ElementName=host1, Path=CenterX}"
      Y1="{Binding ElementName=host1, Path=CenterY}"
      X2="{Binding ElementName=host2, Path=CenterX}" 
      Y2="{Binding ElementName=host2, Path=CenterY}"
     
      />

    <jas:CenteredContentControl x:Name="host1" Canvas.Left="100" Canvas.Top="100">
      <Ellipse Fill="Green" Width="50" Height="50" />
    </jas:CenteredContentControl>

    <jas:CenteredContentControl x:Name="host2" Canvas.Left="200" Canvas.Top="200">
      <Image Source="/Images/harpsichord.jpg" Width="150" Height="150" />
    </jas:CenteredContentControl>
  </jas:DragCanvas>
</Window>

I put the CenteredContentControl into the WPF.JoshSmith library, so you won’t need to bother pasting that code into a new file.

posted November 28, 2006 10:29 PM by Josh

Accessing a Data Object From a Template-Generated Context Menu

This blog entry answers a question posted to the WPF Forum.  It shows how to access a TreeViewItem’s underlying data object when the user clicks on a MenuItem in a ContextMenu generated by the item’s template.  The solution, however, is not only applicable to TreeViewItems.  It will work with any templated data object used anywhere in a WPF user interface.

The scenario is an interesting, yet not unusual, one.  A class you created has a typed data template associated with it.  That data template includes a ContextMenu which will be created when the template is expanded.  Basically, every instance of that class displayed in the UI will have a right-click popup menu available.  That ContextMenu has a MenuItem whose Clicked event handling method needs to access some property of the data object associated with the TreeViewItem which was right-clicked.  The question is, how do you get from to the underlying data object in the MenuItem’s Clicked event handling method?

The answer (well, at least the one I present here) is quite simple.  Just bind the MenuItem’s Tag property to the data object and access the Tag property in the Click event handling method.  The Tag property can be bound in the typed data template via the TemplateBinding markup extension.  Since the visual tree of every generated item in an ItemsControl uses a ContentPresenter to actually display the elements of the expanded data template, we can bind the Tag property to the Content property of that ContentPresenter.  The Content property references the data object which is being "presented" by the ContentPresenter.

Now let’s see a demo.  In this demo we will be binding to my custom Foo class (patent pending).  Here’s another Foo for you:

public class Foo
{
 private List<Foo> children;
 private int id;
 private string name;

 private static int prevId = 0;

 public Foo( string name )
 {
  this.id = ++Foo.prevId;
  this.name = name;
 }

 public List<Foo> Children
 {
  get
  {
   if( this.children == null )
    this.children = new List<Foo>();

   return children;
  }
 }

 public int Id
 {
  get { return id; }
 }

 public string Name
 {
  get { return name; }
 }
}

Now let’s see the XAML for a Window which contains a TreeView that will display a bunch of Foo objects:

<Window x:Class="TreeViewItemsWithContextMenu.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TreeViewItemsWithContextMenu"
    Title="TreeViewItemsWithContextMenu" Height="300" Width="315"
    FontSize="13"
    >
  <Window.Resources>
    <HierarchicalDataTemplate                  
      DataType="{x:Type local:Foo}"
      ItemsSource="{Binding Children}"
      >
      <Border BorderBrush="Gray" BorderThickness="1" CornerRadius="4" Padding="3" Margin="0,3">
        <TextBlock>
          <Run FontWeight="Bold">Name: </Run>
          <TextBlock Text="{Binding Path=Name}" />
        </TextBlock>
        <Border.ContextMenu>
          <ContextMenu>
            <!-- Binding the MenuItem's Tag to the Content property of the underlying ContentPresenter
                 allows us to access the Foo object associated with the TreeViewItem that was right-clicked. -->
            <MenuItem Header="Show me your ID" Click="OnShowMeYourIDClick" Tag="{TemplateBinding Content}" />
          </ContextMenu>
        </Border.ContextMenu>
      </Border>
    </HierarchicalDataTemplate>
  </Window.Resources>
  <Grid>
    <TreeView Name="treeView" />
  </Grid>
</Window>

The HierarchicalDataTemplate is “typed” because it’s DataType property is set, and it does not have a Key specified.  This means that it will be the default template used to create a visual representation of a Foo object.  The real magic is where the MenuItem’s Tag property is bound to the Content property of the implicit ContentPresenter.  Let’s see that again, this time in slow motion…

<MenuItem Header="Show me your ID" Click="OnShowMeYourIDClick" Tag="{TemplateBinding Content}" />

Now we’re ready to see how the code-behind makes use of the fact that each MenuItem’s Tag is bound to the templated Foo object.

public partial class Window1 : System.Windows.Window
{
 public Window1()
 {
  InitializeComponent();
  this.Loaded += Window1_Loaded;
 }

 void Window1_Loaded( object sender, RoutedEventArgs e )
 {
  this.treeView.ItemsSource = CreateFooFamily();   
 }

 void OnShowMeYourIDClick( object sender, RoutedEventArgs e )
 {
  MenuItem menuItem = sender as MenuItem;
  Foo foo = menuItem.Tag as Foo;
  MessageBox.Show( foo.Id.ToString() );
 }

 static List<Foo> CreateFooFamily()
 {
  Foo granddaddyFoo = new Foo( "Old Man Foo" );

  Foo someFoo = new Foo( "Foo Man Chu" );
  someFoo.Children.Add( new Foo( "Foobar Jr." ) );
  granddaddyFoo.Children.Add( someFoo );
     
  someFoo = new Foo( "Foodoo The Great" );
  granddaddyFoo.Children.Add( someFoo );

  List<Foo> foos = new List<Foo>();
  foos.Add( granddaddyFoo );
  return foos;
 }
}

In the OnShowMeYourIDClick method the ‘sender’ argument is the MenuItem that was clicked, so we can cast it to a MenuItem and then cast its Tag to a Foo.  Once we have a reference to that Foo object, we can access whatever properties we need.  In this case, we just show its ID in a MessageBox.

posted November 25, 2006 11:23 PM by Josh

Binding a TreeView to a DataSet

This blog entry demonstrates the fundamentals of binding a WPF TreeView to a DataSet with two related DataTables.  The technique presented herein could easily be extended to fit more sophisticated requirements, such as binding to more than two tables.

Many applications need to display hierarchical data in a TreeView, and often that data is retrieved from a database.  In many situations the developer just needs to bind the TreeView directly to the DataSet which was populated with database data; creating custom domain objects and collections of those objects can be overkill sometimes.  If you are currently in that situation, rest assured that it is actually fairly trivial to do this in WPF. :)

The basic gist of the solution is to bind the top level of TreeViewItems against the master DataTable, and then bind against DataRelations for any descendants of the root nodes.  You need to use a HierarchicalDataTemplate for every non-leaf level of nodes, in other words, only the very lowest DataTable in the hierarchy is displayed with a non-hierarchical DataTemplate.

Let’s just get right into an example.  Here is a class which creates a DataSet with two related DataTables:

public static class DataSetCreator
{
 public static DataSet CreateDataSet()
 {
  DataSet ds = new DataSet();

  // Create the parent table.
  DataTable tbl = new DataTable( "Master" );
  tbl.Columns.Add( "ID", typeof( int ) );
  tbl.Columns.Add( "Name" );

  for( int i = 0; i < 3; ++i )
  {
   DataRow row = tbl.NewRow();
   row["ID"] = i;
   row["Name"] = "Master #" + i;
   tbl.Rows.Add( row );
  }

  ds.Tables.Add( tbl );

  // Create the child table.
  tbl = new DataTable( "Detail" );
  tbl.Columns.Add( "MasterID", typeof( int ) );
  tbl.Columns.Add( "Info" );

  for( int i = 0; i < 9; ++i )
  {
   DataRow row = tbl.NewRow();
   row["MasterID"] = i % 3;
   row["Info"] = "Detail Info #" + (i / 3) + " for Master #" + (i % 3);
   tbl.Rows.Add( row );
  }

  ds.Tables.Add( tbl );

  // Associate the ID and MasterID columns between the tables.
  ds.Relations.Add(
   "Master2Detail",
   ds.Tables["Master"].Columns["ID"],
   ds.Tables["Detail"].Columns["MasterID"] );

  return ds;
 }
}

The resultant DataSet has two DataTables (‘Master’ and ‘Detail’) and one DataRelation (‘Master2Detail’).  We want a TreeView to display the Master rows as top-level nodes and the Detail rows as children of their respective parent node.  Here is the XAML for a Window which contains a TreeView configured to load and display that data:

<Window x:Class="BindingTreeViewToDataSet.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:BindingTreeViewToDataSet"
    Title="Binding TreeView To DataSet" Height="300" Width="300"
    FontSize="13"
    >
  <Window.Resources>
    <!-- Creates a DataSet with two related DataTables. -->
    <ObjectDataProvider
      x:Key="dataSetProvider"
      MethodName="CreateDataSet"
      ObjectType="{x:Type local:DataSetCreator}"
      />

    <!-- Displays a row in the 'Detail' table (i.e. the child table). -->
    <DataTemplate x:Key="DetailTemplate">
      <TextBlock Text="{Binding Info}" />
    </DataTemplate>

    <!-- Displays a row in the 'Master' table (i.e. the parent table).
         Pulls it's child items from the 'Master2Detail' DataRelation
         in the DataSet.  Each child row is displayed via the 'DetailTemplate'. -->
    <HierarchicalDataTemplate
      x:Key="MasterTemplate"
      ItemsSource="{Binding Master2Detail}"
      ItemTemplate="{StaticResource DetailTemplate}"
      >
      <TextBlock Text="{Binding Name}" />
    </HierarchicalDataTemplate>   
  </Window.Resources>
 
  <Grid>
    <TreeView
      DataContext="{StaticResource dataSetProvider}"
      ItemsSource="{Binding Master}"
      ItemTemplate="{StaticResource MasterTemplate}"
      />
  </Grid>
</Window>

If you had, say, three related tables (Master --> Detail --> DetailInfo) then you could have the ‘DetailTemplate’ be a HierarchicalDataTemplate whose ItemsSource was bound to the DataRelation between ‘Detail’ and ‘DetailInfo,’ and the ItemTemplate a DataTemplate which displays the pertinent information in that table.

This code was compiled and tested against the .NET Framework 3.0 RTM.

posted November 21, 2006 10:37 PM by Josh

Dependency Properties in Depth

I recently posted a question to the WPF Forum which asked for someone to give me a really in-depth explanation of the dependency property system.  I've read all of the documentation and blog entries I could find about DPs, but I just never felt like I understood them from the perspective of the WPF framework.  I want to really understand dependency properties, not just know about what they are and how to use them.

Amazingly, Rob Relyea (a WPF Program Manager) arranged to have someone from the WPF team who worked on dependency properties answer my question!  Here's the low-down on DPs by Wolf Schmidt: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=928216&SiteID=1&mode=1

posted November 15, 2006 8:02 PM by Josh

Introducing: Josh Smith on WF

I am pleased to announce that I have started another blog, "Josh Smith on WF", which can be found here: http://blogs.wdevs.com/joshsmithonwf/

That blog will be where I explore the depths of Windows Workflow Foundation, another pillar of the .NET 3.0 Framework.  I hope to be able to continue sharing what I learn about .NET 3.0 as I learn it, but no longer will I be focusing exclusively on WPF.  If you enjoy my WPF blog, have no fear, I intend to continue posting to it as well, just perhaps not as often.  If you haven't looked into WF yet, I highly recommend you check it out.  It is as intellectually intoxicating and sophisticated as WPF, only in a very different way.  Plus, I think you can use it to make programs, or something like that... ;)

posted November 15, 2006 7:25 PM by Josh

World Domination via WPF, and more!

My secret plan to take over the world by the sheer force of my WPF geekery has begun to take effect.  Muahahaha!!!

Below is a screenshot from my WPF.JoshSmith CodeProject article.  Those of you who have been following my journey through the wonderful world of WPF will realize that all of the articles listed in the "Other Popular WPF Articles" section are WPF articles that I wrote.  My reign has begun!  Any day now my army of WPF warrior robots should be emerging from the underground caves to begin executing the TakeOverTheWorld workflow... ;)

I just found this so amusing that I had to share it:

Truth be told, CodeProject is in the midst of restructuring their .NET 3.0 articles, and I guess mine are currently the only WPF articles in the new "Windows Presentation Foundation" category.  That is why only my articles are showing up.  It doesn't seem as grandiose when you see it that way, does it? :)  In fact, it's just downright lonely...

By the way, Marc Clifton has been kind enough to grace my new "Hello, WF!" article with his perspective/wisdom on WF and workflows in general.  I highly recommend reading what he has to say about it here and here.

posted November 14, 2006 7:49 PM by Josh

A Quick Detour into the Exotic Landscape of WF

I have spent most of my free time in the past several months working exclusively with WPF.  Recently I decided to diverge from that pursuit for a while and, instead, learn about Windows Workflow Foundation (WF).  I have not learned enough about WF yet to claim any kind of expertise or even comfort with it, but I find it fascinating and can see real potential in it as a programming platform.  Since the best way to learn something is to teach it, I decided to write an article about the basics of WF and post it to the CodeProject.  Here's the link: http://www.codeproject.com/useritems/HelloWF.asp 

Enjoy!

posted November 12, 2006 9:24 PM by Josh

Powered by Community Server, by Telligent Systems