How Do I Dynamically Add Child Nodes To WPF TreeView? - c#

I have this students list. I want to dynamically add hundreds of child nodes to my Tree View. I can add root items but not not child nodes. How do I do that?
My Code-
public List<MyItem> Items { get; private set; }
public class MyItem
{
public string Student { get; set; }
public string Course { get; set; }
public string Location { get; set; }
}
List<MyItem> list1 = new List<MyItem>();
list1.Add(new MyItem()
{
Student = "Pradip Mhatre",
Course = "Diploma",
Location = "Mumbai"
});
trvStudents.ItemsSource = list1;
XAML:
<TreeView Name="trvStudents"
ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Student}"/>
<TextBlock Text="{Binding Path=Course}"/>
<TextBlock Text="{Binding Path=Location}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Any help is greatly appreciated.

Related

WPF TreeView binding multiple different dictionaries in different levels and sorting them by TKey

I have these Models:
public class CustomerGroup
{
public string GroupName { get; set; }
public string GroupKey { get; set; }
}
public class Customer
{
public string GroupKey { get; set; }
public string CustomerKey { get; set; }
public string CustomerName { get; set; }
// and more irrelevant stuff for my question...
}
public class ConstructionSite
{
public string CustomerKey { get; set; }
public string GroupeKey { get; set; }
public string ConstructionSiteName { get; set; }
// and more irrelevant stuff for my question...
}
And i have these Dictionaries:
public class CustomerGroupList : List<CustomerGroup>
{
// Methods for Serialization etc.
}
public class CustomersDictionary : SortedDictionary<string, List<Customer>> // TKey is GroupKey
{
// Methods for Serialization etc.
}
public class ConstructionSiteDictionary : SortedDictionary<string, List<ConstructionSite>> // TKey is customerKey
{
// Methods for Serialization etc.
}
So the customer's dictionary contains the groupKey as TKey and a list of customers for this group as value.
And the construction sites dictionary contains the CustomerKey as TKey and a list of construction sites for this customer as value.
And the Dictionaries are stored in a static class
Now I want to bind these dictionaries as source of an treeview and it should look like this:
-CustomerGroupOne
-CustomerOne
-Construction site A
-Construction site B
-CustomerTwo
-ConSite C
-GroupTwo
-CustomerThree
So I found a solution to Bind the data, BUT I can't figure out how to get the list from the dictionary with the key of the upper node's key.
I Expected it would work like this:
<TreeView x:Name="treeView" ItemsSource="{Binding Source={x:Static dataServices:DataProviderService.CustomerGroupsList}}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<Label Content="{Binding GroupName}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Source={x:Static dataServices:DataProviderService.CustomersDictionary[GroupKeyOfUpperNodesCurrentGroup]}}">
<Label Content="{Binding CustomerName}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Source={x:Static dataServices:DataProviderService.ConstructionSiteDictionary[CustomerKeyOfUpperNodesCorrentCustomer]}}">
<TextBlock Text="{Binding ConstructionSiteName}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I hope anyone can help me. Thanks for any help!
Sorry for the bad English...
I'm going to school right now and just started with C# for a little more than one year...
As the name TreeView implies, the control displays a tree data structure. But a dictionary is a flat collection of key-value pairs. Obviously the Dictionary is from data source. You have to convert the dictionary to the proper hierarchical data structure. It's recommended to have your data entities reflect the hierarchical data structure. A hierarchical data structure can be achieved by adding child collections to the data models:
Hierarchical data structure
public class CustomerGroup
{
// Define a child collection
public ObservableCollection<Customer> Customers { get; set; }
public string GroupName { get; set; }
public string GroupKey { get; set; }
}
public class Customer
{
// Define a child collection
public ObservableCollection<ConstructionSite> ConstructionSites { get; set; }
public string GroupKey { get; set; }
public string CustomerKey { get; set; }
public string CustomerName { get; set; }
// and more irrelevant stuff for my question...
}
public class ConstructionSite
{
public string CustomerKey { get; set; }
public string GroupeKey { get; set; }
public string ConstructionSiteName { get; set; }
// and more irrelevant stuff for my question...
}
DataSourceViewModel.cs
class DataSourceViewModel : INot6ifyPropertyChanged
{
public ObservableCollection CustomerGroups { get; }
public DataSourceViewModel()
{
this.CustomerGroups = new ObservableCollection()
{
new CustomerGroup
{
CustomerGroupName = "CustomerGroupOne",
Customers = new Customer
{
CustomerName = "CustomerOne",
ConstructionSites = new ConstructionSite {ConstructionSiteName = "Construction site A"}
}
}
};
}
MainWindow.xaml
Define HieratchicalDataTemplates to display the structure and
assign DataSourceViewModel to the DataContext as non-static binding source.
<Window>
<Window.DataContext>
<DataSourceViewModel />
</Window.DataContext>
<TreeView ItemsSource="{Binding CustomerGroups}">
<TreeView.Resources>
<!-- Root node -->
<HierarchicalDataTemplate DataType={x:Type CustomerGroup}"
ItemsSource="{Binding Customers}">
<TextBlock Text="{Binding GroupName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType={x:Type Customer}"
ItemsSource="{Binding ConstructionSites}">
<TextBlock Text="{Binding CustomerName}" />
</HierarchicalDataTemplate>
<!-- Leaf node -->
<DataTemplate DataType={x:Type ConstructionSite}>
<TextBlock Text="{Binding ConstructionSitesName}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Window>

Create TreeView from binding (child node as List<MyType>) in MVVM

I need my TreeView to display playlists and their songs, it should look something like this:
-Playlist1
--Song1
--Song2
--Song3
-Playlist2
--Song1
--Song2
--Song3
--Song4
-Playlist3
--Song1
--Song2
There can be an unlimited amount of playlists in the TreeView, with each node having differing amounts of songs. I want to achieve this using MVVM.
MainWindow.xaml
<TreeView ItemsSource="{Binding PlayLists}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type types:Playlist}" ItemsSource="{Binding PlayLists}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type types:Song}">
<TextBlock Text="{Binding Path=Title}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Playlist.cs
public class Playlist
{
public string Name { get; set; }
public List<Song> Songs { get; set; }
public static ObservableCollection<Playlist> Playlists { get; set; } = new ObservableCollection<Playlist>();
}
Song.cs
public class Song
{
public string FullPath { get; set; }
public string Title { get; set; }
public string Artist { get; set; }
public string Genre { get; set; }
public string Length { get; set; }
public string Size { get; set; }
}
MainViewModel.cs
public ObservableCollection<Playlist> PlayLists { get; set; }
public MainViewModel()
{
/* Get Play-lists */
string listsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AppName", "Playlists");
if (!Directory.Exists(listsPath))
{
Directory.CreateDirectory(listsPath);
}
string[] playlists = Directory.GetDirectories(listsPath);
foreach (var playlist in playlists)
{
var songs = new List<Song>();
string[] songsInPath = Directory.GetFiles(playlist);
foreach (var song in songsInPath)
{
songs.Add(NewSong(song));
}
Playlist newPlaylist = new Playlist()
{
Name = playlist.Split(Path.DirectorySeparatorChar).Last(),
Songs = songs
};
Playlist.Playlists.Add(newPlaylist);
}
PlayLists = Playlist.Playlists;
}
This is what it looks like:
There should be a sub-nodes for every song in the playlist.
The TreeView only shows the Playlist's Name, but not the Song's Title. I realized this is because the Playlist's property Songs is actually a List<Song>. I am not sure how I can display each Song's Title from the property Playlist.Songs.
Can someone please help me with this.
Your structure for Songs is wrong
Have a look at this example:
<HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/group.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" [" Foreground="Blue" />
<TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
<TextBlock Text="]" Foreground="Blue" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type self:FamilyMember}">
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/user.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" (" Foreground="Green" />
<TextBlock Text="{Binding Age}" Foreground="Green" />
<TextBlock Text=" years)" Foreground="Green" />
</StackPanel>
</DataTemplate>
Given this Model
public class Family
{
public Family()
{
this.Members = new ObservableCollection<FamilyMember>();
}
public string Name { get; set; }
public ObservableCollection<FamilyMember> Members { get; set; }
}
public class FamilyMember
{
public string Name { get; set; }
public int Age { get; set; }
}
Take a look at this for reference:
TreeView, data binding and multiple templates

