How to create DataTemplate with ItemsControl from code behind WPF
You could use the XamlReader.Parse
method to create an elements from a XAML string dynamically:
const string Xaml = "<ItemsControl ItemsSource=\"{Binding ListOfSubObjects}\">" +
" <ItemsControl.ItemsPanel>" +
" <ItemsPanelTemplate>" +
" <StackPanel Orientation=\"Vertical\"></StackPanel>" +
" </ItemsPanelTemplate>" +
" </ItemsControl.ItemsPanel>" +
" <ItemsControl.ItemTemplate>" +
" <DataTemplate>" +
" <TextBlock Text=\"{Binding SubObjectName}\"/>" +
" </DataTemplate>" +
" </ItemsControl.ItemTemplate>" +
" </ItemsControl>";
ParserContext parserContext = new ParserContext();
parserContext.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
parserContext.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
ItemsControl itemsControl = XamlReader.Parse(Xaml, parserContext) as ItemsControl;
How to create instance of control from DataTemplate in code behind
Call LoadContent()
and cast the result:
var template = resourceDictionary["Button"] as DataTemplate;
var control = template.LoadContent() as Button;
<DataTemplate x:Key="Button">
<Button Content="btn" />
</DataTemplate>
Creating DataTemplate from code behind
The missing lines are:
var ellipseVisBinding = new Binding("isAdmin");
ellipseVisBinding.Converter = new BooleanToVisibilityConverter();
fef.SetBinding(Ellipse.VisibilityProperty, ellipseVisBinding);
(I note that you've excluded the DockPanel in the template from your code version so I've removed that as well)
wpf applying datatemplates in code-behind
Your control can expose its own property/properties, which you declare in your code-behind.
If you need a single DataTemplate
, then you can expose a property of type DataTemplate
. When a user declares your control type in XAML, she can provide the template:
<ns:YourControl>
<ns:YourControl.DataTemplate>
<DataTemplate>
…
</DataTemplate>
</ns:YourControl.DataTemplate>
</ns:YourControl>
In your own control, you consume this by binding to the DataTemplate
property. Be sure to reference the control itself in your Binding
, rather than the DataContext
. You'll probably want a default DataTemplate
or throw a useful Exception
in the event that the user does not specify a DataTemplate
.
You can give the user some additional flexibility if you expose a property of type DataTemplateSelector
and then apply that to your items, if the data types are disparate or the user is likely to want different templates under different circumstances.
Example
MyControl.xaml
<UserControl x:Class="MyNamespace.MyControl"
x:Name="ThisControl">
<ItemsControl ItemTemplate="{Binding ItemTemplate, ElementName=ThisControl}" />
</UserControl>
MyControl.xaml.cs
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemTemplateProperty
= DependencyProperty.Register("ItemTemplate", typeof (DataTemplate),
typeof (MyControl), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemTemplate
{
get { return (DataTemplate) GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// Other dependency properties (ItemsSource, SelectedItem, etc.)
}
The consumer:
<Grid>
<ns:MyControl ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}">
<ns:MyControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2"
BorderBrush="Black">
<TextBlock Foreground="DarkGray"
Text="{Binding Name}"
Margin="4" />
</Border>
</DataTemplate>
</ns:MyControl.ItemTemplate>
</ns:MyControl>
</Grid>
Update
Okay, here is a working example of populating the Grid
and using the DataTemplate
.
MyControl
exposes a property, ItemsSource
, which allows the consumer to bind to a collection in her view-model. MyControl
also exposes a property, ItemTemplate
, which allows a consumer to specify how to display those items (again, you could also allow the user to specify a DataTemplateSelector
).
In the code-behind, when the source collection changes, we
- create a
ColumnDefinition
for each item, - wrap each item inside another class that exposes
Row
andColumn
properties, and - add each wrapped item to a private collection, which is what we actually bind to in our control.
First, the XAML:
<UserControl x:Class="WpfApplication1.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="ThisControl"
d:DesignHeight="300" d:DesignWidth="300">
<ItemsControl x:Name="ItemsControl"
ItemsSource="{Binding BindableItems, ElementName=ThisControl, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Grid.Row" Value="{Binding Row}" />
<Setter Property="Grid.Column" Value="{Binding Column}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter Content="{Binding Content}"
ContentTemplate="{Binding ItemTemplate, ElementName=ThisControl}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</UserControl>
And the code-behind:
using System.Collections;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource", typeof (IEnumerable), typeof (MyControl),
new PropertyMetadata(default(IEnumerable), OnItemsSourceChanged));
public IEnumerable ItemsSource
{
get { return (IEnumerable) GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
// This is the DataTemplate that the consumer of your control specifies
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
"ItemTemplate", typeof (DataTemplate), typeof (MyControl), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemTemplate
{
get { return (DataTemplate) GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// This is declared private, because it is only to be consumed by this control
private static readonly DependencyProperty BindableItemsProperty = DependencyProperty.Register(
"BindableItems", typeof (ObservableCollection<object>), typeof (MyControl), new PropertyMetadata(new ObservableCollection<object>()));
private ObservableCollection<object> BindableItems
{
get { return (ObservableCollection<object>) GetValue(BindableItemsProperty); }
set { SetValue(BindableItemsProperty, value); }
}
private static void OnItemsSourceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var myControl = dependencyObject as MyControl;
if (myControl == null)
{
return;
}
// Get reference to the Grid using reflection. You could also walk the tree.
var grid = (Grid) typeof (ItemsControl).InvokeMember("ItemsHost",
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
null, myControl.ItemsControl, null);
var columns = grid.ColumnDefinitions;
columns.Clear();
myControl.BindableItems.Clear();
var items = args.NewValue as IEnumerable;
if (items != null)
{
var columnIndex = 0;
foreach (var item in items)
{
columns.Add(new ColumnDefinition{ Width = GridLength.Auto });
var container = new MyItem
{
Row = columnIndex,
Column = columnIndex++,
Content = item
};
myControl.BindableItems.Add(container);
}
}
}
}
public class MyItem
{
public object Content { get; set; }
public int Row { get; set; }
public int Column { get; set; }
}
}
Related Topics
Sending Mail Along with Embedded Image Using ASP.NET
Returning in the Middle of a Using Block
How to Create an Explorer-Like Folder Browser Control
Waiting for Async/Await Inside a Task
ASP.NET 2012 Unobtrusive Validation with Jquery
Query Microsoft Access Mdb Database Using Linq and C#
Fluent API, Many-To-Many in Entity Framework Core
C# Implementation of Deep/Recursive Object Comparison in .Net 3.5
In .Net/C# Test If Process Has Administrative Privileges
How Expensive Is the Lock Statement
Reading Excel Files as a Server Process
Why Cannot Ienumerable<Struct> Be Cast as Ienumerable<Object>