Bind to Method within a DataTemplate

June 12th, 2007 by ryan Leave a reply »

One of my play projects with WPF is a photo viewer for the pictures we put out on http://cromwellhaus.com.  One of the views is a montage of the latest photos with a random RotateTransform Angle applied to each image’s RenderTransform.  Getting the view itself set up as cake, but when I attempted to apply the random angle to each image, it wasn’t so random.

Here’s what happened and how to actually accomplish such a task:

I started out with an ItemsControl, rather than a ListView as most examples show, using a UniformGrid as the ItemsPanel.

<ItemsControl ItemTemplate="{StaticResource photoItem}" ItemsSource="{Binding Source={StaticResource dpPhotos}, XPath=/rss/channel/item}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <UniformGrid HorizontalAlignment="Stretch"  VerticalAlignment="Stretch" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>

I bound the ItemsSource to an XmlDataProvider pointing to the photos RSS feed for Ryan Jr. pictures

<XmlDataProvider x:Key="dpPhotos" Source="http://cromwellhaus.com/photos/baby/pictures_rss.aspx?Tags=Ryan+Jr&amp;AndTags=1" XmlNamespaceManager="{StaticResource rssMapping}"/>

I run our site using Community Server 2007 Express Edition which provides RSS meta extensions for more detailed feeds.  This required that I apply an XmlNamespaceManager to define these extension namespaces.  That’s what this is for:

<XmlNamespaceMappingCollection x:Key="rssMapping">
  <XmlNamespaceMapping Uri="http://search.yahoo.com/mrss" Prefix="media" />
</XmlNamespaceMappingCollection>

Here is the DataTemplate used by the ItemsControl to display/render each image.  You’ll see this referred to in above as {StaticResource photoItem}:

<DataTemplate x:Key="photoItem">
  <Image Margin="12,12,12,12" Source="{Binding Mode=Default, XPath=media:thumbnail/@url}" ToolTip="{Binding XPath=title}" Width="{Binding Mode=Default, XPath=media:thumbnail/@width}" Height="{Binding Mode=Default, XPath=media:thumbnail/@height}" />
</DataTemplate>

You can see the use of the XmlNamespaceMapping in the XPath=media:thumbnail/@url.  That had me stumped for a few seconds, but opening the project up in Expression Blend and re-adding the XmlDataProvider created the Mappings for me.  (Sidebar: VS 2008 does do this for you – thank goodness)

At this point, we are displaying thumbnails and we are certainly aware that I make poor color choices, but we’re on our way.

Almost there...

Applying the RotateTransform is easy…

<Image Margin="12,12,12,12" Source="{Binding Mode=Default, XPath=media:thumbnail/@url}" ToolTip="{Binding XPath=title}" Width="{Binding Mode=Default, XPath=media:thumbnail/@width}" Height="{Binding Mode=Default, XPath=media:thumbnail/@height}">
  <Image.RenderTransform>
    <RotateTransform Angle="10" />
  </Image.RenderTransform>
</Image>

…and it’s almost as easy to use the System.Random class to generate the angle.  Just add the following ObjectDataProvider as a Window or Application Resource and bind the Angle property to it:

<ObjectDataProvider x:Key="randomAngle" ObjectType="{x:Type system:Random}" MethodName="Next">
  <ObjectDataProvider.MethodParameters>
    <system:Int32>-12</system:Int32>
    <system:Int32>12</system:Int32>
  </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

...
<RotateTransform Angle="{Binding Source={StaticResource randomAngle}" />

Well when you do this, you’ll find that you get the same Angle for every image.  This is because you are actually binding to a single instance of the ObjectDataProvider.  To resolve this we actually have to embed the ODP in the transform itself as so:

<RotateTransform>
  <RotateTransform.Angle>
    <Binding>
      <Binding.Source>
        <ObjectDataProvider ObjectType="{x:Type system:Random}" MethodName="Next">
          <ObjectDataProvider.MethodParameters>
            <system:Int32>-12</system:Int32>
            <system:Int32>12</system:Int32>
          </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
      </Binding.Source>
    </Binding>
  </RotateTransform.Angle>
</RotateTransform>

Tada!image

Now you will find that your angle’s aren’t terribly “Random”.  This is because you are actually asking for a new instance of the Random class each time.  You can tell the ODP within the DataTemplate to use the same instance each time by adding this to the Window Resources:

<ObjectDataProvider x:Key="randomAngle" ObjectType="{x:Type system:Random}"/>

and modifying the Angle binding to the following, telling the transform ODP to use a specific resource instance rather than just giving it a type to instantiate each time.

<ObjectDataProvider ObjectInstance="{StaticResource randomAngle}" MethodName="Next">
  <ObjectDataProvider.MethodParameters>
    <system:Int32>-12</system:Int32>
    <system:Int32>12</system:Int32>
  </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

You can download a mini-version used to write this post here.

Advertisement

2 comments

  1. Jon says:

    I think your program could really use a tab navigation with a subnavigation beneath it. And maybe use the colors red and blue all over the place.

    Just a suggestion…

  2. Ryan says:

    That had been my initial thought. I’m glad I’m not alone in it.

Leave a Reply