21

I want to do the following at runtime in code:

<DataTemplate x:Key="lightGreenRectangle">
        <Rectangle Fill="LightGreen"/>
    </DataTemplate>

So far I've got:

public DataTemplate GetColouredRectangleInDataTemplate(Color colour)
{
    DataTemplate dataTemplate = new dataTemplate();

    return dataTemplate;
}

Help? I know this isn't the most elegant way of styling a control, but the component I want to specify a colour for has a property called "PointTemplate" of type DataTemplate.

Corpsekicker
  • 3,276
  • 6
  • 25
  • 34
  • 1
    If you want to style a control you should use a ControlTemplate, Datatemplate is to display data in a specific way. Come back to you questions, what do you want to set the colour for? – MBen Dec 08 '11 at 08:50
  • does something like this work for you? DataTemplate asd = new DataTemplate(); asd.DataType = typeof(Rectangle); asd.VisualTree.SetValue(Rectangle.FillProperty, Brushes.Green); – peer Dec 08 '11 at 09:52
  • Please note that while these answers were correct at the time, the current recommended way to programmatically create a template is to load XAML from a string or a memory stream using the `Load` method of the `XamlReader` class. – Sheridan Mar 22 '18 at 15:07

2 Answers2

29

If for whatever reason you need to create a DataTemplate programmatically you would do:

XAML:

<Grid x:Name="myGrid">
    <ContentControl ContentTemplate="{DynamicResource lightGreenRectangle}" />
</Grid>

Somewhere in your code:

    public static DataTemplate CreateRectangleDataTemplate()
    {
        var rectangleFactory = new FrameworkElementFactory(typeof(Rectangle));
        rectangleFactory.SetValue(Shape.FillProperty, new SolidColorBrush(System.Windows.Media.Colors.LightGreen));

        return new DataTemplate
                   {
                       VisualTree = rectangleFactory,
                   };
    }

    public static void AddRectangleTemplateToResources(FrameworkElement element)
    {
        element.Resources.Add("lightGreenRectangle", CreateRectangleDataTemplate());
    }

Then you just need to add the DataTemplate to a ResourceDictionary so it can be used. For example, in the code behind:

    public MainWindow()
    {
        InitializeComponent();
        AddRectangleTemplateToResources(myGrid);
    }

Hope this helps!

Cœur
  • 37,241
  • 25
  • 195
  • 267
FunnyItWorkedLastTime
  • 3,225
  • 1
  • 22
  • 20
  • Is this possible for silverlight? – Shaun Rowan May 31 '12 at 16:23
  • Nevermind, found: http://blogs.msdn.com/b/scmorris/archive/2008/04/14/defining-silverlight-datagrid-columns-at-runtime.aspx – Shaun Rowan May 31 '12 at 17:03
  • gets me this error: `Cannot find resource named 'lightGreenRectangle'. Resource names are case sensitive.` – Mohsen Aug 17 '13 at 10:35
  • Needed to add `rectangleFactory.Name = "lightGreenRectangle";`, I believe. Added an edit to put it in there. – vapcguy Sep 12 '16 at 18:10
  • Please note that the preferred way to programmatically generate `DataTemplate`s now is to parse XAML directly from a string: `DataTemplate template = XamlReader.Parse(xamlString, parserContext) as DataTemplate;` – Sheridan Feb 16 '18 at 11:09
10

Using the following helper class:

/// <summary>
/// Class that helps the creation of control and data templates by using delegates.
/// </summary>
public static class TemplateGenerator
{
  private sealed class _TemplateGeneratorControl:
    ContentControl
  {
    internal static readonly DependencyProperty FactoryProperty = DependencyProperty.Register("Factory", typeof(Func<object>), typeof(_TemplateGeneratorControl), new PropertyMetadata(null, _FactoryChanged));

    private static void _FactoryChanged(DependencyObject instance, DependencyPropertyChangedEventArgs args)
    {
      var control = (_TemplateGeneratorControl)instance;
      var factory = (Func<object>)args.NewValue;
      control.Content = factory();
    }
  }

  /// <summary>
  /// Creates a data-template that uses the given delegate to create new instances.
  /// </summary>
  public static DataTemplate CreateDataTemplate(Func<object> factory)
  {
    if (factory == null)
      throw new ArgumentNullException("factory");

    var frameworkElementFactory = new FrameworkElementFactory(typeof(_TemplateGeneratorControl));
    frameworkElementFactory.SetValue(_TemplateGeneratorControl.FactoryProperty, factory);

    var dataTemplate = new DataTemplate(typeof(DependencyObject));
    dataTemplate.VisualTree = frameworkElementFactory;
    return dataTemplate;
  }

  /// <summary>
  /// Creates a control-template that uses the given delegate to create new instances.
  /// </summary>
  public static ControlTemplate CreateControlTemplate(Type controlType, Func<object> factory)
  {
    if (controlType == null)
      throw new ArgumentNullException("controlType");

    if (factory == null)
      throw new ArgumentNullException("factory");

    var frameworkElementFactory = new FrameworkElementFactory(typeof(_TemplateGeneratorControl));
    frameworkElementFactory.SetValue(_TemplateGeneratorControl.FactoryProperty, factory);

    var controlTemplate = new ControlTemplate(controlType);
    controlTemplate.VisualTree = frameworkElementFactory;
    return controlTemplate;
  }
}

You can create a data-template like this:

DataTemplate template = 
  TemplateGenerator.CreateDataTemplate
  (
    () =>
    {
      var result = new TextBox()
      result.SetBinding(TextBox.TextProperty, "BindingPathHere");
      return result;
    }
  );

You are free to use any code to create the control as you will do if you were creating the control directly, without any data-template. For more info, see this tip in code project: http://www.codeproject.com/Tips/808808/Create-Data-and-Control-Templates-using-Delegates

Paulo Zemek
  • 447
  • 5
  • 5