Creating a DataTemplate in Code: Can I use the Template property? - c#

I'm attempting to use a DataTemplateSelector with a particular third-party WPF grid control, and I'm having trouble determining if the issues I'm having are a bug in the control or my own lack of understanding about the conventions of WPF data templates.
I realize that the ordinary use case of a DataTemplate is to declare it somewhere in XAML (be it as a resource or explicitly where it's used), but my particular project would benefit greatly if I could create the template in code (C#, specifically) rather than in XAML. The issue I'm running into is the fact that my code-created DataTemplate uses a FrameworkElementFactory as the template's VisualTree, whereas a XAML-created template uses a TemplateContent object as the template's Template value. As best I can tell right now, the grid control in question works with templates that use Template, but doesn't seem to play nicely with templates that use VisualTree.
By way of comparison, here's what one of the templates looks like in XAML as part of my selector:
<MySelectorType>
<MySelectorType.BooleanTemplate>
<DataTemplate>
<EditorControl Name="Reserved_Name" />
</DataTemplate>
</MySelectorType.BooleanTemplate>
</MySelectorType>
And here's how I'm trying to create an equivalent template in code:
var template = new DataTemplate()
{
VisualTree = new FrameworkElementFactory(typeof(EditorControl))
{
Name = "Reserved_Name"
}
};
I've also tried it like this:
var template = new DataTemplate()
{
VisualTree = new FrameworkElementFactory(typeof(EditorControl))
};
template.VisualTree.SetValue(EditorControl.NameProperty, "Reserved_Name");
Which seemed more analogous to what the XAML template would do, but that appeared not to work at all (the editor neither read or set the value, where at least the first version would read it).
Is it possible for my in-code template to use the Template property rather than VisualTree? According to the documentation, there's no public API for this type and the instantiation path is complex, but has this been done? The only example I've found uses hardcoded XAML in the code, which doesn't sit well with me.

I do not like this way of doing things either but this actually is the recommended way, in the documentation of the FrameworkElementFactory the following can be found:
This class is a deprecated way to programmatically create templates, which are subclasses of FrameworkTemplate such as ControlTemplate or DataTemplate; not all of the template functionality is available when you create a template using this class. The 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.
I do not know of any simple way to use the Template property in code, the only way to my knowledge that might be possible is via a lot of reflection.
Setting names is a special case, if you set the Name property of the factory it should be properly registered, if not you need to get the approriate Namescope and register the name manually.

Related

Is there a way in .NET MAUI use multiple components components in one place?

I have a view which has some state and based on that state we want to use dynamically one of 3 component to show.
I know that we could allways hide 2 of them and show only one, but I am looking for a way to create property of in parent viewmodel in which would be stored instance of particular view we want to show. So whenever I would change that property, it would rerender itself with new component.
My main downfall is that I have no idea how to render property with stored view in XAML.
I was looking in microsoft docs, but honestly i have no idea what to look for.
If I got your issue right, what you're looking for are data templates
A DataTemplate is used to specify the appearance of data, and typically uses data binding to display data.
Since in your case the appearance (i.e. the data template) depends on the data to present, you will need a data template selector
A DataTemplateSelector can be used to choose a DataTemplate at runtime based on the value of a data-bound property.
I will try and come up with an example and might edit this answer later to present the example.
You could create multiple ContentView components with xaml UI and bind them with their own ViewModel. Then on the page you want to display your custom component create generic ContentView element and bind it's Content to ViewModel property.
<ContentView Content="{Binding Content}"/>
Then in your ViewModel create ObservableProperty using MVVM community toolkit or just implement INotifyPropertyChanged.
[ObservableProperty]
private ContentView content;
Now you can get your prepared components using Dependency Injection.
private readonly CustomView1 _customView1;
private readonly CustomView2 _customView2;
public ViewModel(CustomView1 customView1, CustomView2 customView2)
{
_customView1 = customView1;
_customView2 = customView2;
}
Now you can simply change your displayed Component in your ViewModel by changing your binded property "Content"
Content = _customView1;

Use the same template but a different value for a property

Is there a way to use the same style/template for two controls but with a modified property without rewriting the whole code? For example two ScrollViewers with the same style or template but with a different background.
It's related to my other question: Change property of component from template based on parent

Access Element inside Silverlight DataTemplate

I have not been able to find a clean solution to the following problem even though there are a few related questions already on SO.
If I have a data template that is being used multiple times, for example, a TreeViewItem.HeaderTemplate, how can I change something the template for only some of the TreeViewItems.
For example, let's say my TVI HeaderTemplate has a textblock and depending on the string, I want to make the fontweight bold.
I want to do something like this:
((TextBlock)myTreeView.Items.ElementAt(0).FindName("myTextBlock")).FontWeight = FontWeights.Bold;
Does anyone have a solution for this? --> Thanks Evan
Edit: Is there a way to write a generic function to get a control based on it's name even if it's in a data template?
LayoutRoot.FindName("myTextBlock"); would work if myTextBlock was not in a datatemplate. How can I write a findElementInDataTemplate(string elementName, string parentName) function?
The reason Evan's answer is not what I'm looking for is because I am developing a control. I want the application developer who uses my control to be able to change any element in the control. If I use Evan's solution, that would require the application developer to have access to all the templates in the control. Possible, but not ideal. Thanks!
One way I have accomplished this is to store all the needed items in a class-level collection variable by using the Loaded event of the control. Take this DataTemplate for example.
<DataTemplate>
...
<TextBlock Loaded="TemplateTextBlock_Loaded" />
</DataTemplate>
Then you use the Loaded event to load up some sort of collection for later use.
private List<TextBlock> templateTextBlocks = new List<TextBlock>();
private void TemplateTextBlock_Loaded(object sender, RoutedEventArgs e)
{
TextBlock tb = sender as TextBlock;
if (!this.templateTextBlocks.Contains(tb)) this.templateTextBlocks.Add(tb);
}
Of course, if you're going to be loading and unloading the control, this may not work well for you.
If you're using data binding, have you tried using a binding converter? In this case you would do something like...
FontWeight={Binding Path=TextProperty, Converter={StaticResource BoldConverter}}
And the converter would be along the lines of...
string myTestString = (string)value;
if (myTestString.Contains("Bob"))
return FontWeights.Bold;
return FontWeights.Normal;
Which makes it less painful to try and root through the elements to locate a particular one.
My first reaction to such a requirement would be: are you really sure you want to be doing that? I would normally urge developers to look at the existing control patterns being used. In this case what you seem a Templated control would seem warranted.
Of course this doesn't provide the flexibility you are after. What you seem to be after is the "holy grail" of customisable controls, the desire to tweak any minor detail about the control without having to duplicate the entire template of the control. OF course this isn't really possible declaratively, if it were I'd dread the syntax and semantic rules that would govern it.
Having said that there are always exceptions. So I'll present a possible option despite feeling that you shouldn't be doing this.
This old answer provides a Descendents extension method allow you to enumerate controls across the object tree. Given an instance of a TreeViewItem you should be able to find the TextBlock you are after with:-
TextBlock tb = treeViewItem.Descendents()
.OfType<TextBlock>()
.Where(t => t.Name == "myTextBlock")
.FirstOrDefault();
what version of silverlight is this?
And what year of " Aug 10 at 18:55" is this post from?
in the current version of SL4 it does not seem to be there..
can also try this
TextBlock txtBlk = grd.FindName("txtBlkName") as TextBlock;
where grd = your root element (Parent of the element you are looking for)

What key to use if adding a data template to the resources of a control

I have a data template that is defined in a XAML file. The root of the XAML is not a resource dictionary but the data template itself. Now I want to add this data template to the resources of a control. Unfortunately, I don't know what key to use.
myControl.Resources.Add(???, dataTemplate);
Although I use the data templates DataType property (i.e. the type that I want to template) the WPF resource lookup engine does not use my data template.
Anyone? Thanks!
EDIT:
I know that with styles the type of the target would do the trick but with data templates this seems to be different...
EDIT:
Answer: Ok, I did a little bit of debugging. Here is the correct code
myControl.Resources.Add(new DataTemplateKey(typeof(...)), dataTemplate);
use as key the type for which the datatemplate is designed like:
myControl.Resources.Add(typeof(TheType), dataTemplate);
Actually Moser is right,
DataTemplates work on data objects.. ControlTemplates are for well.. controls...
If your DataTemplate is for, lets say Car objects, use Mosers example like so:
myControl.Resources.Add(typeof(Car), dataTemplate);
Set your Car object on DataContext of the control that needs to use this DataTemplate, et voila :)
Or you could always make up your own key:
myControl.Resources.Add("MyAwesomeDataTemplate", dataTemplate);
And then set the Template property like so:
Template="{StaticResource MyAwesomeDataTemplate}"

Accessing TemplatePart without changing ControlTemplate?

i wonder if there is a way to access a control's templatepart from within c# for modifying the part (e.g. hiding, etc..). is it possible to get a reference to the part with pure c#?
i don't want to touch the controls template.
thanks
j.
It is possible, but its quite nasty.
On the Template there is a method called FindName, which needs two arguments: the name and the FrameworkElement that has the ControlTemplate as Template. Of course, you need to set the name of the element in the ControlTemplate...
Another more elegent solution is to use a Binding in the ControlTemplate to determine the visibility.. That way you do not need to do stuff in your code behind and you can keep it Xaml only...

Categories

Resources