Dynamically display TreeViewItem children - c#

I have a WPF application with a TreeView. I drag and drop files and/or folders onto this TreeView. In the case that the dragged item is a folder, which I have set up to detect, I search the top level of this directory.
Where I am stuck is in displaying these inner files on the TreeView. I wish for these inner files to be displayed as children of the folder item.
This is the class to model an item in the TreeView:
public class FileList
{
public enum FileType { File, Folder };
public FileType Type { get; set; }
public string Name { get; set; }
private ObservableCollection<FileList> innerFiles;
public ObservableCollection<FileList> InnerFiles
{
get
{
return innerFiles;
}
set
{
innerFiles = value;
}
}
public FileList(string file)
{
Name = file;
Type = FolderOrFile(file);
}
}
This is the xaml code within TreeView.ItemTemplate
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Name="mnuExpand" Header="Expand" Click="mnuExpand_Click" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding InnerFiles}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
I assign an ObservableCollection of FileList objects to the inner collection in the TreeViewItem's FileList and then refresh the grid, but these children items do not appear.
I tried following: TreeView not showing my Children
However I want the potential to open/search as many levels down as possible.
Any help would be much appreciated.

You need to tell the HierarchicalDataTemplate what object the source for the child items is using the ItemsSource property. Try this instead:
<HierarchicalDataTemplate ItemsSource="{Binding InnerFiles}">
<TextBlock Text="{Binding Name}"> <!-- DataTemplate for parent -->
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Name="mnuExpand" Header="Expand" Click="mnuExpand_Click" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate> <!-- DataTemplate for children -->
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
See the HierarchicalDataTemplate Class on MSDN for more information.

Related

TextBlock not showing up in ListView

This is my ListView:
<ListView ItemsSource="{Binding ModuleList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--<Image Source="{Binding ModuleImage}" Stretch="UniformToFill"/>-->
<TextBlock Text="{Binding ModuleName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is the code in WPF (MVVM):
public ItemListViewVM() : base()
{
ModuleList = new List<Module>();
modulLoader.LoadAllModules();
tempModulList = modulLoader.GetAllModules();
foreach (var module in tempModulList)
{
ModuleImage = module.ModuleImage;
ModuleName = module.Name;
ModuleList.Add(module);
}
}
Long story short: The List tempModulList contains Objects of type Module, which has an ImageSource Image and a string Name. Then the ModuleList gets one item after another. When I uncomment the Image in xaml, you can see it. But the TextBlock won't show up no matter what. I checked the string module.Namefor every item, it is not empty.
EDIT: Add Module Class
The Module Class just contains Name and Image:
public class Module
{
public ImageSource ModuleImage { get; set; }
public string Name { get; set; }
}
The Object gets created by deserializing a Json
The Bindings in the ItemTemplate use the properties of the data item class as their source properties. Hence you should write
Text="{Binding Name}"
instead of
Text="{Binding ModuleName}"
Unless you set the View property of a ListView (to e.g. a GridView) you could also better use a ListBox, which is the base class of ListView, and simpler:
<ListBox ItemsSource="{Binding ModuleList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ModuleImage}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Initializing the ModuleList property in the view model constructor would be as simple as this:
public ItemListViewVM()
{
modulLoader.LoadAllModules();
ModuleList = modulLoader.GetAllModules();
}

TreeView databind to list of list of list

