I checked these answers, but none had the information I'm looking for:
How to setup a WPF datatemplate in code for a treeview?
How to set Control Template in code?
Create ControlTemplate programmatically in WPF
Here is the gist of my code:
DataGridTextColumn col = new DataGridTextColumn();
Style styl = null;
// Need to add this on a per-column basis
// <common:RequiredPropertyDisplayBrushConverter x:Key="requiredDisplayBrushConverter" />
string xaml = "<ControlTemplate TargetType=\"{x:Type DataGridCell}\"> <TextBox Background=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text, Converter={StaticResource requiredDisplayBrushConverter} > </TextBox> </ControlTemplate>";
MemoryStream sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
ParserContext pc = new ParserContext();
pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
ControlTemplate ct = (ControlTemplate)XamlReader.Load(sr, pc);
styl.Setters.Add(new Setter(TemplateProperty, ct));
col.CellStyle = styl;
In the ControlTemplate, a binding refers to the converter mentioned in the comments. When the converter is defined in the xaml as a resource for the DataGrid, I get a runtime error: {"Cannot find resource named 'requiredDisplayBrushConverter'. Resource names are case sensitive."}
Can I add this as a resource to the column? Or add it to the DataGrid's resources at runtime?
Or is there some other technique?
Thanks --
You can add it to the xaml but you'll need to rearrange it. The resource will need to be defined before you can use it. Which means the background will need to be defined as a child tag.
Change your xaml to this:
string xaml = "<ControlTemplate TargetType=\"{x:Type DataGridCell}\"><TextBox><TextBox.Resources><common:RequiredPropertyDisplayBrushConverter x:Key=\"requiredDisplayBrushConverter\" /></TextBox.Resources><TextBox.Background><Binding RelativeSource=\"{RelativeSource TemplatedParent}\" Path=\"Content.Text\" Converter=\"{StaticResource requiredDisplayBrushConverter}\"/></TextBox.Background></TextBox></ControlTemplate>";
And you'll need to define the common namespace. Add this after your other namespaces (with the correct namespace/assembly, of course):
pc.XmlnsDictionary.Add("common", "clr-namespace:WPFApplication;assembly=WPFApplication");
OR, you could just add the resource to your App.xaml, if that is an option.
Related
I am attempting to add an expander from code behind to an existing ListBox. The content that needs to be displayed in the header of the expander comes from a directory name which may or may not contain underscores. I want to preserve the directory name and not have the first underscore be interpreted as a keyboard shortcut.
I found this thread discussing how to do it in xaml and have tried to implement the same solution in code behind without any luck.
I also found this thread discussing how to create a data template from the code behind, but I can't get that to work either.
I have tried the following code snippets but either it won't compile or it just displays blanks for the expander headers:
String markup = String.Empty;
markup = "<TextBlock text=\"" + directory.Name + "\"/>";
ex.HeaderTemplate = new DataTemplate((DataTemplate)XamlReader.Load(markup));
.
ex.HeaderTemplate = new DataTemplate("TextBlock");
TextBlock tb = new TextBlock();
tb.Text = directory.Name;
ex.Header = tb;
you don't need to change HeaderTemplate to avoid underscore conversion into AccessKey.
add TextBlock into Expander.Header explicitly, and it will keep text unchanged.
<Expander>
<Expander.Header>
<TextBlock x:Name="ExpanderHeader"/>
</Expander.Header>
</Expander>
this way you don't need to create UI elements in c# code.
change header text in ExpanderHeader textBlock
ExpanderHeader.Text = directory.Name;
or bind it if there is a view model:
<TextBlock x:Name="ExpanderHeader" Text="{Binding Path=...}"/>
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.
In a WPF app, if a ContentControl is declared in XAML,
<Grid Name="MyGrid">
<ContentControl Name="MyContentControl" />
</Grid>
then I can easily reference it in code using FindName:
ContentControl cc = FindName("MyContentControl") as ContentControl;
cc.Content = ...
But if I add the ContentControl in code instead:
ContentControl contentcntr = new ContentControl();
contentcntr.Name = "MyContentControl";
this.MyGrid.Children.Add(contentcntr);
The FindName doesn't find it.
What's wrong with it in the second case? What's the difference?
The XAML parser automatically registers the names in a namescope, if you create elements like this you may need to do that yourself using RegisterName. (There is an accessor on FrameworkElement as well.)
Is it possible to create data-template for the list box in WP7 using C# code instead of XAML??
You can't instantiate a DataTemplate in code in the same way that you can for regular controls, but you can use the XamlReader.Load() method to create a DataTemplate from a XAML string:
string xaml = #"<DataTemplate
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<!-- Template content goes here. -->
</DataTemplate>";
var dt = (DataTemplate)XamlReader.Load(xaml);
Be sure to add any additional namespaces that you might need.
The answer to this question also shows that you can create bindings in the DataTemplate in the same way: Creating a Silverlight DataTemplate in code.
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.