How to create DataTemplate at runtime? - c#

I have defined the DataTemplate in the XAML, however I need to change it to be defined at run time, because its binding is dynamic.
<UserControl.Resources>
<DataTemplate x:Key="myCellTemplate">
<TextBlock Text="{Binding Description}" Margin="4"/>
</DataTemplate>
</UserControl.Resources>
Is there any way to define it in code-behind?
Thank you.

You might be able to accomplish what you need with a custom DataTemplateSelector and I'd recommend that approach, if possible. That said, it is possible to create a DataTemplate in code:
Type type = typeof(MyUserControl); //for example
var template = new DataTemplate();
template.VisualTree = new FrameworkElementFactory(type);
return template;
In this context, type is the type of visual element you want to have as the root of your template. If necessary, you could add additional elements using the factory, but in the one case where I've used this, I simply created UserControl elements to represent the different templates I was dynamically creating.
My apologies, this apparently isn't supported in Silverlight. In Silverlight, you have to use XamlReader.

Related

Adding Static Resources inside Contol.Resources Tag?

I am writing an application that allows me to leverage the default template selector provided by a ListBox. Currently, I am defining the DataTemplates within the Listbox.Resources tag like so:
<ListBox.Resources>
<DataTemplate DataType="{x:Type Dog}">
// Some XAML here
</DataTemplate>
<DataTemplate DataType="{x:Type Cat}">
// Some XAML here
</DataTemplate>
</ListBox.Resources>
The problem is that these Templates can get rather lengthy. I know this is probably trivial, but how can I reference these Datatemplates from a ResourceDictionary that has been added to the XAML file without writing a template selector? I can't seem to find documentation for doing this anywhere online.
Instead of placing it in a resource dictionary, why not create a composite control(s) which would achieve easier reading in the datatemplates as well as centralize the xaml into a named entity control. Plus one could add any dependency properties which would help with the data transfer.

How to create DataTemplate from UserControl instance?

Question
Basically I would like to do the following but it seems that I cannot:
UserControl myControl = new UserControl();
DataTemplate template = new DataTemplate(myControl);
The question: Is it possible to construct a DataTemplate from UserControl instance? If not, are there any other possible solutions?
Real problem
I'm working on a project where majority of UI views are simple static Word-like documents (e.g some text fields and maybe some images, nothing too fancy). Because most of persons working on this project are not coders we have designed very simple in-house markup language for UI generation. An example of markup of simple view is following:
First name: [Person.FirstName]
Last name: [Person.LastName]
Address: [Person.Address.Street], [Person.Address.City]
Now these templates are loaded at runtime and usercontrols are created based on them. In this case one usercontrol would be created and it would contain simply couple of stack panels and text blocks so that resulting control would look a bit like text document. XAML equivalent would be something like:
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="First name: "/>
<TextBlock Text={Binding Person.FirstName}
</StackPanel>
<StackPanel>
...
</StackPanel>
...
</StackPanel>
Then, I started to implement support for lists but couldn't think of a way how to do that. In theory it is simple and I came up with following syntax (+ XAML equivalent):
[List Customers]
First name: [Person.FirstName]
Last name: [Person.LastName]
Address: [Person.Address.Street], [Person.Address.City]
[EndList]
->
<StackPanel>
<ItemsControl ItemsSource="{Binding Customers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
[Insert code from previous XAML example here]
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
But I can't do that because it seems that I cannot construct DataTemplate directly from UserControl instance. There would be no problems if my UserControls were types, but they are not.
One possible solution is that I could bind ItemsControl.Items directly to list of UserControls (instead of binding to ItemsSource and ItemTemplate) but this is sub-optimal in our case for couple of reasons. I would be willing to try some other solutions first!
Update
For clarification: all I have is plain instance of UserControl class which contains the content I need. E.g.
UserControl control = new UserControl();
var panel = new StackPanel();
panel.Children.Add(...);
panel.Children.Add(...);
control.Content = panel;
// How to use that control as ItemTemplate for ItemsControl?
// It seems that it is not possible directly but I want to
// know what my options are.
I don't have class for it because I'm constructing it at run-time and I don't want to create new type dynamically by emiting IL code because it is way too painful.
Creating a datatemplate from Code behind goes like this:
FrameworkElementFactory factory = new FrameworkElementFactory(MyUserControl.GetType());
DataTemplate dt = new DataTemplate();
dt.VisualTree = factory;
yourItemsControlInstance.ItemTemplate = dt;
A datatemplate is a definition of controls to be built at runtime, that is way this construction with a ElementFactory. You do not want the same instance of the UserControl for every item in your ItemsControl.
Ah I understand your problem now. I don't think there is an easy way (one or two lines of code) to create a datatemplate from a UserControl instance.
But to solve your problem I see two directions:
At the point where an usercontrol is created, create a datatemplate instead and use that. It will be cumbersome, with nested FrameworkElementFactories. I have never done that, and the MSDN documentation says that you may encounter some limitations you cannnot do compared to datatemplates in Xaml. But if it is simple it must be doable. There used to be a codeproject article by Sacha Barber you could use as a guidance (if needed).
You pack the creation of the UserControl in a method called private UserControl createMyUserControl(){}
And do something like this:
ItemsControl itemsControl = new ItemsControl();
foreach (var customer in Customers)
{
var usercontrol = createMyUserControl(...);
usercontrol.DataContext = customer;
itemsControl.Items.Add(usercontrol);
}
Second option is less elegant in my opinion, so I would check out the option 1 first.
WPF: How to create Styles in code/and magical Content (see section at the end for extensive sample of a DataTemplate in Code behind)
I think you can replace UserControl with the ContentControl.
Just set the content of the ContentControl to the desired template and use it as ItemTemplate for the ItemsControl.