I am new to WPF and I am stuck in creating a Treeview that is bound to a list of lists of list, etc, the number of childnodes can increase as needed. I have created two HierarchicalDataTemplates to test off the code, but the child nodes are not appearing
My tree view is defined as
<telerik:RadTabItem Header="Lookup Sets">
<telerik:RadTreeView IsLoadOnDemandEnabled="True" ItemsSource="{Binding AttributeLookupSetConversions}">
<telerik:RadTreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type cm:AttributeLookupSetConversion}">
<CheckBox Content="{Binding Path=Name}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type telerikDocking:RadSplitContainer}}, Path=DataContext.UpdateSelectionCommand}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type cm:AttributeConversion}">
<CheckBox Content="{Binding Path=Name}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type telerikDocking:RadSplitContainer}}, Path=DataContext.UpdateSelectionCommand}"/>
</HierarchicalDataTemplate>
</telerik:RadTreeView.Resources>
</telerik:RadTreeView>
</telerik:RadTabItem>
Is there something I am missing or will I have to create the nodes in code? I have a tabControl that has treeViews of lists of lists, the problem is one tab can contain items of another treeView that are linked relationally by an Id, so if I have say fro example
stundents
-Student1
-Course 1
-Course 2
- Department 1
-Student 2
-Course 6
Department12
Course
-Qualification 1
-WorkType
Create a class as the root of all your ltems, which has a DisplayName and an observablecollection of other Items:
public class Item : INotifyPropertyChanged
{
string _displayText;
public string DisplayText { get { return _displayText; } set { _displayText = value; RaisePropertyChanged("DisplayText"); } }
ObservableCollection<Item> _items;
public ObservableCollection<Model> Items { get { return _items; } set { _items = value; RaisePropertyChanged("Items"); } }
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
Any other type (Student, Teacher, Course, Department, etc.) must be derived from this class. They might have their specific properties too.
public class Student : Item
{
}
public class Course : Item
{
}
public class Qualification : Item
{
}
Note that if you have two types of Course class (with different Inner list of items), create two separate classes for them.
Now, you should populate the observableCollection in the view model propertly, and everything will be taken care of in a TreeView such as the following:
<TreeView DataContext="{Binding}" ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding DisplayText}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Having trouble binding to a treeview in WPF

I am trying to make a treeview that represents data from a file. The file data has data packets organized into three different sections: Header Info, Operation Header, and Operation Data. The packets in Header Info and Operation Header are one level deep, while the packets in Operation Data are grouped into lists so I thought it'd be easier to organize by making it go an extra level. That way you could pop open Data, see the lists, and pop those open to see individual data packets.
This is my xaml:
<TreeView ItemsSource="{Binding TreeViewItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding TreeNodes}">
<TextBlock Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding DataPackets.ItemName}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And here is the code we are trying to bind to the hierarchical datatemplates:
var childData = new Children();
result.OperationData.ToList().ForEach(x => childData.DataPackets.Add(x));
if (result.HeaderInfo.Any())
{
TreeViewItems.Add(new TreePair() { TreeNodes = result.HeaderInfoProperties.ToList(), Name = "Header Info" });
}
TreeViewItems.Add(new TreePair() { TreeNodes = result.OperationHeaderProperties.ToList(), Name = "Operation Header" });
TreeViewItems.Add(new TreePair() { TreeNodes = result.OperationDataProperties.ToList(), Children = new List<Children> {childData}, Name = "Operation Data" });
and the classes
public class TreePair
{
public TreePair()
{
TreeNodes = new List<PropertyInfo>();
Children = new List<Children>();
}
public List<Children> Children { get; set; }
public List<PropertyInfo> TreeNodes { get; set; }
public string Name { get; set; }
}
public class Children
{
public Children()
{
DataPackets = new List<DataPacketBase>();
}
public string Name { get; set; }
public List<DataPacketBase> DataPackets { get; set; }
}
I have the data packets for Header info and operation header showing up, as well as the list names for Operation data but none of the child packets show up. They exist in the Children.DataPackets object.
This is throwing in the output window:
BindingExpression path error: 'Children' property not found on 'object'
The second TextBlock is correct, but the Children ItemSource Is not being found, but the list is being filled with items.
Children Name property Missing From Second Level
The binding won't find Children because it is looking for it on a PropertyInfo object. Children and TreeNodes are siblings in the tree, there is no Child/Parent relationship between the two so the HierarchicalDataTemplate is broken when it tries to find the Children object that belongs to PropertyInfo object.
If your object hierarchy is correct then you'll need a CompositeCollection that can combine the TreeNodes and Children objects into a single collection.
private CompositeCollection _treeNodeChildCollections;
public CompositeCollection TreeNodeChildCollections
{
get
{
if (_treeNodeChildCollections == null)
{
_treeNodeChildCollections = new CompositeCollection();
var cc1 = new CollectionContainer();
cc1.Collection = Children;
var cc2 = new CollectionContainer();
cc2.Collection = TreeNodes;
_treeNodeChildCollections.Add(cc1);
_treeNodeChildCollections.Add(cc2);
}
return _treeNodeChildCollections;
}
}
Then bind to that in the xaml.
<TreeView ItemsSource="{Binding TreeViewItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding TreeNodeChildCollections}">
<TextBlock Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding DataPackets}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemName}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Binding Hierarchical TreeView To Dictionary

