Treeview Binding (With a 'Folder' between the bound Nodes) - wpf

I have following problem with which I'm wrestling now for about 5 hours.
How do I correctly bind the following class structure to a TreeView?
public class Person
{
public string Display { get; set; }
public List<Car> Cars { get; set; }
public List<House> Houses { get; set; }
}
public class Car
{
public string Display { get; set; }
}
public class House
{
public string Display { get; set; }
}
I want the TreeView to have the following structure:
(Where Person #1 has both houses and cars, and Person #2 just has houses)
- Person #1
|-- Cars
|-- Car #1
|-- Car #2
|-- Houses
|-- House #1
- Person #2
|-- Houses
|-- House #1
|-- House #2
How on earth do you this in XAML with automatic Databinding to an ObservableCollection?
I set the binding only once in the Constructor of my Treeview (UserControl) like this:
this.ItemsSource = SomeStaticClass.People;
Help is very very much appreciated!
Thanks! =)
EDIT:
I tried the HierarchicalDataTemplate, but I can't get it to work ;)
EDIT2: I tried many many different things, but the closest I have come to is following code (with just one Enumeration in mind at this time ;)):
<HierarchicalDataTemplate DataType="{x:Type e:Person}" ItemsSource="{Binding Cars}">
<TextBlock Text="{Binding Display}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Cars}">
<TextBlock Text="Cars" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Display}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
Unfortunately, it doesn't display the TextBlock Cars and the Cars themself. How do I do that?

Finally solved.
I just inserted another Collection of classes between my Person and Car (resp. Home) like this:
public class CollectionHolder<T> : IEnumerable
{
public List<T> Items { get; set; }
public string Display { get; set; }
private List<CollectionHolder<T>> self;
public CollectionHolder()
{
self = new List<CollectionHolder<T>>();
self.add(this);
}
public IEnumerator GetEnumerator()
{
return self.GetEnumerator();
}
}
And than the Person class:
public class Person
{
public string Display { get; set; }
public CollectionHolder<Car> Cars { get; set; }
public CollectionHolder<House> Houses { get; set; }
public IList Children
{
get
{
return new CompositeCollection
{
new CollectionContainer { Collection = Cars },
new CollectionContainer { Collection = Houses }
});//In real world: don't generate the list everytime!
}
}
}
And finally my XAML:
<TreeView x:Name="trvData" Grid.Row="0" BorderThickness="0,0,0,1">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type e:Person}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Display}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Display}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Display}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>

Related

WPF Expander Hierarchical data Template of 2 levels

I have a repeating data structure like this
public class Chapter
{
public string id { get; set; }
public string name { get; set; }
public List<BPage> pages { get; set; }
public List<SubChapter> chapters { get; set; }
}
public class SubChapter
{
public string name { get; set; }
public string id { get; set; }
public List<BPage> pages { get; set; }
public string ParentPageId { get; set; }
public List<SubChapter> chapters { get; set; }
}
public class BPage
{
public string name { get; set; }
public string label { get; set; }
}
Here is the xaml i am using , But this xaml is giving only one level of data back to me . Second level is missing completely
ie if Chapter 1 contains 2 subchapters and pages , then those info is missing
Chapter 1
page 1
page 2
Chapter 1.1
page 1a
page 1b
Chapter 1.2
page 2a
page 2b
And xaml is
<ListBox ItemsSource="{Binding}" Name="TOCView" ItemContainerStyle="{StaticResource ListBoxItemStyle}">
<ListBox.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding chapters}">
<Expander>
<Expander.Header>
<BulletDecorator>
<Label Style="{StaticResource ResourceKey=ChapterHeadStyle}" Content="{Binding name}"></Label>
</BulletDecorator>
</Expander.Header>
<ItemsControl Margin="25,0,0,0" ItemsSource="{Binding pages}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Tag="{Binding}" Click="btnNavigateToPage_Click" Style="{StaticResource ResourceKey=BookPageStyle}" Content="{Binding Path=label}" >
</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Your problem is caused because you have only provided one HierarchicalDataTemplate, but you want to define two hierarchical levels. Therefore, you need to provide another HierarchicalDataTemplate for your second level. Please note that you do not have to define them in the ListBox.ItemTemplate property, instead defining them both in a Resources section:
<HierarchicalDataTemplate DataType="{x:Type YourPrefix:Chapter}"
ItemsSource="{Binding CollectionPropertyInSubChapterClass}">
...
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type YourPrefix:SubChapter}">
ItemsSource="{Binding CollectionPropertyInBPageClass}">
...
</HierarchicalDataTemplate>
As long as you don't specify the x:Key directive for either of them, they will be implicitly applied, so you won't have to provide any value for the ListBox.ItemTemplate property.

Binding list property to TextBlock

