HierarchicalDataTemplate issue - wpf

I need some help with HierarchicalDataTemplate...
I am trying to build a TreeView to display some hierarchical data
like this:
RuleSet <- (root)
-RuleA
RuleB
RuleC
RuleA
.....
RuleD
RuleA, ... are derived from the same RuleBase that has a
Type
RuleBase[] Rules
RuleSet has
Name
List
my code as far as I get:
<TreeView x:Name="ruleSetTree" Margin="0,10,0,0" ItemsSource="{Binding Path=SelectedTypeRuleSet>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type engine:RuleSet}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate x:Name="leafTemplate"
ItemsSource="{Binding Path=Rules}"
DataType="{x:Type engine:RuleBase}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock x:Name="hierarchyItem" Text="{Binding Path=TargetType}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
What I see now is the Name of RuleSet. The toggle button of TreeView is hidden.
I deserialize an xml into my RuleSet model to get the data for this TreeView.
The RuleSet is filled in correctly...can anyone give a hand please?
Thank you!

There is no ItemsSource specified in the first HierarchicalDataTemplate. Shouldn't you bind it to the List property of your RuleSet ?

Why do you nest another hierarchical data template into the existing one? That may be the mistake. Especially because you didn't specify an ItemSource in your first data template. If all the nodes are of type RuleSet you can do it like this:
<TreeView x:Name="ruleSetTree" Margin="0,10,0,0" ItemsSource="{Binding Path=SelectedTypeRuleSet>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type engine:RuleSet}"
ItemsSource="{Binding Path=Rules}">
<StackPanel>
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=TargetType}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
UPDATE:
Here's an updated version, which should match your requirements. This just works, though, if all the child nodes are of type RuleBase
<UserControl.Resources>
<HierarchicalDataTemplate x:Key="RuleBaseTemplate"
ItemsSource="{Binding Rules}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="RuleSetTemplate"
ItemsSource="{Binding Rules}"
ItemTemplate="{StaticResource RuleBaseTemplate}">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding TargetType}"/>
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<!-- rest of the code -->
<TreeView x:Name="ruleSetTree" Margin="0,10,0,0"
ItemsSource="{Binding SelectedTypeRuleSet}"
ItemTemplate="{StaticResource RuleSetTemplate}"/>

Related

WPF binding child controls DataContext to HierarchicalDataTemplate ItemsSource item

I have a collection of Barcodes that contains a collection of Positions. How do I set the DataContext of a custom control to a Position item in the collection?
<TreeView ItemsSource="{Binding SelectedPlate.Barcodes}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Positions}"
DataType="{x:Type ControlViewModels:BarcodeViewModel}">
<TextBox Text="{Binding Code}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<!--The Custom Control that needs to bind to the Position item-->
<ControlViews:PositionControl DataContext="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The root element in the ItemTemplate inherits the DataContext so you can remove DataContext="{Binding}".
<TreeView ItemsSource="{Binding SelectedPlate.Barcodes}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Positions}"
DataType="{x:Type ControlViewModels:BarcodeViewModel}">
<TextBox Text="{Binding Code}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<ControlViews:PositionControl />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
In the PositionControl you can then bind directly to a public property of a Position object assuming you don't explicitly set the DataContext of the control somewhere.

TreeView with multiple levels

Of the following code
<TreeView Name="tree" ItemsSource="{Binding Path=AllNotes}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type m:Note}" ItemsSource="{Binding Path=ListIssuesType}">
<TextBlock Text="{Binding Path=SoftwareVersion}" Margin="2" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type m:IssueType}" ItemsSource="{Binding Path=IssueNames}">
<TextBlock Text="{Binding Path=IssueTypeName}" Margin="2" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type m:IssueType}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=IssueTypeName}" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
I get the error:
"Item has already been added. Key in dictionary: 'DataTemplateKey(ReleaseNotes_Window.Models.IssueType)' Key being added: 'DataTemplateKey(ReleaseNotes_Window.Models.IssueType)'"
When you put something into a ResourceDictionary it either needs an explicit x:Key or the key will be determined by the DictionaryKeyPropertyAttribute applied on the class.
For DataTemplate it is the following:
[DictionaryKeyProperty("DataTemplateKey")]
public class DataTemplate : FrameworkTemplate
This will depend on the DataType.
Because you have:
<HierarchicalDataTemplate DataType="{x:Type m:IssueType}" ItemsSource="{Binding Path=IssueNames}">
<TextBlock Text="{Binding Path=IssueTypeName}" Margin="2" />
</HierarchicalDataTemplate>
And
<DataTemplate DataType="{x:Type m:IssueType}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=IssueTypeName}" />
</StackPanel>
</DataTemplate>
Both with DataType="{x:Type m:IssueType}", that's why the program fails.
Use an additional x:Key for one of the DataTemplates and reference it as a StaticResource where you're planning to use it.