WPF Hierarchial Data Template differrent properties in classes

How to make tree where leaves would be different properties of the classes?
I want to get something like this
AMachine
-Wheels
Bmachine
-Years
Code.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
}
public class TestViewModel
{
public ObservableCollection<IBase> Items { get; set; }
public TestViewModel()
{
Items = new ObservableCollection<IBase>();
Items.Add(new AMachine { Wheels = "3", Name = "AMachine" });
Items.Add(new BMachine { Years = "2", Name = "BMachine" });
}
}
public interface IBase
{
}
public class AMachine : IBase
{
public string Name { get; set; }
public string Wheels { get; set; }
}
public class BMachine : IBase
{
public string Name { get; set; }
public string Years { get; set; }
}
Xaml.cs
<Grid>
<TreeView Height="300" Width="300" ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type self:IBase}" ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
You don't need hierarchical data template here, the logical would be to use normal data templates and ListBox (with expanders as nodes?):
<ListBox ItemsSource="{Binding Items}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:AMachine}">
<Expander Header="{Binding Name}">
<TextBlock Text="{Binding Wheels}" />
</Expander>
</DataTemplate>
<DataTemplate DataType="{x:Type local:BMachine}">
<Expander Header="{Binding Name}">
<TextBlock Text="{Binding Years}" />
</Expander>
</DataTemplate>
</ListBox.Resources>
</ListBox>

