I want to create a dynamic tree view from a list in C# WPF. So I will get all the queues (queue1, ...) and topics (topic1, ...) from a list. Furthermore I need a specific context menu for the different hierarchical points. I want to create a tree view like this:
queues
queue1
queue2
topics
topic1
topic2
There should be a specific context menu for the main point queues and a specific for the main point topics. Additional I need a specific for the subitems queues1 and the topic1. I tried a few things but without success. Has anybody a small example which shows who to solve this problem?
Best Regards
Creating a tree is not an issue. It involves bit of work, but straight forward. You have to create a hierarchical data template from your list and get the tree populated. Following link has all the info.
Creating a wpf tree
OR if you don't want to use sdk
page resource:
tree in xaml:
<Grid TextElement.FontSize="10" DataContext="{StaticResource MyHierarchicalViewSource}" >
<GroupBox x:Name="gbTree">
<TreeView Name="HierarchyTreeview" HorizontalAlignment="Left" AllowDrop="True"
BorderThickness="0" VerticalAlignment="Top" Height="Auto"
ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Itemchildren, Mode=TwoWay}">
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock x:Name="text" Text="{Binding Item.ItemLabel}" >
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</GroupBox>
</Grid>
code behing:
Me._HierarchyViewSource = CType(Me.Resources("MyHierarchicalViewSource"), System.Windows.Data.CollectionViewSource)
Me._HierarchyViewSource.Source = your hierarchical data collection
assuming your hierarchy class structure:
Item has
ItemChildren collection
However,I have the same issue in creating a specific context menu. I have posted my own question and haven't found a solution yet. I tried to do it with data triggers with no luck. The only way I know is creating a context menu for the whole tree and make it visible or invisible depending on item type.
If I find a workaround, I will post.
I used the following code for add to TreeView dynamically.
CategorieList - Collection of categories which has id, name, bool value as IsSubCategory and Id of parent category.
private void AddToTree()
{
List<Category> topCategory = CategorieList.Where(c => c.IsSubCategory == false).ToList();
foreach(Category c in topCategory)
{
CategoryTree.Items.Add(CreateTreeViewItem(c));
}
}
private TreeViewItem CreateTreeViewItem(Category category)
{
TreeViewItem tItem = new TreeViewItem();
tItem.Header = category.Name;
List<Category> sub = CategorieList.Where(c => category.Id == c.ParentCategory).ToList();
if (null != sub && sub.Count() != 0)
{
foreach (Category c in sub)
{
TreeViewItem item = CreateTreeViewItem(c);
tItem.Items.Add(item);
}
}
return tItem;
}
XAML code is below
<TreeView x:Name="CategoryTree"> </TreeView>
Related
I'm working on a small "fun" project using WPF/MVVM/Prism which main component is a TreeView showing the file structure of a certain path. The idea how the ViewModel works is taken from Josh Smiths' article http://www.codeproject.com/Articles/28306/Working-with-Checkboxes-in-the-WPF-TreeView.
I actually need two things:
I'd like to get a list of checked items of the TreeView-View, presented in a another view (let's say List-View) and also show that the their state has changed.
I'd like to modify the List-View, which shall then be reflected within the TreeView.
Somehow I did not find a nice solution, as the hierarchical ViewModel used by Josh makes it hard for me to get hold on a "shared" model useable within both ViewModels.
But let's have a look at my code before stating my question:
My "ExplorerView" uses a hierarchical datatemplate and looks as follows:
<UserControl x:Class="MyFunProject.Views.ExplorerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:MyFunProject.ViewModels"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<UserControl.Resources>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:ItemBaseViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<CheckBox Focusable="True" IsChecked="{Binding IsChecked}" VerticalAlignment="Center" ToolTip="{Binding Path}">
<TextBlock Text="{Binding Name}" />
</CheckBox>
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<TreeView ItemsSource="{Binding Children}" />
</UserControl>
Following Josh's article, the ExplorerViewModel exhibits a child as a List<CheckableItemViewModel> with only one entry - which in fact has other childs directories or files. Directories itself again have childs.
public class ExplorerViewModel : BindableBase
{
private List<CheckableItemViewModel> childred;
public List<CheckableItemViewModel> Children
{
get { return childred; }
set { SetProperty(ref childred, value); }
}
public ExplorerViewModel(IExplorerModel ExplorerModel)
{
CheckableItemViewModel root = new CheckableItemViewModel();
AddChildrenToNode(root, ExplorerModel.GetCheckableItems());
root.Initialize(); // initialize children (and each children its own children, etc...)
Children = new List<CheckableItemViewModel> { root });
}
private void AddChildrenToNode(CheckableItemViewModel root, IList<CheckableItem> items)
{
foreach(var item in items)
{
var child = new CheckableItemViewModel(item);
var dirItem = item as DirectoryItem; // it's a directory and so it has childs
if(dirItem != null)
{
AddChildrenToNode(child, dirItem.Items);
}
root.Children.Add(child);
}
}
}
And now to my questions:
How do I connect my CheckableItemViewModel to a "kind of" global CheckableItemModel? If I inject the constructor by resolving a registered instance (unity container) I've got the problem, that I cannot do that if I'd like to have two ExplorerViews simultanously (or don't I?).
How do I inject if every CheckableItemViewModel also needs constructor parameters (is this the case where to use parameter override?)
How do I get a retrieve a list of the actual selected items (or when and where should I update the according model)?
How do I get a "changed" flag if one of the CheckableItemViewModel is altered?
Let me know if i missed a piece of my puzzle.
Thanks for any suggestions.
Im getting this error while trying to giva my treeview an itemsource
"Items collection must be empty before using ItemsSource."
I have checked a lot of solutions and I cant seem to find a way to solve this. Here are my code snippets:
XAML:
<HierarchicalDataTemplate x:Key="Category">
<TextBlock Text="{Binding Name}">
</TextBlock>
</HierarchicalDataTemplate>
XAML
<telerik:RadTreeView x:Name="treeview" IsDragDropEnabled="True" HorizontalAlignment="Left" Height="250" Margin="10,10,0,-3" VerticalAlignment="Top" Width="190" IsManipulationEnabled="True" IsLoadOnDemandEnabled="True" LoadOnDemand="treeview_LoadOnDemand" IsExpandOnSingleClickEnabled="True" ItemTemplate="{StaticResource Category}">
</telerik:RadTreeView>
C# - Giving the treeview a data source:
Data d = new Data();
treeview.ItemsSource = d.Get_Categories();
C# - My database query:
public List<Category> Get_Categories()
{
using (var context = new ProcessDatabaseEntities())
{
return context.Category.ToList();
}
}
Category only has two properties, Name and ID. I know that the itemsource-list is not empty when I assign it. So it's probably something wrong with my XAML-code. Thank you in advance!
I believe that your problem is a common one. Basically, you cannot use both the TreeView.ItemsSource and the TreeView.Items properties together... you must choose one way or the other. Usually this problem manifests itself because a developer has done something like this...:
<TreeView Name="TreeView" ItemsSource="{Binding SomeCollection}" ... />
... and then tried to do something like this in the code behind:
TreeView.Items.Add(someItem);
The solution in that case would be to manipulate the data bound collection instead of the TreeView.Items collection:
SomeCollection.Add(someItem);
However, in your case (and it's a little bit difficult to guess without seeing your code), you have probably done the second part first (set or manipulated the Items property) and then tried to set the ItemsSource property. Your solution is the same... use one method of editing the items or the other... not both.
I'm using ICollectionView to display, filter and sort items of a TreeView in WPF. Each tree item has its own collection of sub-items created in the following way:
children = new ObservableCollection<MyTreeItem>();
visibleChildren = CollectionViewSource.GetDefaultView(children);
The filtering consists of two steps. The first one is to go through the whole hierarchy (~600 items total) and chooses, which items are to be displayed and which ones should be filtered out.
private static bool RecursiveApply(StructureTreeBaseItem root,
StructureTreeFilterInfo info) {
bool show = false;
foreach (var child in root.Children)
show &= RecursiveApply(child, info);
if (!show)
show = root.MatchesFilter(info);
root.IsFilteredOut = !show;
return show;
}
The second step is to recursively call Refresh on all ICollectionViews to allow them react to changes.
root.RefreshChildren();
// ->
public void RefreshChildren() {
if (ChildrenChanged)
VisibleChildren.Refresh();
foreach (var child in VisibleChildren)
(child as StructureTreeBaseItem).RefreshChildren();
}
I'm a little bit concerned about performance of such scenario. I made some tests in similar conditions and it turned out, that building the tree structure from scratch takes around 100-150ms, applying filter usually costs no more than 50ms, but recursive calls to Refresh takes 600ms and more.
From what I found, it seems, that it makes a lot more sense to rebuild the tree structure from scratch each time the filter is applied instead of using the ICollectionView features to do it. This seems wrong. Am I doing something not the way I should or maybe it's a general problem with ICollectionView and I should solve this problem in another way?
Edit: (in response to comments)
This is a relevant part of structure of my tree in XAML - nothing special :) The rest is purely presentational.
<TreeView x:Name="tsdTree" DockPanel.Dock="Left" Width="250" Margin="0, 2, 0, 0" ItemsSource="{Binding Path=StructureView}" BorderThickness="0">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding VisibleChildren}" DataType="{x:Type st:StructureTreeItem}">
(...)
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding VisibleChildren}" DataType="{x:Type st:StructureTreeFolder}">
(...)
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Kind of an odd problem I have here.
I have a pretty basic recursive tree structure:
public class TreeNode
{
public string Name { get; set; }
public IEnumerable<TreeNode> Children { get; set; }
}
and am displaying the data in a TreeView using a HierarchicalDataTemplate, like so:
<TreeView Name="_tree">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" >
<CheckBox/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I populate the tree from code behind:
//allTreeNodes is a list of all existing Tree objects
public void PopulateTree(List<TreeNode> allTreeNodes)
{
foreach (var node in allTreeNodes)
{
_tree.Items.Add(node);
}
}
The result is a TreeView with each existing Tree object as a root node with their respective subtrees.
Note that each TreeNode can be displayed in the tree at multiple locations depending on whether or not it has ancestors.
Up to this point, everything works well, but what I want to do now is only have the checkboxes displayed for the visual root nodes only. I've attempted using a DataTemplateSelector with two HierarchicalDataTemplates, selecting the Template based on an additional boolean property of the TreeNode, but this doesn't work because the TreeNode needs to be displayed with the checkbox once and potentially multiple times without the checkbox.
Any help is appreciated.
Edit: Here is some dummy data to help explain what I want. (Also note that there are no cyclic references in the data.)
+TreeNodeA
-TreeNodeB
-TreeNodeC
-TreeNodeB
+TreeNodeB
+TreeNodeC
-TreeNodeB
+TreeNodeD
-TreeNodeE
-TreeNodeC
-TreeNodeB
+TreeNodeE
-TreeNodeC
-TreeNodeB
In the above view, only the nodes preceded with + should have checkboxes.
Well, I don't know of any straightforward approaches, but what you could do is to create an Attached Property and apply it to each TreeViewItem element. See more about implementing Attached Properties in the Overview and this example on StackOverflow.
This Attached Property would apply a different style to the TreeViewItem depending on the level.
It would be a bit tricky to get the level unless you're programmatically producing the TreeViewItem hierarchy.
If not, you could either calculate it every time a registration of the Attached Property call is made by traversing up the Visual Tree.
Or you could try - should work in theory - by doing RelativeSource binding to an Attached Property of the parent and increment it by 1 using an IValueConverter:
<TreeViewItem TreeViewLevelStyle.Level="{Binding RelativeSource=
{RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}},
Converter=IncrementByOneValueConverter}">
(Disclaimer - this is an idea, and not something I tried previously)
I'm new to WPF and EF; I've looked but I wasn't able to find appropriate help.
This is what I have:
Entity Framework (ReportDefinition.ParentID and ReportDefinition.SectionID are Section.idSections), ReportDefinition example and Section example.
This is what I would like to present:
TreeView.
.
I'm trying to achieve this programmatically. I would much appreciate any help.
You need a collection of top-level ReportDefinition objects:
TopLevelReportDefinitions = ReportDefinitions.Where(rd => rd.ParentID == 0)
You need to bind this collection to the ItemsSource of the TreeView.
In EF, you also need to create a parent-child relation on ReportDefinition linking the children to the parent using ParentID. For convenience you can name the reverse collection Children. The collection of ReportDefinition objects directly below another ReportDefinition is then the collection:
ReportDefinition.Children
You then have to create HierarchicalTemplate in the TreeView:
<TreeView ItemsSource="{Binding TopLevelReportDefinitions}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>