I'm trying to get my TreeView to bind to a Dictionary that will be updated. I've implemented INotifyProperyChangedHandler on the data type itself, but will this effect the treeview at all?
What I'm aiming for is:
-FolderName
--->Item1
--->Item2
-FolderName
--->Item1
This is my View:
<UserControl.Resources>
<HierarchicalDataTemplate x:Key="ChildTemplate" >
<TextBlock FontStyle="Italic" Text="{Binding Path=m_Items}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="NameTemplate"
ItemsSource="{Binding Path=Value}"
ItemTemplate="{StaticResource ChildTemplate}">
<TextBlock Foreground="White" Text="{Binding m_Name}" FontWeight="Bold" />
</HierarchicalDataTemplate>
</UserControl.Resources>
<TreeView x:Name="NewTree" ItemsSource="{Binding m_FolderList, UpdateSourceTrigger=PropertyChanged}" ItemTemplate="{StaticResource NameTemplate}">
</TreeView>
My ViewModel:
public Dictionary<UInt16, Folder> m_FolderList
{
get { return Manager.Instance.GetFolderDirectory(); }
}
My Folder class:
public class Folder
{
public m_Name { get; set; }
public ObservableCollection<String> m_Items { get; set; }
}
What I'm getting is a blank treeview that never updates. Whenever I add a new item to the "FolderDirectory" in that singleton manager instance, I do an OnPropertyChanged call. Which works for anything else I've been binding to that dictionary or Folder item.
I was trying to follow this, but how the binding observes that it automatically childrens the "FamilyMembers" list escapes me because nowhere do you explicitly tell the XAML template that it should bind itemsource to that collection. http://www.wpf-tutorial.com/treeview-control/treeview-data-binding-multiple-templates/
EDIT:
The control is definitely getting all the values from my singleton. I just was playing around with the naming of the Binding Path and when I accidentally set the TreeView to bind to m_FolderList.Value (dictionaries have Values, not just Value), it gave me as many binding errors in the console as there were Items in the dictionary.
EDIT2:
public List<Folder> m_FolderList
{
get
{
List<Folder> list = new List<Folder>();
list.AddRange(Manager.Instace.GetFolderDirectory().Values);
return list;
}
}
If I do this, instead of Dictionary, the first Level of information appears... Which is a nuisance
Try this
<Window.Resources>
<DataTemplate x:Key="secondLevel">
<TextBlock Text="{Binding}"/>
</DataTemplate>
<HierarchicalDataTemplate x:Key="topLevel" ItemsSource="{Binding Value.m_Items}" ItemTemplate="{StaticResource secondLevel}">
<TextBlock Text="{Binding Value.m_Name}">
</TextBlock>
</HierarchicalDataTemplate>
</Window.Resources>
<StackPanel>
<TreeView x:Name="FolderTree" ItemsSource="{Binding m_FolderList}" ItemTemplate="{StaticResource topLevel}">
</TreeView>
</StackPanel>
output
public List<Folder> m_FolderList
{
get
{
List<Folder> list = new List<Process>();
list.AddRange(Manager.Instance.GetFolderDirectory().Values);
return list;
}
}
I hate doing this, but its the only method that worked here with the Heirarchical template. I've used a binding to dictionary Path=Value before numerous times but it just refused to work in this instance with many attempts. Its not the best answer, but its the only working one I have at the moment.

