I've a problem building up a ComponentOne TreeView in Silverlight (C1TreeView) with a C1HierarchicalDataTemplate. In detail the Tree only shows 2 levels (H1 and H2), although 3 levels are defined through HierarchicalDataTemplates like:
<c1:C1HierarchicalDataTemplate x:Key="H3Template">
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
</c1:C1HierarchicalDataTemplate>
<c1:C1HierarchicalDataTemplate x:Key="H2Template" ItemsSource="{Binding Path=H3Items}" ItemTemplate="{StaticResource H3Template}">
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
</c1:C1HierarchicalDataTemplate>
<c1:C1HierarchicalDataTemplate x:Key="H1Template" ItemsSource="{Binding Path=H2Items}" ItemTemplate="{StaticResource H2Template}">
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
</c1:C1HierarchicalDataTemplate>");
I'm using this Templates in a Custom TreeView (derived from C1TreeView):
<c1:C1TreeView ... ItemTemplate="{StaticResource H1Template}">
</c1:C1TreeView>
The constructor of this TreeView looks like this:
public MyTreeView(ObservableCollection<H1> h1Items)
{
InitializeComponent();
ItemsSource = h1Items;
}
Can anybody see the error in these code snippets??
thx, Dom
While I'm unfamiliar with the ComponentOne TreeView that you're using, and despite the fact that you are using Silverlight, normally in WPF when you are using HierarchicalDataTemplates, you tell the template what type it's for. Sub-item templates are similarly told what type they apply to. You don't specifically tell the data template what template to use for it's ItemTemplate. That is automatically figured out by the system based on the type of object. This also applies when you bind an item collection to the TreeView--you don't have to specify the ItemTemplate.
So in your case (local: is a namespace defined at the top of your xaml):
<c1:C1HierarchicalDataTemplate DataType="{x:Type local:H1}"
ItemsSource="{Binding Path=H2Items}">
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
</c1:C1HierarchicalDataTemplate>
<c1:C1HierarchicalDataTemplate DataType="{x:Type local:H2}"
ItemsSource="{Binding Path=H3Items}">
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
</c1:C1HierarchicalDataTemplate>
<c1:C1HierarchicalDataTemplate DataType="{x:Type local:H3}">
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
</c1:C1HierarchicalDataTemplate>
And the TreeView:
<c1:C1TreeView ItemsSource="{Binding SomeH1List}"/>
Of course, as I said, this applies to WPF, so it might not apply in your case.
Related
I have ListBox that has binding to the collection (Publishers), every publisher has a name and collection of authors in it, every author has a name. So how can I provide binding to author's name?
<TextBlock Text="{Binding name}" Grid.Column="1" Grid.Row="1"></TextBlock>
<TextBlock Text="{Binding city}" Grid.Column="1" Grid.Row="0"></TextBlock>
It is binding to a publisher's name and city.
Something like this provided that the Publisher class, or whatever you call it, exposes the authors using a public collection property that has an indexer:
<ListBox x:Name="listBox" ItemsSource="{Binding Publishers}" .... />
<TextBlock Text="{Binding SelectedItem.Authors[0].Name, ElementName=listBox}" Grid.Column="1" Grid.Row="0"></TextBlock>
This will the display the name of the first author at index 0.
If you intend to display all authors of the selected publisher, you should use an ItemsControl:
<ItemsControl ItemsSource="{Binding SelectedItem.Authors, ElementName=listBox}"
Grid.Column="1" Grid.Row="0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding name}"></TextBlock>
<ListBox ItemsSource="{Binding authors}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This will show name of a publisher with a list of all its authors for every publisher. I assumed a publisher has a property named authors that returns IEnumerable<Author>
An author would be an element in a collection which is part of a publisher.
So there is no way for WPF to 'know' which author's name to display: there are multiple.
Instead of a textblock, an ItemsControl could be used that binds its ItemsCollection to the collection of authors in the current publisher.
i am completly new to WPF and need your help. I followed many tutorials but nothing works.
I have two ObserveableLists L1 and L2 to bind and I want to archiev:
On depth 1 - The first List and for each child list 2.
L1.1
L2.1
L2.2
L2.3
L1.2
L2.1
L2.2
L2.3
L1.3
L2.1
L2.2
L2.3
Update 16.02.07 - 16:44
My first try:
<TreeView Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" Margin="10" ItemsSource="{Binding orderCities}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type model:city}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding products}" DataType="{x:Type model:Product}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
My second try: Defining two DataTemplates how to handle my types:
<UserControl.Resources>
<HierarchicalDataTemplate DataType="{ x:TypeExtension model:city }" ItemsSource="{Binding orderCities}">
<StackPanel>
<TextBlock Text="{Binding name}" />
<!-- Here embed Product Type (Dont know how)-->
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{ x:TypeExtension model:Product }" ItemsSource="{Binding products}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
<TextBlock Text=" - " />
<TextBox Text="1" />
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
And testet both templates and got the right design.
<TreeView ItemsSource="{Binding orderCities}" />
<TreeView ItemsSource="{Binding orderCities}" />
The treeview alone won't help you here. You have to actually create the data you want to display, i.e. the treeview won't "multiply" the two lists.
Each element in the first list may of course return the same instance of the second list in its children-property. You might want to have look at this codeproject article, it's rather old, but gives a nice introduction on how to use the wpf treeview.
I have a TreeView that looks like this:
<TreeView Grid.Row="1" x:Name="InspectionResultsTreeView"
ItemsSource="{Binding Source={StaticResource InspectionTypeGroupViewSource}, Path=Groups}"
ItemTemplate="{StaticResource InspectionTypeGroupsTemplate}">
</TreeView>
The ItemsSource is a keyed resource that goes by the name of InspectionTypeGroupViewSource:
<CollectionViewSource x:Key="InspectionTypeGroupViewSource" Source="{Binding Results}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Inspection.InspectionType" />
<PropertyGroupDescription PropertyName="Inspection" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
The role of this little thing is to take the ViewModel's Results property:
private ObservableCollection<ICodeInspectionResult> _results;
public ObservableCollection<ICodeInspectionResult> Results
{
get { return _results; }
set { _results = value; OnPropertyChanged(); }
}
...and group it on two levels - first by InspectionType, then by Inspection - the result is a 3-level hierarchy with inspection types, inspections, and then individual inspection results. At this point a screenshot might help visualizing I guess:
So, the ItemTemplate of the InspectionResultsTreeView is another keyed resource, by the name of InspectionTypeGroupsTemplate - that's the bold "inspection type" items:
<HierarchicalDataTemplate x:Key="InspectionTypeGroupsTemplate"
DataType="{x:Type CollectionViewGroup}"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource InspectionGroupsTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{Binding Name}"
FontWeight="Bold"
TextWrapping="NoWrap"/>
<TextBlock Margin="4,0,4,0"
VerticalAlignment="Center"
Text="{Binding ItemCount, StringFormat=({0})}"
TextWrapping="NoWrap"/>
</StackPanel>
</HierarchicalDataTemplate>
And the ItemTemplate of that template is an InspectionGroupsTemplate - that's the individual inspections, with the "severity" icons:
<HierarchicalDataTemplate x:Key="InspectionGroupsTemplate"
DataType="{x:Type CollectionViewGroup}"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource InspectionResultTemplate}">
<StackPanel Orientation="Horizontal">
<Image Style="{StaticResource IconStyle}"
Source="{Binding Name, Converter={StaticResource InspectionIconConverter}}"
VerticalAlignment="Center" />
<TextBlock Margin="4"
VerticalAlignment="Center"
Text="{Binding Name, Converter={StaticResource InspectionDescriptionConverter}}"
TextWrapping="NoWrap"/>
<TextBlock Margin="0,4,0,4"
VerticalAlignment="Center"
Text="{Binding ItemCount, StringFormat=({0})}"
TextWrapping="NoWrap"/>
</StackPanel>
</HierarchicalDataTemplate>
Lastly, the ItemTemplate of this grouping is an InspectionResultTemplate, which is for each individual inspection result:
<DataTemplate x:Key="InspectionResultTemplate"
DataType="{x:Type inspections:ICodeInspectionResult}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Margin="4"
Text="{Binding Name}"
TextWrapping="NoWrap"/>
</StackPanel>
</DataTemplate>
The ICodeInspectionResult interface has a string Name property that I'm using here; this Name is different from the Name that's used in the grouping levels, where it's an object CollectionViewGroup.Name - the underlying type of that Name is that of the grouping, so level 1 is an InspectionType, and level 2 is an Inspection.
The problem is that I'm using more converters than I believe I'd need to, to convert this object Name and access the members I need to access and display... but then, I need to display the number of items in each grouping so the DataType ought to be a CollectionViewGroup... right?
How can I do this without resorting to a converter for everything that needs to be displayed? How is this supposed to be done? Every TreeView / CollectionViewGroup tutorial I could find was a trivial implementation.
You've encountered the iconic problem with XAML: it's almost too structured.
The most idiomatic solution is writing a custom WPF User Control. (How and what you include in it is up you.) The goal of the WPF User Control is to eliminate the duplicate XAML markup and logic. You can include your Converter in the User Control, and eliminate the converters from your main control.
There are plenty of tutorials on creating UserControl objects in WPF, so I'll not go into detail here.
As far as the Converter issue: this is almost the most idiomatic way. Each converter is reusable, and focuses only on one source type. There's not much else you can do about it, except consider merging converters that support the same source type together. (There's a reason the converter has a Type targetType parameter, and an object parameter.)
I try to have a treeview that browse potentially cyclic hierarchical data.
This means that I cannot try to load all the tree at once, since there maybe be infinite loops then.
I would like to react to the TreeView.AfterCollapse Event documented here at MSDN
however, my control doesnt seem to have this event. If I try to add the AfterExpand attribute, i get this error message:
error MC3072: The property 'AfterExpand' does not exist in XML namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'. Line 23 Position 21.
What I am doing wrong ? Calling a wrong namespace ?
Here is the code:
<Window x:Class="MyApp.Edit.EditView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp.Edit"
Title="{Binding WindowTitle,UpdateSourceTrigger=PropertyChanged}" MinHeight="350" MinWidth="350">
<Window.Resources>
<HierarchicalDataTemplate x:Key="sectionTemplate"
ItemsSource="{Binding ChildSections}"
DataType="{x:Type local:Section}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Label, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text=" - " />
<TextBlock Text="{Binding Description}" FontStyle="Italic" Foreground="#777" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<StackPanel>
<TreeView ItemsSource="{Binding Sections}"
SelectedItemChanged="TreeView_SelectedItemChanged"
ItemTemplate="{StaticResource sectionTemplate}"
MinHeight="150"
MinWidth="300"
Name="treeView"
AfterExpand="MyEventHandler"
</TreeView>
<TextBox Text="{Binding Label, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0 10 0 0"/>
<TextBox Text="{Binding Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0 10 0 0"/>
<StackPanel Orientation="Horizontal">
<Button Content="Add Child" Click="Button_Click_AddChild" />
</StackPanel>
</StackPanel>
</Window>
That is a Windows Forms tree view event, it does not belong to the WPF TreeView, in WPF you can use Collapsed and Expanded of the TreeViewItems, not the TreeView itself.
However you can subscribe to the events on the TreeView as they are routed.
I have a textblock which is inside a listbox and I am trying to write an if statement which is dependant on the contents of this textblock. I am trying to get the data from the TextBlack which I have named "category1" however when I try to write my if statement I am getting a message which just says
"the name category1 does not exist in the current context"
I tired moving that TextBLock out of the ListBox and it works fine but wont work while its inside there. Does anyone know how to reference this textblock.
Here is the my XAML code
<ListBox x:Name="HINList" Margin="0,300,-12,0" ItemsSource="{Binding Details}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding HINNumber}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding CategoryLetter}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="category1" Text="{Binding Category1}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="{Binding Category2}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="{Binding Category3}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Assuming you're writing your if statement in the code behind file, wouldn't something like:
if(((WhateverTypeIsInDetailsCollection)HINList.SelectedItem).Category1 == something) {
// then do whatever you want
}
As Russell pointed out there is a category1 item for every entry in the list. I assume you wanted to do something with the selected item.
This is due to xaml namescopes. The names inside a DataTemplate are in a different namescope than outside, that's why you can't access them (what #Russell pointed is part of why it's done this way).
I think that you want to access that field for the "Category1" property on the selected item of the HINList ListBox that is bound to the Details collection. What you can do is set the binding on the Category1 to be two way, and bind the SelectedItem of the ListBox to a Detail item like so:
xaml:
<ListBox x:Name="HINList" ItemsSource="{Binding Details}"
SelectedItem={Binding SelectedDetailItem, Mode=TwoWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding Category1, Mode=TwoWay}" TextWrapping="Wrap" .../>
<!-- the other fields -->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
code-behind
if(SelectedDetailsItem.Category1==...)
{
....
}
Hope this helps :)