Bind to a DataTemplate dynamically

I have a ListBox and multiple DataTemplates, in separate files.
<ListBox ItemTemplate="{StaticResource ItemTemplate1}"/>
In the Styles.xaml file:
<DataTemplate x:Key="ItemTemplate1">...</DataTemplate>
<DataTemplate x:Key="ItemTemplate2">...</DataTemplate>
I want to change the ItemTemplate of the ListBox depending on the type of object that's in its list.
Is there a way to access the separate DataTemplates in the code-behind, so that I can bind to a property of my Page?
The way to do that without TemplateSelector is to specify DataType property and don't specify x:Key.
<DataTemplate DataType="{x:Type Type1}">...</DataTemplate>
<DataTemplate DataType="{x:Type Type2}">...</DataTemplate>
In this case appropriate DataTemplate will be automaticly applied in all places where property of specified type have been bound.
But I'd prefer to use TemplateSelector.
To access separated DataTemplate in code-behind you should first get resource dictionary:
var dict = new ResourceDictionary
{Source = new Uri("/ProjectNamespace;component/Styles.xaml", UriKind.Relative)};
Then you can get template:
var dataTemplate = (DataTemplate) dict["ItemTemplate1"];
Try this solution does pretty much what your trying to achieve:
Applying Data Templates Dynamically by Type in WP7
http://www.codeproject.com/Articles/113152/Applying-Data-Templates-Dynamically-by-Type-in-WP7
Its based on WP7 but should work for you too.
There is inbuilt support in WPF for your requirement. Start reading on DataTemplateSelector to select the template at runtime based on certain criteria.

WPF Datatemplating an ItemsControl

I have an ItemsControl whose ItemsSource gets bound to an ObservableCollection<Component> at run time. I have defined a data template for type Component which works fine.
Now Component has a ObservableCollection<Control> and I want to add another ItemsControl inside my Component Datatemplate to render all the controls. Control here is my own custom object not related to a wpf control.
There are different types of controls so I am trying to use an ItemTemplateSelector to select the right template for each type. In the example below to keep it small I have only shown one of the templates "RWString" which I find using a FindResource in MyControlTemplateSelector overriding SelectTemplate. But the SelectTemplate never gets called(using a breakpoint to check). Is there something wrong in my xaml?
<ItemsControl.Resources>
<src:MyControlTemplateSelector x:Key="XSelector" />
<DataTemplate DataType="{x:Type src:Component}" >
<Expander Visibility="{Binding Path=Show}">
<ItemsControl ItemsSource="{Binding Path=Contrls}"
ItemTemplateSelector="{StaticResource XSelector}">
<ItemsControl.Resources>
<DataTemplate x:Key="RWstring" >
<TextBlock Text="{Binding Path=Label}"/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><WrapPanel /></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Expander>
</DataTemplate>
</ItemsControl.Resources>
Update: Contrls is not a typo, its just me using a silly naming system. Contrls is a property of Component of type ObservableCollection<Control>. Also the reason I am trying to use the the ItemsTemplateSelector is that the ObservableCollection<Control> contains objects of generic types like Control<int> Control<string> etc all deriving from Control and apparently you cant create datatemplates referring to generic types.
Update3: Removed update 2 as it was unrelated. I got the ItemTemplateSelector to work by changing StaticResource to DynamicResource. But I don't know why this works...
I'm guessing this doesn't work with a StaticResource as the Resource is inside the ItemsControl which probably has not been created at load time when StaticResources are evaluated.
DynamicResources at load time are evaluated to an expression at load time and then evaluated to the correct value when requested.
Try move the Resource outside of the ItemsControl.
In the line where you bind the nested ItemsControl, is the Path correct? It is currently "Contrls", should it be "Controls"?

Creating a XAML Resource from Code Without a Key

Is there a way to add a resource to a ResourceDictionary from code without giving it a resource key?
For instance, I have this resource in XAML:
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type xbap:FieldPropertyInfo}"
ItemsSource="{Binding Path=Value.Values}">
<TextBlock Text="{Binding Path=Name}" />
<HierarchicalDataTemplate>
</TreeView.Resources>
I need to create this resource dynamically from code and add it to the TreeView ResourceDictionary. However, in XAML having no Key means that it's used, by default, for all FieldPropertyInfo types. Is there a way to add it to the resource in code without having a key or is there a way I can use a key and still have it used on all FieldPropertyInfo types?
Here's what I've done in C# so far:
HierarchicalDataTemplate fieldPropertyTemplate = new HierarchicalDataTemplate("FieldProperyInfo");
fieldPropertyTemplate.ItemsSource = new Binding("Value.Values");
this.Resources.Add(null, fieldPropertyTemplate);
Obviously, adding a resource to the ResourceDictionary the key null doesn't work.
Use the type that you want the template to apply to as the key:
HierarchicalDataTemplate fieldPropertyTemplate = new
HierarchicalDataTemplate("FieldProperyInfo");
fieldPropertyTemplate.SetBinding(
HierarchialDataTemplate.ItemSourceProperty,
new Binding("Value.Values");
this.Resources.Add(FieldPropertyInfo.GetType(), fieldPropertyTemplate);
The reason your code wasn't working was your weren't actually setting the binding. You need to call SetBinding, with the property you want the binding bound to.
Use the type that you want the template to apply to as the key:
this.Resources.Add(FieldPropertyInfo.GetType(), fieldPropertyTemplate);
As with your template above you provide a type. You have to either have to provide a name or a type.

Categories