WPF datatemplateselector not getting called

Hello I'm trying to dynamically change datatemplate but my method SelectTemplate in class TreeViewItemTemplateSelector never getting called (I've checked it by debugger) :( please help me :)
Code from xaml MainWindow:
Code in code behind:
Your problem seems to be that your TreeViewCustomItem is inheriting from TreeViewItem. (As seen in http://pastebin.com/jnP2nWMF)
Removing that inheritance (and the dependency property) causes the template selector to fire fine. What were/are you trying to achieve with the node item?
Looking at the OutputWindow, I get this message:
System.Windows.Data Error: 26 : ItemTemplate and ItemTemplateSelector are ignored for items already of the ItemsControl's container type; Type='TreeViewCustomItem'
You don't have to have items inherit from the TreeViewItem in order to bind them to a TreeView, TreeViewItem is something that the TreeView uses to hold the data, and then the DataTemplate is used to present the data.
Move DataTemplates from TreeView.Resources to Window.Resources
<Window.Resources><DataTemplate x:Key="DefaultTemplate">
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate><DataTemplate x:Key="Regulation">
<TextBlock Text="{Binding Path=Header}" FontWeight="Bold"></TextBlock>
</DataTemplate>
<DataTemplate x:Key="Article">
<TextBlock Text="{Binding Path=Header}" Foreground="Green"></TextBlock>
</DataTemplate>
<local:TreeViewItemTemplateSelector x:Key="TemplateSelector" DefaultTemplate="{StaticResource DefaultTemplate}" ArticleTemplate="{StaticResource Article}" RegulationTemplate="{StaticResource Regulation}" />
and make change
<TreeView ItemTemplateSelector="{StaticResource TemplateSelector}" Height="409" HorizontalAlignment="Left" Margin="10,10,0,0" Name="treeView1" VerticalAlignment="Top" Width="277" ItemsSource="{Binding}"/>
Update code and we will see. I put similar code into VS and it works so we need to take a closer look. So i checked this and changed
public class TreeViewCustomItem
{
public string Header { get; set; }
}
and this
listmy = new ObservableCollection<TreeViewCustomItem> { new TreeViewCustomItem { Header = "xD" }, new TreeViewCustomItem { Header = "xxD" } };
//treeView1.ItemsSource = listmy;
this.DataContext = listmy;
public class selector : DataTemplateSelector
{
public DataTemplate RegulationTemplate { get; set; }
public DataTemplate DefaultTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
TreeViewCustomItem treeViewItem = item as TreeViewCustomItem;
if (treeViewItem.Header == "xD")
{
return RegulationTemplate;
}
else
{
return DefaultTemplate;
}
}
}
and in XAML looks like this
xmlns:local="clr-namespace:WpfApplication1.Views">
<Window.Resources>
<DataTemplate x:Key="DefaultTemplate">
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate>
<DataTemplate x:Key="Regulation">
<TextBlock Text="{Binding Path=Header}" FontWeight="Bold"></TextBlock>
</DataTemplate>
<local:selector x:Key="selector_" DefaultTemplate="{StaticResource DefaultTemplate}" RegulationTemplate="{StaticResource Regulation}"/>
</Window.Resources>
<Grid>
<TreeView Height="409" HorizontalAlignment="Left" Margin="10,10,0,0" Name="treeView1" VerticalAlignment="Top" Width="277"
ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource selector_}"/>
</Grid>
And it works so my presumption is that problem is inside TreeViewCustomItem.

Categories