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; }
Related
i have these two classes.
classes:
public class OuterList
{
public string Process { get; set; }
public OuterList(string _process)
{
Process = _process;
}
}
public class InnerList
{
public string Order { get; set; }
public string Product { get; set; }
public InnerList(string _order, string _product)
{
Order = _order;
Product = _product;
}
}
And i will get a Listbox in a ListView.
XAML:
<ListView x:Name="Container" ItemsSource="{Binding OuterList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Margin="5" FontSize="24" FontFamily="Arial" Background="Red" Text="{Binding Process}"/>
<ListBox x:Name="Orders" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=DataContext.InnerList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Product}"/>
<TextBlock Text="{Binding Order}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code:
public partial class MainWindow : Window
{
ObservableCollection<OuterList> myContainers = new();
ObservableCollection<InnerList> myItems = new();
public MainWindow()
{
InitializeComponent();
myContainers.Add(new OuterList("Start"));
myContainers.Add(new OuterList("Middle"));
myContainers.Add(new OuterList("End"));
Container.ItemsSource = myContainers;
myItems.Add(new InnerList("34545","SD5"));
myItems.Add(new InnerList("45654", "SD5"));
myItems.Add(new InnerList("65775", "SD5"));
myItems.Add(new InnerList("78677", "SD5"));
myItems.Add(new InnerList("35887", "SD5"));
Orders.ItemsSource = myItems; //<- The name "Orders" does not exist in the current context.
}
}
All the Content of the OuterList will shown, but no Item from InnerList
How can i add Content from two different ItemSources? Or, what am I doing wrong?
Got an issue in Code: The name "Orders" does not exist in the current context.
Thanks for you Help!
Because your DataContext is incorrect.
Assuming the Grid that wraps both of them its DataContext contains Outer and Inner
You can do this:
<Grid>
<ListView x:Name="Container" ItemsSource="{Binding OuterList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Margin="5" FontSize="24" FontFamily="Arial" Background="Red" Text="{Binding Process}"/>
<ListBox x:Name="Orders" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=DataContext.InnerList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Product}"/>
<TextBlock Text="{Binding Order}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
So I have an ``ObservableCollection which I'm binding to a Pivot control. This ObservableCollection has a child that is also an ObservableCollection and I'm trying to bind a GridView in each Pivotitem's body.
I've tested my code-behind to ensure the collections are populated i.e.
// Level 1 ObservableCollection
foreach (var dchallenge in ChallengesList)
{
// Level 2 ObservableCollection
Debug.WriteLine(dchallenge.Name);
foreach (var dactivity in dchallenge.Activities)
{
Debug.WriteLine(dactivity.Name);
}
}
And its output is as expected.
For reference, my model is as follows:
public class Challenge
{
public string Type { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Slug { get; set; }
public string Image { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ObservableCollection<Activity> Activities { get; set; }
public class Activity
{
public string Name { get; set; }
public string Description { get; set; }
public long Hash { get; set; }
}
}
And finally, my view:
<Pivot x:Name="Items" ItemsSource="{x:Bind ChallengesList, Mode=OneWay}" Margin="20,417,0,0">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="data:Challenge">
<TextBlock Text="{Binding Name, Mode=OneWay}" FontWeight="Normal" FontSize="16"/>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate x:DataType="data:Challenge">
<StackPanel Margin="0,20,0,0" HorizontalAlignment="Left">
<TextBlock Text="{Binding Description, Mode=OneWay}" MaxWidth="300" TextWrapping="WrapWholeWords"/>
<TextBlock Text="Activities" FontWeight="Medium" MaxWidth="300" Margin="0,20,0,0"/>
<!--<GridView DataContext="{Binding}" ItemsSource="{x:Bind Activities}" SelectionMode="None"
IsItemClickEnabled="False" IsSwipeEnabled="False" CanDragItems="False"
HorizontalAlignment="Left" Margin="0,70,0,0" VerticalAlignment="Top" Width="300">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Challenge">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="{x:Bind Name}" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>-->
</StackPanel>
</DataTemplate>
</Pivot.ItemTemplate>
</Pivot>
A GridView may not be the control that I finally settle on, but if I know how to bind it then I should be able to use that knowledge with another control.
Intellisense helps a lot in determining which properties are available, but Activities isn't one. I only get strings such as Name and Description which work correctly (first two TextBlock).
The only problem I see is that you are using the wrong DataType in the inner GridView. The type in Activities collection is Challenge.Activity, which is a nested class and nested classes are denoted by plus sign (+) in XAML, so data:Challenge+Activity:
<GridView DataContext="{Binding}" ItemsSource="{x:Bind Activities}" SelectionMode="None"
IsItemClickEnabled="False" IsSwipeEnabled="False" CanDragItems="False"
HorizontalAlignment="Left" Margin="0,70,0,0" VerticalAlignment="Top" Width="300">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Challenge+Activity">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="{x:Bind Name}" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
VS may still throw some warnings but those will disappear after successful compilation.
I have the following classes -
public class A : INotifyPropertyChanged
{
public string Name { get; set; }
public ObservableCollection<Child> Children { get; set; }
...
}
public class Child : INotifyPropertyChanged
{
public string InstanceName { get; set; }
...
}
And here is my TreeView XAML -
<TreeView ItemsSource="{Binding ListOfClassA}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=InstanceName}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
In this case, the parent class A name is not displayed. How can I show the Name property on class A as well as the InstanceName property on the Child class.
In your code
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=InstanceName}"/>
</HierarchicalDataTemplate>
Is actually the template for your A class. It should be:
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate>
<!-- this is how Child is visualised-->
<TextBlock Text="{Binding Path=InstanceName}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<!-- this is how A is visualised-->
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
For better code visibility you can write them in resource section as two separate templates:
<HierarchicalDataTemplate x:Key="Child_template">
<!-- this is how Child is visualised-->
<TextBlock Text="{Binding Path=InstanceName}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="A_template" ItemsSource="{Binding Path=Children}"
ItemTemplate="{StaticResource Child_template}">
<!-- this is how A is visualised-->
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
Hope this helps.
For reference: http://msdn.microsoft.com/en-us/library/dd759035(v=vs.95).aspx
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
I've got webservice asmx, and there are classes:
Country
public string Name {get;set;}
public string Code {get;set;}
public List<Area> Areas {get;set;}
Area
public string Name {get;set;}
public string Code {get;set;}
public List<Regions> Provinces {get;set;}
Provinces
public string Name {get;set;}
public string Code {get;set;}
I bind it to mz TreeView WPF:
Country[] items = new MyService().GetListOfCountries();
structureTree.ItemsSource = items;
Code of myTree:
<UserControl x:Class="ObjectsAndZonesSimpleTree"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
<Grid>
<StackPanel Name="stackPanel1">
<GroupBox Header="Choose" Height="354" Name="groupBox1" Width="Auto">
<TreeView Name="structureTree" SelectedItemChanged="structureTree_SelectedItemChanged" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding}" Height="334" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Visible" Width="Auto" PreviewMouseRightButtonUp="structureTree_PreviewMouseRightButtonUp" FontFamily="Verdana" FontSize="12" BorderThickness="1" MinHeight="0" Padding="1" Cursor="Hand" Margin="-1">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type MyService:Country}"
ItemsSource="{Binding Path=ListOfRegions}">
<StackPanel Orientation="Horizontal">
<TextBlock TextAlignment="Justify" VerticalAlignment="Center" Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type MyService:Region}"
ItemsSource="{Binding Path=Provinces}">
<StackPanel Orientation="Horizontal">
<TextBlock TextAlignment="Justify" VerticalAlignment="Center" Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type MyService:Province}"
ItemsSource="{Binding Path=ListOfCities}">
<StackPanel Orientation="Horizontal">
<TextBlock TextAlignment="Justify" VerticalAlignment="Center" Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</GroupBox>
</StackPanel>
</Grid>
</UserControl>
This gives me null:
private void structureTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
TreeViewItem treeViewItem = structureTree.SelectedItem as TreeViewItem;
}
SelectedItem will actually contain a Country, Area, or Region (or null). If you really want the TreeViewItem, you can do strutureTree.ItemContainerGenerator.ContainerFromItem(structureTree.SelectedItem).
Correct. You should expect a Country as your SelectedItem. WPF works entirely different than Windows Forms did. It's all about databinding!