How to add an editable TreeViewItem with c#

I have a program that exibits the file system content (files & folders) with code.
each file or folder reflected by a TreeViewItem.
i want to able editing each TreeViewItem from the UI.
Yes, quite easy. Here is an example...
<TreeView x:Name="treeView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Background="Beige">
<TreeViewItem Header="Files" ItemsSource="{Binding Root}">
<TreeViewItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding}">
<TextBox Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</TreeView>
Code behind...
public interface INamedObject
{
string Name { get; set; }
}
public class FileObject : INamedObject
{
public string Name { get; set; }
};
public class FolderObject : ObservableCollection<INamedObject>, INamedObject
{
public string Name { get; set; }
};
FolderObject _root = new FolderObject() { Name = "root" };
public FolderObject Root
{
get
{
return _root;
}
}

Producing a hierarchy of treeview in xaml

This seems like a common question but I have not been able to get it working from the answers of other questions.
I have a company, which has multiple projects, and each project can have multiple tasks. The company class contains some metadata and also a list of projects that it owns, the project class also has some metadata and a list of tasks it has. What I'm trying to do is to display this hierarchy in a tree view. I have made a sample object of a company which has 2 projects and project1 has 2 tasks. The tree view should only display one company at a time, so the root treeviewitems are the projects.
Heres my attempt at the problem, but nothing is displayed.
<TreeView ItemsSource="{Binding listOfProjects}" Margin="10" Height="200">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding listOfTasks}" DataType="{x:Type local:Task}">
<TreeViewItem Header="{Binding name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TreeViewItem Header="{Binding name}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
How do I go about making it correct? What should I bind to ItemsSource? For the TreeViewItem headers, how do I let it know which field to bind? At the moment, I have {Binding name}, but how do I differentiate between where its coming from (because project has a field 'name', and so does task).
I will be extending the hierarchy further (a task can have multiple parts and so on) but I'm assuming that each additional level would be done the same as the first 2.
edit: here my company, project and task classes.
class Company
{
public List<Project> listOfProjects;
public string name { get; set; }
private int companyID { get; set; }
public Company()
{
companyID = 0;
name = "Default";
listOfProjects = new List<Project>();
}
public Company(string inName)
{
companyID = 0;
name = inName;
listOfProjects = new List<Project>();
}
}
class Project
{
private int projID { get; set; }
public string name { get; set; }
public DateTime startDate { get; set; }
public DateTime endDate { get; set; }
public string region { get; set; }
private int companyID { get; set; }
public List<Task> listOfTasks;
public Project()
{
projID = 0;
name = "Default";
startDate = new DateTime();
endDate = new DateTime();
region = "Default";
companyID = 0;
listOfTasks = new List<Task>();
}
public Project(string inName)
{
projID = 0;
name = inName;
startDate = new DateTime();
endDate = new DateTime();
region = "Default";
companyID = 0;
listOfTasks = new List<Task>();
}
}
Tasks atm doesnt have anything in it except a constructor. It has 2 fields, projectID and name.
Your hierarchical data template override your treeview. You must rename listofProject and listofTask to same name. Therefore you create Interface or Class.Then implement Company,Project,Task from this interface.
public interface ITreeItem
{
string Name { get; }
List<ITreeItem> childs { get; }
}
public class Company : ITreeItem
{
public string Name
{
get { return name; }
}
public List<ITreeItem> childs
{
get { return listofProjects; }
}
}
public class Project : ITreeItem
{
public string Name
{
get { return name; }
}
public List<ITreeItem> childs
{
get { return listofTask; }
}
}
public class Task : ITreeItem
{
public string Name
{
get { return name; }
}
public List<ITreeItem> childs
{
get { return null; }
}
}
Then your template is
<HierarchicalDataTemplate ItemsSource="{Binding childs}" DataType="{x:Type local:ITreeItem}">
<TreeViewItem Header="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TreeViewItem Header="{Binding Name}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
So the top level will all be of type "project", and all branches will be of type "task"? You can use multiple HierarchicalDataTemplates like this:
<StackPanel.Resources>
<sdk:HierarchicalDataTemplate x:Key="TaskTemplate"
ItemsSource="{Binding Path=Childs}">
<TextBlock FontStyle="Italic" Text="{Binding Path=Name}" />
</sdk:HierarchicalDataTemplate>
<sdk:HierarchicalDataTemplate x:Key="ProjectTemplate"
ItemsSource="{Binding Path=ListOfTasks}"
ItemTemplate="{StaticResource TaskTemplate}">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
</sdk:HierarchicalDataTemplate>
</StackPanel.Resources>
And then declare your treeview like this ...
<sdk:TreeView ItemsSource="{Binding MyProjectList}"
ItemTemplate="{StaticResource ProjectTemplate}" x:Name="myTreeView" />

Categories