When I set the treeviewitem's header via a DataContext, it adds a few pixels of padding that are clickable, and then puts the text with isn't clickable. I shall post an image; blue: clickable, red: unclickable.
The classes that store the data:
public class TagClass
{
public string TagClassMagic { get; set; }
public ITagClass RawClass { get; set; }
public List<TagEntry> TagEntries = new List<TagEntry>();
public IList Children
{
get
{
return new CompositeCollection()
{
new CollectionContainer() { Collection = TagEntries }
};
}
}
}
public class TagEntry
{
public string TagFileName { get; set; }
public ITagEntry RawTag { get; set; }
}
The XAML for displaying the Data:
<TreeView x:Name="tvTagList" Margin="15, 40, 15, 50" ItemsSource="{Binding}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="{x:Null}" BorderBrush="{DynamicResource ExtryzeAccentBrushSecondary}" BorderThickness="2" ScrollViewer.CanContentScroll="True" Foreground="White">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagClass}" ItemsSource="{Binding Children}" >
<TreeViewItem Header="{Binding TagClassMagic}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagEntry}" >
<TreeViewItem Header="{Binding TagFileName}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Try to remove this ones:
<TreeViewItem Header="{Binding TagClassMagic}" />
<TreeViewItem Header="{Binding TagFileName}" />
and instead add a data templates for TagEntry accordingly - put just simple textblocks in those data templates
Related
I would like to create an TreeView from an List<PhonesFromStudents>.
My custom class is PhonesFromStudents
public class PhonesFromStudents
{
public string Name { get; set; }
public List<string> Phones { get; set; }
}
XAML is :
<TreeView x:Name="tv_source" Grid.Row="2" Grid.Column="1" Margin="0,5" HorizontalAlignment="Stretch" ItemsSource="{Binding ListStudents}" Background="White" BorderBrush="{x:Null}">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
MainWindows.cs
internal MainWindow(List<PhonesFromStudents> list)
{
InitializeComponent();
this.ListStudents = list;
this.DataContext = this;
}
Example :
2 students
Thomas - iPhone8,iPhone6
Lucas - iPhone4s, S8
i would like to have
Thomas
|_____iPhone8
|_____iPhone6
Lucas
|_____iPhone4s
|_____S8
But I get an empty list from UI.
Can someone help me?
Thanks
Use an HierarchicalDataTemplate:
<TreeView x:Name="tv_source" Grid.Row="2" Grid.Column="1" Margin="0,5"
HorizontalAlignment="Stretch" ItemsSource="{Binding ListStudents}" Background="White" BorderBrush="{x:Null}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:PhonesFromStudents}" ItemsSource="{Binding Phones}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
...and make sure that ListStudents is a public property:
public List<PhonesFromStudents> ListStudents { get; set; }
I have a Projects collection that contains two collections (either of which can be empty):
class Project {
public string Name { get; set; }
public int Priority { get; set; }
public List<Project> Projects { get; set; }
public List<Task> Tasks { get; set; }
}
I can get the nested Projects to display:
<TreeView x:Name="ProjectsTree" >
<TreeViewItem Header="Projects"
ItemsSource="{Binding ProjectsCollection, Mode=TwoWay}"
IsExpanded="True" >
<TreeViewItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Project}" ItemsSource="{Binding Projects}">
<TextBlock Text="{Binding Name}"></TextBlock>
</HierarchicalDataTemplate>
</TreeViewItem.Resources>
</TreeViewItem>
How do I add a 2nd template for Tasks? If I add:
<HierarchicalDataTemplate DataType="{x:Type local:Project}" ItemsSource="{Binding Tasks}">
<TextBlock Text="{Binding Name}"></TextBlock>
</HierarchicalDataTemplate>
I get an error about there already being an entry in the resource dictionary for datatype 'Project' (or something like that).
All help would be appreciated...
Assumption: the Project is given and cannot be changed. That means in the context of MVVM Project is the model. You can now create a view model that connects the view and model. It could look like this:
public class ProjectViewModel
{
public Project Project { get; set; }
public string Name
{
get
{
return Project.Name;
}
}
public int Priority
{
get
{
return Project.Priority;
}
}
public IList Children
{
get
{
if (Project.Projects.Count > 0)
{
return Project.Projects;
}
return Project.Tasks;
}
}
}
Then you adapt the template to the view model and you are done:
<TreeView x:Name="ProjectsTree" >
<TreeViewItem Header="Projects"
ItemsSource="{Binding ProjectsViewModelCollection, Mode=TwoWay}"
IsExpanded="True" >
<TreeViewItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Project}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"></TextBlock>
</HierarchicalDataTemplate>
</TreeViewItem.Resources>
</TreeViewItem>
I created a simple example that contains a list of classes and a list of students within the class. This gives a TreeView like this:
school
|-class1
|-student1
|-student1
|-class2
...
But what I want is a look like this:
school
|-CLASSES
|-class1
|-STUDENTS
|-student1
|-student1
|-class2
...
I would like to do this without altering the objects bound to the TreeView. It would be perfect if I could add a custom (CLASSES, STUDENTS, etc...) naming somehow to each HierarchicalDataTemplate.
How can I achieve this?
Here is my basis:
C# Classses needed
public class School
{
public string name { get; set; }
public List<Classi> classes { get; set; }
}
public class Classi
{
public string name { get; set; }
public List<Student> students { get; set; }
}
public class Student
{
public string name { get; set; }
}
C# List bound to TreeView
private List<object> _items = new List<object>();
public List<object> items
{
get
{
return _items;
}
set
{
_items = value;
NotifyOfPropertyChange(() => items);
}
}
C# Filling my school
var stud1 = new Student { name = "student1" };
var stud2 = new Student { name = "student2" };
var clas1 = new Classi { name = "class1" };
clas1.students = new List<Student>();
clas1.students.Add(stud1);
var clas2 = new Classi { name = "class2" };
clas2.students = new List<Student>();
clas2.students.Add(stud2);
var school = new School();
school.name = "school";
school.classes = new List<Classi>();
school.classes.Add(clas1);
school.classes.Add(clas2);
items.Add(school);
XAML The TreeView containing the HierarchicalDataTemplates
<TreeView ItemsSource="{Binding items}">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding classes}" DataType="{x:Type src:School}">
<TextBlock Text="{Binding name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding students}" DataType="{x:Type src:Classi}">
<TextBlock Text="{Binding name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type src:Student}">
<TextBlock Text="{Binding name}" />
</DataTemplate >
</TreeView.Resources>
</TreeView>
It's an old question, but since I was searching for an answer for a very similar problem, for the reference here's one possible (not very general) solution.
<Window.Resources>
<HierarchicalDataTemplate x:Key="studentTemplate">
<TextBlock Text="{Binding Path=name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="classesTemplate">
<TreeViewItem Header="{Binding Path=name}" >
<TreeViewItem Header="STUDENTS" ItemsSource="{Binding Path=students}"
ItemTemplate="{StaticResource studentTemplate}"/>
</TreeViewItem>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="schoolTemplate">
<TreeViewItem Header="{Binding Path=name}" >
<TreeViewItem Header="CLASSES" ItemsSource="{Binding Path=classes}"
ItemTemplate="{StaticResource classesTemplate}"/>
</TreeViewItem>
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView Name="thisTree" ItemTemplate="{StaticResource schoolTemplate}" />
The solution is "inspired" by this and this
I tried everything but no matter how I configure HierarchicalDataTemplate it always shows only the top level of the collection (StarSystem items and StarSystemName properties)
This is how my TreeView is configured right now:
<TreeView x:Name="StarSystemTreeView" ItemsSource="{Binding StarSystems, ElementName=window}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding}">
<TextBlock Text="{Binding Path=StarSystemName}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Planets}">
<TextBlock Text="{Binding Path=PlanetNumber}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Moons}">
<TextBlock Text="{Binding Path=MoonNumber}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=MoonMinerals}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
window is my MainWindow class, in which StarSystems is initialized:
public partial class MainWindow
{
private ObservableCollection<StarSystem> _starSystems = new ObservableCollection<StarSystem>
{
new StarSystem("FooSystem",
new ObservableCollection<Planet>
{
new Planet(1, new ObservableCollection<Moon>
{
new Moon(1, new ObservableCollection<string>
{
"FooMineral"
})
})
})
};
public ObservableCollection<StarSystem> StarSystems
{
get { return _starSystems; }
set { _starSystems = value; }
}
}
The 3 classes I want to bind to the TreeView are defined outside of MainWindow class, in the same namespace:
[NotifyPropertyChanged]
public class StarSystem
{
public StarSystem()
: this("", new ObservableCollection<Planet>())
{
}
public StarSystem(string starSystemName, ObservableCollection<Planet> planets)
{
StarSystemName = starSystemName;
Planets = planets;
}
public string StarSystemName { get; set; }
public ObservableCollection<Planet> Planets { get; set; }
}
[NotifyPropertyChanged]
public class Planet
{
public Planet()
: this(0, new ObservableCollection<Moon>())
{
}
public Planet(int planetNumber, ObservableCollection<Moon> moons)
{
PlanetNumber = planetNumber;
Moons = moons;
}
public int PlanetNumber { get; set; }
public ObservableCollection<Moon> Moons { get; set; }
}
[NotifyPropertyChanged]
public class Moon
{
public Moon()
: this(0, new ObservableCollection<string>())
{
}
public Moon(int moonNumber, ObservableCollection<string> moonMinerals)
{
MoonNumber = moonNumber;
MoonMinerals = moonMinerals;
}
public int MoonNumber { get; set; }
public ObservableCollection<string> MoonMinerals { get; set; }
}
This is how it look after starting the application:
I've been trying to figure it out for the past 3 days and I think I've tried every way possible to configure the HierarchicalDataTemplate so I'm probably just doing something wrong...
ItemTemplate is bit messed up. You have set ItemSource one hierarchy level down. It should be:
<TreeView x:Name="StarSystemTreeView"
ItemsSource="{Binding StarSystems, ElementName=window}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Planets}">
<TextBlock Text="{Binding Path=StarSystemName}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Moons}">
<TextBlock Text="{Binding Path=PlanetNumber}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate
ItemsSource="{Binding Path=MoonMinerals}">
<TextBlock Text="{Binding Path=MoonNumber}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I'd like to visualize the following data structure using TreeViews in WPF:
class MyDataContext
{
ICollectionView Outers {get;set;}
//...
}
class Outer
{
string Name {get;set;}
IEnumberable<Inner> Actions {get;set;}
}
class Inner
{
string Description {get;set;}
Command OnClick {get;set;}
}
This is my attempt so far:
<!-- DataContext is MyDataContext at this point -->
<TreeView ItemsSource="{Binding Path=Outers}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type myns:Outer}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}"/>
<TreeView ItemsSource="{Binding Path=Actions}" >
<DataTemplate DataType="{x:Type myns:Inner}">
<Button Command={Binding Path=OnClick}>
<TextBlock Text="{Binding Path=Description}"/>
</Button>
</DataTemplate>
</TreeView>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
It seams like there's something wrong with this access since I get the following InvalidOperationException:
Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
If I drop the inner TreeView there's no exception (but also no buttons of course).
I used the page Mateusz mentioned (HierarchicalDataTemplate) and after reading the answer to this question: Bind Collection to StackPanel I found a solution that did what I wanted:
Here the players (level 3) are on the same row as the team (level 2):
<TreeView ItemsSource="{Binding League}">
<!-- Conference template -->
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Teams}">
<TextBlock Foreground="Red" Text="{Binding Name}" />
<!-- Team template -->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<ItemsControl ItemsSource="{Binding Players}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding }"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Please try to use HierarchicalDataTemplate with TreeView.
HierarchicalDataTemplate
Maybe this helps you a little bit ;)
The xaml:
<TreeView ItemsSource="{Binding Outers}">
<TreeView.ItemTemplate>
<DataTemplate>
<TreeViewItem ItemsSource="{Binding Actions}" Header="{Binding Name}">
<TreeViewItem.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Click}" Content="{Binding Name}" />
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The data:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyDataContext();
}
}
class MyDataContext
{
public ObservableCollection<Outer> Outers { get; set; }
public MyDataContext()
{
Outers = new ObservableCollection<Outer>();
Outers.Add(new Outer() { Name = "Herp" });
Outers.Add(new Outer() { Name = "Derp" });
}
}
class Outer
{
public string Name { get; set; }
public ObservableCollection<Inner> Actions { get; set; }
public Outer()
{
Actions = new ObservableCollection<Inner>();
Actions.Add(new Inner { Name = "Test1" });
Actions.Add(new Inner { Name = "Test2" });
Actions.Add(new Inner { Name = "Test3" });
Actions.Add(new Inner { Name = "Test4" });
Actions.Add(new Inner { Name = "Test5" });
Actions.Add(new Inner { Name = "Test6" });
Actions.Add(new Inner { Name = "Test7" });
}
}
class Inner
{
public string Name { get; set; }
public ICommand OnClick { get; set; }
}
And if you are using Commands...
Try it with this example:
ICommand