I have a class named MyClass which looks like this:
public class MyClass : BaseObject, IDisposable
{
public int MyClassId { get; set; }
public IList<MyClassTranslation> MyTranslations { get; set; }
}
And MyClassTranslation looks like this:
public class MyClassTranslation
{
public string MyClassName { get; set; }
}
I have a view and a view model. The View model looks like this:
class MyClassViewModel : ViewModelBase, IMyClassViewModel
{
public MyClassViewModel()
{
myObjects = GetMyObjects();
}
public IList<MyClass> myObjects { get; set; }
}
And here's the view:
<ItemsControl ItemsSource="{Binding myObjects}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Center">
<TextBlock FontSize="24pt" FontWeight="Bold" TextAlignment="Center" Text="{Binding MyClassTranslation.MyClassName[0]}"></TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The problem is that the myClassName property isn't shown. How do I bind it?
assuming that the data context is set properly, so the proper binding would be
<StackPanel HorizontalAlignment="Center">
<TextBlock FontSize="24pt"
FontWeight="Bold"
TextAlignment="Center"
Text="{Binding MyTranslations[0].MyClassName}">
</TextBlock>
</StackPanel>
you can use indexer to bind such values

WPF TabControl ContentTemplate List

Inexperienced with WPF, so I need a little help. Appreciate the help in advance.
I have the following class:
public class TabDefn
{
public TabDefn() { }
public TabDefn(string inFolderName, List<FilesFolder> inFilesFolders)
{
folderName = inFolderName;
FFs = inFilesFolders;
}
public string folderName { get; set; }
public List<FilesFolder> FFs {get; set;}
}
public class FilesFolder
{
public FilesFolder() {}
//public Image image { get; set; }
public string ffName { get; set; }
//public Image arrow { get; set; }
}
The TabControl.ItemContent is working fine. I can't get anything to show up for the TabControl.ContentTemplate. I've tried many things, but this is where the WPF is right now:
<TabControl Grid.Column="1" Grid.Row="1" Visibility="Hidden" Name="Actions">
<!-- This displays the tab heading perfectly.-->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding folderName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<!-- This is the content of the tab that I can't get anything to show up in.-->
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding FF}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ffName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I don't care if the content changes, so I don't need the INotifyPropertyChanged or ObservableCollection. However, if I have to put all that code in, so be it.
You declare FF as field which is invalid binding source. You need to convert it into property
public List<FilesFolders> FF { get; set; }
and initialize it for example in TabDefn constructor. You can find more as to what is a valid binding source on MSDN

Set TreeView HierarchicalDataTemplate with 2 levels in WPF

I have a View Model that is represented in the DataContext of the window.
public class SchoolViewModel:ViewModelBase
{
public ObservableCollection<SchoolClassGroup> ClassesCollection { get; set; }
....
}
public class SchooleClassGroup:ViewModelBase
{
public string ClassName {get;set;}
public string TeacherName {get;set;}
public ObservableCollection<Students> StudentCollection{ get; set; }
}
public class Student
{
public string Name {get;set;}
public int Age {get;set;}
public DateTime BirthDate {get;set;}
...
}
I want to represent a TreeView that will show me schools,class and students.
How can I do it?
Thanks!
You need to create HeirarchicalDataTemplates for each level of your object tree that has a Parent-Child relationship, plus a simple DataTemplate for the leaf node.
Your object tree should be School -> Class -> Student
class School has a
List<Class>
class Class has a
List<Student>
Then it's simple
<Window.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Classes}" DataType="{x:Type School}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Students}" DataType="{x:Type Class}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type Student}">
<TextBlock Text="{Binding Name}" />
</DataTemplate >
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding Schools}" >
</Grid>

Silverlight - binding a listbox within a listbox template

In silverlight, I'm trying to build a listbox of reports and I'm trying to show the parameters of the report in a listbox within the datatemplate of the outer reports listbox.
Here are the data classes:
public class Report
{
public string Title { get; set; }
public string Description { get; set; }
public List<ReportParameter> Parameters = new List<ReportParameter>();
}
public class ReportParameter
{
public string Name { get; set; }
public string ParameterType { get; set; }
public bool Required { get; set; }
}
Here's the XAML I'm trying to use to do it:
<ListBox x:Name="lstReports">
<ListBox.ItemTemplate>
<DataTemplate>
<Border>
<StackPanel>
<TextBlock Text="{Binding Title}"/>
<ListBox ItemsSource="{Binding Parameters}" Height="60" Width="60">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The binding for the Title of the report works, but the inner listbox is empty for each of the reports.
The Parameters list is populated.
What am I doing wrong?
Thanks!
Your "Parameters" list is a public field not a property, silverlight can only bind to properties and not fields. Try changing your class to this:
public class Report
{
public Report()
{
Parameters = new List<ReportParameter>();
}
public string Title { get; set; }
public string Description { get; set; }
public List<ReportParameter> Parameters { get; set; }
}
This should work the way you want it.

Resources