I'm trying to populate a TreeView in a TestExplorerControl:
I never had to use a CollectionViewSource until now, so I used this tutorial to get a grasp of how to group my ObservableCollection<TestMethod> in XAML, and use that grouping for my tree view - I implemented the data templates in the <UserControl.Resources>, because I want the flexibility to eventually allow the user change the way tests are regrouped:
<CollectionViewSource x:Key="OutcomeGroupViewSource" Source="{Binding Model.Tests}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Result.Outcome" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<DataTemplate x:Key="TestMethodTemplate" DataType="{x:Type local:TestMethod}">
<StackPanel Orientation="Horizontal">
<Image .../>
<TextBlock .../>
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="OutcomeTemplate" DataType="{x:Type CollectionViewGroup}"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource TestMethodTemplate}">
<StackPanel Orientation="Horizontal">
<Image ../>
<TextBlock ../>
<TextBlock ../>
</StackPanel>
</HierarchicalDataTemplate>
Then I have this markup for the actual <TreeView>:
<TreeView Grid.Row="2"
ItemsSource="{Binding Source={StaticResource OutcomeGroupViewSource}, Path=View.Groups}"
ItemTemplate="{StaticResource OutcomeTemplate}" />
What's wrong with this markup, for the TreeView to fail updating? The ViewModel clearly has all the data I need to display (the breakpoint that was hit is on the current line, in yellow):
Found it. It's the Path of the ItemsSource binding in the tree view:
ItemsSource="{Binding Source={StaticResource OutcomeGroupViewSource}, Path=View.Groups}"
Path=View.Groups satisfies IntelliSense, but is wrong.
It needs to be Path=Groups, even if the designer supposedly can't resolve the property:
Related
Consider a Tasks class:
TaskID
ParentID
Title
<several other properties>
Description
SubTasks <-- a collection of tasks
First I populate a hierarchy of Tasks called AllTasks and then load it into the TreeView by:
TaskTree.DataContext = AllTasks
That works fine. Now I want to populate several other controls with the Task data when the user clicks on a Task from the TreeView. I'll just consider the Description property as that is sufficient to illustrate the problem.
My TreeView is defined as follows:
<TreeView
x:Name="TaskTree"
SelectedValuePath="Task">
<TreeView ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=SubTasks}">
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</TreeView>
The TextBox that should display the Description property of the Task selected by the user is defined by:
<DockPanel
DataContext="{Binding ElementName=TaskTree}">
<TextBox
x:Name="txtDescription"
Text="{Binding Path=SelectedItem.Discussion}"
</DockPanel>
Nothing is showing up in the TextBox. I tried setting....
Text="{Binding Path=Discussion}"
... but that doesn't work either. I've tried other combinations but to no avail. What works?
You don't select any Task in the outer TreeView named "TaskTree". Remove the outer one:
<TreeView ItemsSource="{Binding}" x:Name="TaskTree">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Task}" ItemsSource="{Binding Path=SubTasks}">
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
...and then try to bind to its SelectedItem property:
<TextBox x:Name="txtDescription" Text="{Binding Path=SelectedItem.Discussion, ElementName=TaskTree}" />
I tried to implement treeview with 4 levels using WPF/C#.Net 4.0.It loads all 4 levels but can't select 4th level and when selecting 3rd level it select group with 4th level.
Continent->Country->District->Artifacts is one structure but there is another one Continent->Products->Artifacts
Resource DataTemplates->
<DataTemplate x:Key="DistrictTemplates">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=ArtifactName}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="CountryTemplate">
<TreeViewItem ItemsSource="{Binding Path=District}"
ItemTemplate=" {StaticResource DistrictTemplates}"
Header="{Binding Path=Code}">
</TreeViewItem>
</DataTemplate>
TreeView code->
<TreeView Name="treeExplorer" MouseDoubleClick="TreeView_MouseDoubleClick" SelectedItemChanged="treeExplorer_SelectedItemChanged">
<TreeViewItem Name="tviDefinition" IsExpanded="True" Header="Continent">
<TreeViewItem ItemsSource="{Binding Path=Country}" ItemTemplate="{StaticResource CountryTemplate}" Header="Country" />
</TreeViewItem>
</TreeView>
there are some other treeItems as well.I can't use inline template inside the TreeView.Resources and also im confused if can use this HierarchicalDataTemplate sine i cant call Country.Districts.ArtifactName and got two hierarchies but I can call Country.Districts() and then Districts has code property and using code i can find Artifacts.And im using datatemplates inside usercontrol.resources
How would I be able to do this?
Here's something I've written that goes 4 levels deep. It's a TreeView which shows HL7 message structure. For a quick background HL7 is a field separated message. You have a message. Each message has a Segment. Each Segment has at least one field. A field could have multiple components. A component can have subcomponents. This tree displays the HL7 message structure, where each level is a part of the HL7 message format.
For example, if there is PID segment in the message the tree would like this:
PID
...PID.1
...PID.2
......PID.2.1
......PID.2.2
......PID.2.3
......PID.2.4
.........PID.2.4.1
.........PID.2.4.2
etc...
Here is the XAML:
<TreeView x:Name="hl7Structure" ItemsSource="{Binding Path=MessageSegments}" IsEnabled="True">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type MyNamespace:MessageSegment}" ItemsSource="{Binding Path=Fields}">
<TextBox x:Name="segmentName" BorderBrush="Transparent" BorderThickness="0" Text="{Binding Path=Name}" FocusVisualStyle="{x:Null}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type MyNamespace:MessageField}" ItemsSource="{Binding Path=Components}">
<TextBlock x:Name="fieldName" Text="{Binding Path=Name}" ToolTip="{Binding Path=Info}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type MyNamespace:MessageComponent}" ItemsSource="{Binding Path=Subcomponents}">
<TextBlock x:Name="componentName" Text="{Binding Path=Name}" ToolTip="{Binding Path=Info}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type MyNamespace:MessageSubcomponent}">
<TextBlock x:Name="subComponentName" Text="{Binding Path=Name}" ToolTip="{Binding Path=Info}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
Now the explanation of how it works. I have a base object that each HL7 message piece inherits. The ItemsSource of the TreeView is bound to that collection. Since there are 4 levels, where 3 show hierarchy and one that does not, there are 3 HierarchicalDataTemplates and 1 DataTemplate.
Think of it this way...The HL7 Message Segment, Field, and Components are tree nodes because they have children. The HL7 subcomponent is a leaf, because it has none. Each tree node gets a HierarchicalDataTemplate, but each leaf just gets a DataTemplate.
Each of HierarchicalDataTemplates know what object type to display by using the DataType property. Here is where I tell the control, the child type it's displaying. This allows me to use the base type collection and then display all the child types at their appropriate node levels.
Hope this helps.
Finally I managed to solve this.Thanks Josh and everyone.
Resource DataTemplates->
<DataTemplate x:Key="DistrictTemplates">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=ArtifactName}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="CountryTemplate" DataType="Continent.Countries" ItemsSource="{Binding Path=District}" ItemTemplate="{StaticResource DistrictTemplates}">
<TextBlock Text="{Binding Path=Code}"/>
</HierarchicalDataTemplate>
TreeView code->
<TreeView Name="treeExplorer" MouseDoubleClick="TreeView_MouseDoubleClick" SelectedItemChanged="treeExplorer_SelectedItemChanged">
<TreeViewItem Name="tviDefinition" IsExpanded="True" Header="Continent">
<TreeViewItem ItemsSource="{Binding Path=Country}" ItemTemplate="{StaticResource CountryTemplate}" Header="Countries" />
</TreeViewItem>
</TreeView>
I have a datagrid nested inside the ItemTemplate of a ListBox. I'm trying to display a tree like data structure using this. My classes are as follows.
The object in my data context contains a List<Section> named Sections, my ListBox is bound to this. Each Section contains a List<Item> named Items, the DataGrid in eac ItemTemplate is bound to this.
When I run the app, I get a null reference exception from the XAML at the line with the binding. Is there a better/alternative way of doing this, or am I missing a trick with the binding?
<Window.Resources>
<CollectionViewSource x:Key="SectionSource" /><!-- this is initialized and filled with an ObservableCollection<Section> Sections when the window loads-->
</Window.Resources>
<ListBox x:Name="lstIngredients" ItemsSource="{Binding Source={StaticResource SectionSource}}">
<ListBox.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<CollectionViewSource x:Key="itemsSource" Source="{Binding Items}"/>
</DataTemplate.Resources>
<DataGrid x:Name="dgItems" IsReadOnly="false" AutoGenerateColumns="False" SelectionMode="Single" SelectionUnit="FullRow" IsSynchronizedWithCurrentItem="True"
DataContext="{Binding}"
ItemsSource="{Binding Source={StaticResource Items}}"
EnableRowVirtualization="false"
VirtualizingStackPanel.VirtualizationMode="Standard"
<DataGrid.Columns>
<DataGridTemplateColumn Width="2*" Header="{lex:LocText ChickenPing.Shared:Strings:Measurement}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="quantity" Text="{Binding Measurement}" TextTrimming="CharacterEllipsis" TextAlignment="Left"/>
<!-- Null reference on this line caused by the binding. If I set this to any DependencyProperty on an Item object, I get a null reference-->
</DataTemplate>
This need to be path
ItemsSource="{Binding Source={StaticResource Items}}"
ItemsSource="{Binding Path=PropertyThatIsCollection}"
And delete the DataContext line
I eventually tracked this down to an event which was set in one of the TemplateColumns. Switching the event from
<TextBlock x:Name="quantity" Text="{Binding Measurement}" GotFocus="txt_GotFocus" />
to
<Style x:Key="FocusableTextbox" TargetType="{x:Type TextBox}">
<EventSetter Event="GotFocus" Handler="txt_GotFocus" />
</Style>
...
<TextBlock x:Name="quantity" Text="{Binding Measurement}" Style={StaticResource FocusableTextbox} />
I have a tree view with bind to some ObservableCollection, which is filled with some asyncronous function. The problem is that it's not getting updated on UI after the asyncronous command worked and updated the source collection (added some child nodes or something like that). My XAML looks like this:
<StackPanel>
<StackPanel.Resources>
<HierarchicalDataTemplate x:Key="CheckBoxItemTemplate"
ItemsSource="{Binding Children, Mode=TwoWay}">
<StackPanel Orientation="Horizontal">
<CheckBox Focusable="False" IsChecked="{Binding IsChecked, Mode=TwoWay}"
VerticalAlignment="Center" />
<ContentPresenter Content="{Binding Node.Caption, Mode=OneWay}" />
</StackPanel>
</HierarchicalDataTemplate>
</StackPanel.Resources>
<TreeView Style="{DynamicResource FormItem}" ItemsSource="{Binding Nodes, Mode=TwoWay}"
ItemTemplate="{StaticResource CheckBoxItemTemplate}" >
</TreeView>
</StackPanel>
Any suggestions?
Are you sure you're updating your UI on the correct thread?
Sorry for bothering you, guys. It was a silly mistake of mine. I just didn't set DataContexts of one control and of the window using this control. So it turned out that they had different contexts, because my ViewModel isn't a singleton. I should have been more careful about it.
I have a listbox and I want to iterate over a collection of Bars in my Foo-object.
<ListBox DataContext="{Binding Path=Foo.Bars}" >
<ListBox.Items>
<ListBoxItem>
<ContentControl DataContext="{Binding Path=.}" />
</ListBoxItem>
</ListBox.Items>
</ListBox>
This is the datatemplate I want to use.
<DataTemplate DataType="{x:Type Bar}">
<Label Content="hello stackoverflow" />
</DataTemplate>
If I snoop (--> examine by using the tool Snoop) my application, I notice that the entire collection of Bars is bound to the ContentControl, in stead of just 1.
How can I properly bind so the iteration over the collection goes fine?
You can just set the DataTemplate, and WPF does all the work. Set the ItemsSource to a list of Bar items, and then define a DataTemplate for Bar items.
<ListBox ItemsSource="{Binding Path=Foo.Bars}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type Bar}">
<Label Content="hello stackoverflow" />
</DataTemplate>
</ListBox.Resources>
</ListBox>
You could also set the ItemsTemplate directly by using <ListBox.ItemTemplate> instead of <ListBox.Resources>
See Data Binding Overview at MSDN.
First add your namespace to the Window element (Intellisense) :
xmlns:local="clr-namespace:yourenamespace"
Then the following XAML ( in Window.Resources is a clean way to do it ) :
<Window.Resources>
<ObjectDataProvider x:Key="DataProvider" ObjectType="{x:Type local:Foo}"/>
<DataTemplate x:Key="Template" >
<TextBlock Text="{Binding Bar}"/>
</DataTemplate>
</Window.Resources>
Place the Listbox :
<ListBox DataContext="{Binding Source={StaticResource DataProvider}}" ItemsSource="{Binding Bars}" ItemTemplate="DynamicResource Template" />
But, it depends on your code-behind object, you have to set a constructor to initialise public properties within your object which are ObservableCollection<> preferably (There is some restriction rules with object instance in XAML).