Treeview Bindings Not Showing Second level - wpf

Hello I have a treeview that I am trying to diplay 2 levels deep.
The second level is not displaying
Here is the class and Collection from my VM
public class BuildTree
{
public int Id { get; set; }
public string Groups { get; set; }
public IEnumerable<myAreas> Areas { get; set; }
}
//Areas has a name property I want to show on the second level under the group
public ObservableCollection<BuildTree> MyObsvCollection
{
get { return _myCollection; }
}
Here is my Xaml
<TreeView ItemsSource="{Binding MyObsvCollection}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Foreground="Red" Text="{Binding Groups}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Areas}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Your HierarchicalDataTemplate has no items to display. You should move ItemsSource to outer HierarchicalDataTemplate and then ItemTemplate can be simple DataTemplate
<HierarchicalDataTemplate ItemsSource="{Binding Areas}">
<TextBlock Foreground="Red" Text="{Binding Groups}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>

Related

WPF MVVM open view depending on the type of object

For example, I have a program for editing creature's parameters. Each entity has an id and a name property. Also every entity has its own unique parameters. For editing additional parameters I created 2 views How could I place a UserControl instead of a comment and destroy a previous if it was?
class Entity {
public int Id { get; set; }
public string Name { get; set; }
}
class Ant : Entity {
public bool HasWings { get; set; }
public int BodyColor { get; set; }
//...
}
class Cat : Entity {
public bool HasWool { get; set; }
public int EyesColor { get; set; }
//...
}
And the view's code:
<Window xmlns=...>
<StackPanel>
<TextBox Text="{Binding Id}" />
<TextBox Text="{Binding Name}" />
<!-- Place for UserControl -->
</StackPanel>
</Window>
<UserControl ...>
<StackPanel>
<Checkbox IsChecked="{Binding HasWings}" />
<TextBox Text="{Binding BodyColor}" />
</StackPanel>
</UserControl>
<UserControl ...>
<StackPanel>
<Checkbox IsChecked="{Binding HasWool}" />
<TextBox Text="{Binding EyesColor}" />
</StackPanel>
</UserControl>
You can use a ContentControl and data templates instead of UserControls. The ContentControl will show the appropriate data template for the given type and responds to data context changes.
<Window xmlns=...>
<StackPanel>
<TextBox Text="{Binding Id}" />
<TextBox Text="{Binding Name}" />
<ContentControl Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:Ant}">
<StackPanel>
<CheckBox IsChecked="{Binding HasWings}" />
<TextBox Text="{Binding BodyColor}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Cat}">
<StackPanel>
<Checkbox IsChecked="{Binding HasWool}" />
<TextBox Text="{Binding EyesColor}" />
</StackPanel>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</StackPanel>
</Window>

ListView SelectedItem does not get sychronized

I have following ListView and using MVVM Light
<ListView ItemsSource="{Binding Combinations, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Combination, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource UserValueStyle}" SelectionMode="Single"
IsSynchronizedWithCurrentItem="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Text}" FontSize="20" Height="80" Width="100" FontWeight="Bold"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.OnUserValuePressed}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The property:
public SapInspectionUserValue Combination
{
get
{
return _combination;
}
set
{
_combination = value;
RaisePropertyChanged();
}
}
The class property:
public class SapInspectionUserValue
{
public string Combination { get; set; }
public string Value { get; set; }
public string Text { get; set; }
}
My problem is, when I select an item(in my case, press the button) the SelectedItem does not get synchronized and I do not know why. Whats is wrong?

Multiple levels of binding in WPF ItemsControl

How can i bind multiple levels of data in same time like List of chapters and under each chapter list of pages.
The class structure and xaml i used is shown here
public class ContentsPage
{
public string cname{ get; set; }
public string label { get; set; }
}
public class Chapter
{
public string name { get; set; }
public string id { get; set; }
public List<ContentsPage> pages { get; set; }
}
public class Model
{
public List<Chapter> chapters { get; set; }
}
<ItemsControl x:Name="TopLevelListBox" ItemsSource="{Binding}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander ExpandDirection="Down" Width="175">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=name}" Margin="0,0,5,0"/>
</StackPanel>
</Expander.Header>
<ListBox x:Name="SubListBox" ItemsSource="{Binding Path=enrichments}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=cname}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For binding i used the code
Model data = new Model(); //load data
TopLevelListBox.DataContext = data.chapters;
Only my expander headers are filled with result. What i need to do fill the pages inside the expander ? Any ideas or samples link for doing the same
Your ItemsControl Item template should change a bit. Instead of setting the ListBox as Content of Expander, set it as Content Template.
<Expander ExpandDirection="Down"
Width="175" Content="{Binding}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=name}"
Margin="0,0,5,0" />
</StackPanel>
</Expander.Header>
<Expander.ContentTemplate>
<DataTemplate>
<ListBox x:Name="SubListBox"
ItemsSource="{Binding Path=enrichments}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=cname}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</Expander.ContentTemplate>
</Expander>
And make sure you have sub items to show in ListBox.

WPF Relative Binding using hierarchical collection structure

