I have a TreeView bound to a list of Tileset. Tileset contains TileGroup, TileGroup contains both Tile and TileRun instances. Both Tile and TileRun implement ITile, but eventually there will be many more types implementing ITile
I have the following XAML:
<TreeView
Grid.Row="0"
Grid.Column="0"
BorderThickness="0"
ItemsSource="{Binding Path=Tilesets}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Tileset}" ItemsSource="{Binding Path=TileGroups}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TileGroup}" ItemsSource="{Binding Path=Tiles}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type tiles:ITile}">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
Tileset and TileGroup choose the correct DataTemplate but ITile does not, no template is selected, the tree just displays the data type.
However, if I add a DataTemplate for both Tile and TileRun explicitly, everything works great.I don't want to do that though, as there will eventually be many more classes implementing ITile.
I am aware that I could handle this using a DataTemplateSelector, but I'd like a pure XAML solution if possible.
Am I doing something wrong here, or does WPF just not support this type of automatic template selection based on interfaces?
Am I doing something wrong here, or does WPF just not support this type of automatic template selection based on interfaces?
You are not doing something wrong. This kind of data binding support for interfaces is simply not supported. Please refer to Beatriz Costa's (MSFT) answer in the following thread on the MSDN forums for more information about why.
Data templates and interfaces: https://social.msdn.microsoft.com/Forums/vstudio/en-US/1e774a24-0deb-4acd-a719-32abd847041d/data-templates-and-interfaces?forum=wpf
"The data binding team discussed adding support for interfaces a while ago but ended up not implementing it because we could not come up with a good design for it. The problem was that interfaces don't have a hierarchy like object types do. Consider the scenario where your data source implements both IMyInterface1 and IMyInterface2 and you have DataTemplates for both of those interfaces in the resources: which DataTemplate do you think we should pick up?
When doing implicit data templating for object types, we first try to find a DataTemplate for the exact type, then for its parent, grandparent and so on. There is very well defined order of types for us to apply. When we talked about adding support for interfaces, we considered using reflection to find out all interfaces and adding them to the end of the list of types. The problem we encountered was defining the order of the interfaces when the type implements multiple interfaces."
So you will either have to define a DataTemplate for both Tile and TileRun explicitly or use a DataTemplateSelector.
Related
I'm having trouble pulling off a binding that I believe should be valid in Silverlight 5:
<Controls:GraphLayout>
<Controls:GraphLayout.VertexTemplate>
<DataTemplate>
<TextBox ... />
</DataTemplate>
</Controls:GraphLayout.VertexTemplate>
<Controls:GraphLayout.SubgraphTemplate>
<DataTemplate>
<Controls:GraphLayout VertexTemplate="{Binding VertexTemplate, RelativeSource={RelativeSource AncestorType=Controls:GraphLayout}}"/>
</DataTemplate>
</Controls:GraphLayout.SubgraphTemplate>
</Controls:GraphLayout>
This is a simplified example but hopefully it's pretty readable. In excessence I'm trying to bind the VertexTemplate of the SubgraphTemplate to the VertexTemplate of the containing control (the root GraphLayout control). Unfortunately, in the code behind I can see that VertexTemplate is null in the subgraph control.
May be your code can work (this is strange that you bound to something that doesn't have an x:Key and I think you should use {StaticResource SomethingWithName})).
But actually what you looking for is DataTemplateSelector. It's very easy to use and actually support the logic you described.
I want to set the background color separately for each item in a WPF ListBox. e.g. If I am adding Widgets to the ListBox, I might set the background color for each one based on the type of widget. This must be done in code (not XAML) as I only know what the items are at run time.
I know how to use ItemContainerStyle to set the style for all items, but how do you do it separately for each item?
Yes you do set ItemContainerStyle, using a StyleSelector.
This example at MSDN is exactly what you are looking for.
There are lots of ways to do this.
One is to use a StyleSelector, as loxxy suggests. This is pretty low on my list, because that kind of code is harder to read (well, find) and test than I'd like.
Another is to use a DataTrigger in the style. This is simple, if (and only if) the items all implement a common property that can be used in the trigger. You might be well served by implementing a wrapper class that exposes this common property, and contains the logic that figures out what value to assign to the property based on the object it's wrapping. (Whether or not this is easier than a StyleSelector is certainly arguable.)
If the items are really and truly heterogeneous, you can accomplish the result by using data templates, e.g.:
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Foo}">
<TextBlock Text="{Binding FooText}" Background="Red"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Bar}">
<TextBlock Text="{Binding BarText}" Background="Yellow"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Baz}">
<TextBlock Text="{Binding BazText}" Background="PapayaWhip"/>
</DataTemplate>
</ListBox.Resources>
etc. This would generally be my first choice, but your question doesn't really explain enough about the circumstances to know if it's the right way to go or not.
When I searched the internet to find out how to set the itemtemplate for a listbox, I found an example where they used a Style Setter in the window.resources to do this. So, I have a couple of options, I can either create a datatemplate in my window.resources, or, create a Style Setter. Would it be overkill to set the style instead of the datatemplate? Which method should I use?
Thank You.
You can't say one or the other is better, it depends. Implicit DataTemplates are a nice way to globally (even if just for part of the visual tree) how a Type should look. While using ItemTemplate allows you to indicate how it should look in a given ItemsControl (i.e. ListBox, ComboBox, etc.).
You can even use a combination of both of them, which allows you have a "default" look, but customize it per control or control type.
Even with ItemTemplate, you can set it globally for say all ListBoxes, or for just one ListBox instance. So again, sometimes one method is better, sometimes not. It depends on what you are trying to do.
Styles in general are used to apply the same values to different instances of the same type. If this is not the case there is no need to create a style at all.
I prefer to make a <DataTemplate> for the type of item and then let WPF figure it out.
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
<List ItemSource="{Binding MyListOfTaskItems"/>
I'm fairly new to Data binding & XAML, so this probably is fairly simple thing but I've been stumped on it for days now (and frustrated with more googling than i can track at this point) and would appreciate any pointers in the right direction. My only preference is to keep it in pure XAML if possible.
In my RIA SL4 project, I have two Entities PackageOS and OS where PackageOS has an association to OS through PackageOS.OS (associating through PackageOS.OSID <-> OS.ID - and [Include] + .Include() setup properly on relevant sections)
This is the template (defined in Page.Resource section along with all other involved DDS) I'm using in DataForm to get OSEntities List to bind into PackageOS Entity (coming from RIA GetOSEntities() using DDS):
<DataTemplate x:Key="POSItemTemplate">
<StackPanel>
<toolkit:DataField Label="PackageOS.OS">
<TextBlock Text="{Binding Source={StaticResource packageOSEntityDomainDataSource}, Path=Data.CurrentItem.OS}" />
</toolkit:DataField>
<toolkit:DataField Label="OS">
<ComboBox ItemsSource="{Binding Path=Data, Source={StaticResource osEntityDomainDataSource}}"
SelectedItem="{Binding Path=Data.CurrentItem.OS, Source={StaticResource packageOSEntityDomainDataSource}}"/>
</toolkit:DataField>
</StackPanel>
</DataTemplate>
The core problem is SelectedItem of ComboBox is not working. All the bindings are reachable from IDE Binding wizard so it's not a problem of me typing incorrect path. I can see packageOSEntityDomainDataSource.Data.CurrentItem to be of type PackageOS.
If i create a manual entry in backend database, the result is shown in PackageOS.OS textblock so I know it is properly being returned but SelectedItem refuses to pick it up (it ends up selecting the first value in dropdown list regardless of OS item in PackageOS).
Many thanks in advance!
Finally figured this out. Leaving my answer in hopes that it saves somebody else the time that I spent on this.
First Lesson
The problem was in the fact that I didn't have a custom Equality implementation for generated entities and default reference equality didn't work as I was using two different instances. Once I implemented IEquatable on my generated entities (through .shared.cs partial classes on server side) everything started working like a charm.
For details please see Silverlight ComboBox Control Population by Manishdalal
Second lesson
Do not use multiple DDS controls if you can help it. Especially once you use a write operation on a DDS, you cannot load/refresh any other DDS that is sharing the DomainContext until changes are committed. The link above shows how to avoid multiple DDS by using list generators when all you want is to pick up list of entities to fill ComboBox up.
My new code looks like this:
<DataTemplate x:Key="POSItemTemplate">
<StackPanel d:DataContext="{Binding Source=packageOSDomainDataSource, Path=Data.CurrentItem}">
<toolkit:DataField Label="OS">
<ComboBox DisplayMemberPath="Name"
ItemsSource="{Binding Path=OSList, Source={StaticResource OSListGenerator}}"
SelectedItem="{Binding Path=OS, Mode=TwoWay}" />
</toolkit:DataField>
</StackPanel>
</DataTemplate>
Where OSListGenerator is returning an IEnumerable<OSEntity> through its OSList property after loading it from DomainContext
Third Lesson
In DDS DataTemplate you have to be explicit with TwoWay Binding. This is the new behaviour; something that took me days to figure as most of the tutorials I referred to were using SL3 and I didn't realize that this was a breaking change in DDS DataTemplate behaviour in SL4.
How can I recursivly bind a Treeview to an XDocument, mapping each XML Element to a Node in the Treeview?
The code below should work from my perspective (and also according to the very few posts I found regarding direct binding), however it does not:
<sdk:TreeView ItemsSource="{Binding Path=Elements}" DataContext="{Binding Path=Data}">
<sdk:TreeView.ItemTemplate>
<data:HierarchicalDataTemplate ItemsSource="{Binding Path=Elements}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</data:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:Treeview>
(Data is a Property of type XElement on the parents' DataContext)
Did I make a mistake somewhere or do I really need to implement an IValueConverter just to get at the child elements of an XElement?
The "Elements" member is not a Property, It's a Method call.
You cannot bind to method calls in Silverlight.
If you're really bent on getting this scenario to work you've got 2 options I can see:
1. Use an IValueConverter to extract the contents of the "Elements" method.
2. Wrap the XDocument in managed classes in a proper hierarchy.
Personally, While option #1 seems the fastest, I believe that in the long run it'll cost you more time to maintain and support then spending an additional 10 minutes building a proper domain model.
Sincerely,
-- Justin Angel