Worked Example: Programmatic Addition of a WPF Data Template to a Resource Dictionary

I have noticed some interest in my earlier post on how to add a WPF data template to a resource dictionary in code and so this post is a demonstration of the technique.  The code is presented in F# but I have deliberately kept the code simple and fairly non-functional so that those of a C# or VB.NET background can follow the process easily.

At the end of my previous post (Programmatic Addition of a WPF Data Template to a Resource Dictionary), I recommended that the XamlReader class was used to create data templates.  Using the FrameworkElementFactory class is now deprecated.

The following code is a very simple example of the recommended process:

  • Create a window containing a StackPanel and a ListBox
  • Add a list of strings to the ListBox
  • Control the display of the ListBox using a dynamically added data template

Here is the code (for the F# interpreter):

// Reference required assemblies
#r "System.Xml"
#r "WindowsBase"
#r "PresentationCore"
#r "PresentationFramework"

// Open required namespaces
open System.Xml
open System.IO
open System.Windows
open System.Windows.Controls
open System.Windows.Markup

// Load an object from a XAML string
let loadFromString xaml =
  use sr = new StringReader(xaml)
  use xr = XmlReader.Create(sr)
  XamlReader.Load xr

// Create a data template resource pair
let buildDataTemplate<'T> xaml =
  (new DataTemplateKey(typeof<'T>),
   (xaml |> loadFromString :?> FrameworkTemplate))

// XAML for the data template
let myDataTemplateXaml =
  @"<DataTemplate
    xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
    xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
    xmlns:m=""clr-namespace:System;assembly=mscorlib""
    DataType=""{x:Type m:String}""
    ><TextBox Text=""{Binding Mode=OneWay}""/></DataTemplate>"

// Create a list box to hold the templated items
let lb = new ListBox()

// Add the data template
do    myDataTemplateXaml
   |> buildDataTemplate<System.String>
   |> lb.Resources.Add 

do lb.ItemsSource <- &#91;"Item 1" ; "Item 2" ; "Item 3"&#93;

// Build the window
let wnd = new Window()
let sp = new StackPanel()
do (sp:>IAddChild).AddChild lb
do wnd.Content <- sp

// Show the window and run the application
// Note: the F# Interpreter must be reset
// between executions because of the
// behaviour of Application
do    (new Application()).Run(wnd)
   |> ignore

I have chosen to use System.String as a very simple example but this method works for referencing any assembly available to your application. The Binding element is OneWay because it is bound to a constant string, but this is not generally required.

5 thoughts on “Worked Example: Programmatic Addition of a WPF Data Template to a Resource Dictionary

  1. Pingback: Rick Minerich's Development Wonderland : F# Discoveries This Week 08/31/2009

  2. Matt

    Hi Steve,

    I love this example, but I’m having a hard time getting it to work with new types specified in F# Interpreter (i.e. not in any assembly). Is there a namespace for the FSI that I can’t find? Or another way around this?

    Much appreciated.

    Matt

    Reply
    1. Steve Horsfield Post author

      Hi Matt,

      I am not sure you will be able to make it work (certainly not easily) because of the way FSI creates classes. In particular, you cannot use namespaces directly. Instead, FSI creates namespaces implicitly. Here is a very simple test using a clean FSI environment:

      let docClass o = printf "%s (%s)\n" (o.GetType().FullName) (o.GetType().Assembly.FullName);;

      type C() = member s.m() = ();;

      module X = type D() = member s.m() = ();;

      docClass (new C()) ; docClass (new X.D()) ;;

      In my case, the response is:

      FSI_0002+C (FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
      FSI_0009+X+D (FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)

      Further investigation shows that FSI creates a dynamic assembly that does not support the Assembly CodeBase property and so you are quite limited in how you can reference it.

      I hope that helps a little. Let me know if I have misunderstood your problem.

      Steve

      Reply
    1. Steve Horsfield Post author

      Glad to help, although I am not sure I did! You may be able to get around this by using your own dynamic assembly but you will probably need to wait until code generation is fully part of the .NET libraries — Microsoft are planning this, but it may be .NET 5.0 (or later!).

      Reply

Leave a reply to Steve Horsfield Cancel reply