How to build Behavior Tree editor using WPF?

We are going to develop a behavior tree editor using WPF.
However, we are totally new to WPF.
How to generate shape components that represent tree nodes and the components should be able to respond to mouse event like right mouse clicked.
Do you have any suggestions on this?
You can specify different HierarchicalDataTemplate for your tree. They get chosen automatically by classname. You can also specify one for your base type. Be sure not to use interfaces, but real classes.
So if you use different classes for your view models you can get along with something like this:
<TreeView
ItemsSource="{Binding MyTreeVariable}"
SelectedItemChanged="MyTree_SelectedItemChanged">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}" DataType="{x:Type self:MyBaseType}">
<StackPanel Orientation="Horizontal">
<Rectangle Width="16" Height="16"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}" DataType="{x:Type self:MySpecialType1}">
<StackPanel Orientation="Horizontal">
<Ellipse Width="16" Height="16"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}" DataType="{x:Type self:MySpecialType2}">
<StackPanel Orientation="Horizontal">
<!--- Triangle -->
<Polygon Points="50,0 100,100 0,100" Width="16" Height="16"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
If you like to tell the different behaviors by data, you can use DataTrigger and Setter, but I`d recommend the way shown above.

Data Binding with WPF

I'm new to WPF and this following has stumped me for a while now:
I have an observableCollection of People object in my model that is bound to my tabControl. So each my a new People object is added, a new tab is created with People.Title as the Tab's header.
Each People object has an ObservableCollection of Friend object. Inside of the Tab I would like to have a List of two textboxes, one for Friend.FirstName and another for Friend.LastName.
My first requirement is working fine, but the second one is giving me an error 'ItemsSource is already in use'
Here's my code so far:
<TabControl Name="ConversationTabs" Grid.Row="0"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource HeaderInfoTabControl}"
ContentTemplate="{StaticResource DialogueList}" />
<Window.Resources>
<DataTemplate x:Key="HeaderInfoTabControl">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
<DataTemplate x:Key="DialogueList">
<ItemsControl ItemsSource="{Binding Path=DialogueCollectionVM}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Sent}" />
<TextBlock Text="{Binding Path=DateSent}" />
<TextBlock Text="{Binding Path=Message}" />
</StackPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
I appreciate your help.
You cannot add items to an ItemsControl and use the automatic population (via ItemsSource) at the same time. If that StackPanel is supposed to be used for the items in the ItemsSource you should do this:
<ItemsControl ItemsSource="{Binding Path=DialogueCollectionVM}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Sent}" />
<TextBlock Text="{Binding Path=DateSent}" />
<TextBlock Text="{Binding Path=Message}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

How can I change Item and Content Templates based on the object type?

I have a WPF TabControl that I have set the ItemTemplate as well as the ContentTemplate. This tab control displays Call Log information based on customer tech support information.
Inside of this same control, I would also like to be able to show a ReturnAuthorization template.
I would like to swap these out based on the object type added to the TabControl's Items collection. Is this something that is possible?
I have some Pseudo-code that sort of shows what I want to pull off:
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True" ItemContainerStyle="{StaticResource ClosableTabItemTemplate}" >
<TabControl.ItemTemplate>
if ( Type is Entities:Case )
{
<DataTemplate DataType="{x:Type Entities:Case}">
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
}
else if ( Type is Entities1:RAMaster )
{
<DataTemplate DataType="{x:Type Entities1:RAMaster}">
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
}
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
One way to do this is using something like an ItemTemplateSelector, which you can set on the TabControl. However, if you only need the different templates within the TabControl and know what they all are ahead of time, then you can just let them automatically be applied by using the DataTypes.
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True" ItemContainerStyle="{StaticResource ClosableTabItemTemplate}" >
<TabControl.Resources>
<DataTemplate DataType="{x:Type Entities:Case}">
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
<DataTemplate DataType="{x:Type Entities1:RAMaster}">
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
</TabControl.Resources>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>

Resources