I have an ObservableCollection containing an ObservableCollection:
public class BooksDetailModel
{
public BookModel Book{ get; set; }
public ObservableCollection<AuthorModel> Authors { get; set; }
}
Property in ViewModel:
public ObservableCollection<BooksDetailModel> Books { get; set; }
I would like to render this in a ListBox like this:
Book1
Author1
Author2
Book2
Author1
etc.
The top level bind is easy but I am having trouble with the inner child collection.
XAML so far:
<ListBox ItemsSource="{Binding Books}" BorderBrush="{x:Null}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Book.Name}" FontSize="12" FontWeight="Bold" />
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ???, Path=Author.Name}" FontSize="10" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Any suggestions for ??? - Relative binding the inner listbox itemsource to the parent itemsource's Authors collection.
You should bind the ItemsSource of the inner ListBox to the Authors property. And the binding in the DataTemplate will be simply binding to the Name property of the author:
<ListBox ItemsSource="{Binding Books}" BorderBrush="{x:Null}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Book.Name}" FontSize="12" FontWeight="Bold" />
<ListBox ItemsSource="{Binding Authors}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontSize="10" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Question on Hierarchial data template in WPF Tree

I have a WPF tree which needs to show some nodes. Lets say I have 2 types of entities, EntityA and EntityB. Both these entities implement a common interface IEntity. Now, EntityA will have a collection of EntityB elements as well as EntityA elements. How can I show this via a HierarchicalDataTemplate ?
I am exposing a ObservableCollection() called "DisplayItems" in my VM that will contain elements of EntityA type.
Both EnittyA and EntityB will have another ObservableCollection called "ItemCollection". For EntityA, the ItemCollection list should ideally contain entities of EntityA and EntityB types.
The current HierarchicalDataTemplate and the XAML that I am using is as follows:
<HierarchicalDataTemplate ItemsSource="{Binding Path=ItemCollection}" DataType="{x:Type Entities:EntityB}">
<Grid>
<StackPanel Orientation="Horizontal" x:Name="compositeCT">
<Image Source="/Images/EntityB.png" Width="15" Height="15"/>
<Label Foreground="Blue" Content="{Binding Path=Name}"/>
<Label Foreground="Black" Content=" = "/>
<Label Foreground="Blue" Content="{Binding Path=CompositeLabel}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" x:Name="nCompositeCT">
<Image Source="/Images/EntityB.png" Width="15" Height="15"/>
<TextBlock Foreground="Blue" Text="{Binding Path=Name}"/>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="Green" Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=ItemCollection}" DataType="{x:Type Entities:EntityA}">
<StackPanel Orientation="Horizontal" >
<Image Source="/Images/ElementA.png" Margin="3" Width="15" Height="15" Focusable="False"/>
<TextBlock Foreground="Red" Text="{Binding Path = Name}" Focusable="False"/>
</StackPanel>
</HierarchicalDataTemplate>
<TreeView x:Name="tvMyTree"
ItemsSource="{Binding DisplayItems}"
AllowDrop="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
ScrollViewer.IsDeferredScrollingEnabled="True"
Margin="5"
TreeViewItem.Expanded="OnTreeViewItemExpanded"
TreeViewItem.Selected="OnTreeViewItemSelected"
/>
You can define two HierarchicalDataTemplates you are fine. And in the place of TextBlock you can put whatever complex visualization you need depending on the other Properties of your EntityA and EntityB
<HierarchicalDataTemplate DataType="{x:Type local:EnittyA}" ItemsSource="{Binding ItemCollection}" >
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:EnittyB}" ItemsSource="{Binding ItemCollection}" >
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
I suppose ItemTemplateSelector fits perfect to your requirements. ItemTemplateSelector is inherited, so you should not care about endpoint receiver of template. Receiver (item container) just addresses to selector and the last one returns proper template according to DataType:
public class LayoutItemTemplateSelectorItem
{
public Type TargetType
{
get;
set;
}
public DataTemplate Template
{
get;
set;
}
}
[ContentProperty("Items")]
public class LayoutItemTemplateSelector : DataTemplateSelector
{
public LayoutItemTemplateSelector()
{
this.Items = new Collection<LayoutItemTemplateSelectorItem>();
}
public Collection<LayoutItemTemplateSelectorItem> Items
{
get;
private set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var component = (LayoutItem)item;
var typeToSearch = component.GetType();
var foundItem = this.Items
.Where(i => i.TargetType == typeToSearch)
.FirstOrDefault();
if (foundItem != null)
{
return foundItem.Template;
}
throw new Exception(string.Format(Properties.Resources.AppropriateTemplateNotFound, typeToSearch.FullName));
}
}
Usage in XAML:
<UserControl ...>
<UserControl.Resources>
<ResourceDictionary>
<HierarchicalDataTemplate x:Key="EntityBTemplate"
ItemsSource="{Binding Path=ItemCollection}"
DataType="{x:Type Entities:EntityB}">
...
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="EntityATemplate"
ItemsSource="{Binding Path=ItemCollection}"
DataType="{x:Type Entities:EntityA}">
...
</HierarchicalDataTemplate>
<LayoutItemTemplateSelector x:Key="TemplateSelector">
<LayoutItemTemplateSelectorItem TargetType="{x:Type EntityA}"
Template="{StaticResource EntityATemplate}"/>
<LayoutItemTemplateSelectorItem TargetType="{x:Type EntityB}"
Template="{StaticResource EntityBTemplate}"/>
</LayoutItemTemplateSelector>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<TreeView ItemsSource="{Binding DisplayItems}"
ItemTemplateSelector="{StaticResource TemplateSelector}"/>
</Grid>
</UserControl>

Resources