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.
Related
I have a ListView that's on a ContentPage, and I want the ItemTemplate of the ListView to be user-definable - i.e. parsing the XAML for the template at runtime. However, I need to give the user the option of having a button that invokes a command on the page. See below XAML;
<SwipeView>
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem
Text = ""Delete""
BackgroundColor=""Red""
Command=""{Binding Path=ItemDeletedCommand, Source={x:Reference multiEntryPage}}""
CommandParameter=""{Binding .}"" />
</SwipeItems>
</SwipeView.RightItems>
<SwipeView.Content>
...
</SwipeView.Content>
And my content page has the x:Name="multiEntryPage" attribute in its ContentPage element. The code I use to parse the XAML is shown here:
listView.ItemTemplate = new DataTemplate(() => new ViewCell().LoadFromXaml(xaml));
However, this gives the below error:
{Xamarin.Forms.Xaml.XamlParseException: Position 11:33. Can not find
the object referenced by multiEntryPage at
Xamarin.Forms.Xaml.ApplyPropertiesVisitor.ProvideValue (System.Object&
value, Xamarin.Forms.Xaml.ElementNode node, System.Object source,
Xamarin.Forms.Xaml.XmlName propertyName)...
So I think it's fairly clear that the error is happening because the SwipeView XAML doesn't know about its parent at this stage, but I'm at a loss of how to resolve this. Any ideas?
Thanks!
You can create DataTemplate in following ways
Creating an Inline DataTemplate
Creating a DataTemplate with a Type
Creating a DataTemplate as a Resource
Refer https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/creating.
While all examples and sources I found is to set the resource in XAML statically, I would only know in run time the name of the XML file to be connected with XMLDataProvider. Is there a way to set either in code behind or in XAML?
<Window.Resources>
<XmlDataProvider x:Key="XMLFoo" Source="Foo.xml" XPath="Foo"/>
</Window.Resources>
It could be Foo.xml, or could be Goo.xml.
Yes you can change that while runtime. Unfortunately you cannot bind it, so you have to do stuff in Code-Behind.
Here's a simple example:
(this.Resources["XMLFoo"] as XmlDataProvider).Source = new Uri("Goo.xml");
Cheers
If you are trying to have just one instance of XamlDataProvider and would like your source to change dynamically, I don't think it is possible in pure XAML as you cannot bind to Source property, since that is not a DependencyProperty.
From code-behind, you can get the instance your provider and change it's source.
var provider = (XmlDataProvider) Resources.FindName("XMLFoo");
provider.Source = new Uri("bar.xml", UriKind.Relative);
Alternatively, you can use MVVM and expose your XmlDataProvider as a property on the ViewModel and bind it to your View, you can then change the Source and refresh data from the ViewModel itself.
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.
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.
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.