30

How do i add controls to datatemplates programmatically?

For Example. Below I've created TextBlock and DataTemplate.

TextBlock text = new TextBlock();
DataTemplate template = new DataTemplate();

Now I need to add TextBlock to DataTemplate. How to achieve this?

I know that there are other ways of addind data template in code behind 1. create a data template in XAML and load it on code behind 2. create and add using XamlParser

but i need to do in the way i showed in example.

Need some help.

nalka
  • 1,894
  • 11
  • 26
MYRAO
  • 301
  • 1
  • 3
  • 4

3 Answers3

43

Although Archedius's method works, officially it is deprecated and instead 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 like this...

public DataTemplate Create(Type type)
{
    StringReader stringReader = new StringReader(
    @"<DataTemplate 
        xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""> 
            <" + type.Name + @" Text=""{Binding " + ShowColumn + @"}""/> 
        </DataTemplate>");
    XmlReader xmlReader = XmlReader.Create(stringReader);
    return XamlReader.Load(xmlReader) as DataTemplate;
}

Official line taken from msdn: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelementfactory.aspx

Code example from Fredrik Hedblad's post here: Problems with XamlReader generating DataTemplate

Graviton
  • 81,782
  • 146
  • 424
  • 602
Russell Giddings
  • 8,731
  • 5
  • 34
  • 35
  • 9
    Use the XamlReader in System.Windows.Markup NOT System.Xaml – Jason Rae Feb 17 '16 at 21:37
  • 4
    Obsolete as the other option might be, this is a *very* inconvenient method, not only it provides virtually no static error checking and the produced error messages are cryptic (e.g. unexpected charater 0x20 at line 5 position 27), moreover it also means you have no access to project's resources. IMHO stay away from this for anthing but simplest templates. – wondra Oct 13 '20 at 08:56
  • Hard to call the alternative a deprecated approach when the `DataTemplate.VisualTree` method is the one that is still used internally in WPF to create templates programmatically. – caesay May 19 '22 at 18:30
39

You have first to declare a DataTemplate:

DataTemplate template = new DataTemplate { DataType = typeof(< Type of the object the template refers>) };

Then declare a layout panel like StackPanel in this way

FrameworkElementFactory stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Vertical);

and finally attach the TextBlock piece at it:

FrameworkElementFactory title = new FrameworkElementFactory(typeof(TextBlock));
title.SetBinding(TextBlock.TextProperty, new Binding("< name of your binding >"));
stackPanelFactory.AppendChild(title);

in order to display the StackPanel created in this way you have to attach it to the VisualTree:

template.VisualTree = stackPanelFactory;

Hope it helps! :)

Archedius
  • 2,887
  • 3
  • 20
  • 24
18

I know it is an work-around, but I published a tip in code project (http://www.codeproject.com/Tips/808808/Create-Data-and-Control-Templates-using-Delegates) that allows you to create a data-template using a delegate. For example:

TemplateGenerator.CreateDataTemplate(() => new TextBox());

This will be enough to create a datatemplate that creates a new textbox. If you want a binding too, it could be written like:

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

The code of the TemplateGenerator is the following:

/// <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;
  }
}

And it has a method for ControlTemplates too.

Paulo Zemek
  • 447
  • 5
  • 5
  • 1
    This is by far the best answer, works also with complex DataTemplates - where the other two answers failed – Coden Nov 12 '20 at 20:06