3 Answers, 1 is accepted
Hello Victor,
The reason why the behavior happens on my side is because SelectedItems/CheckedItems collection is set first in xaml, then ItemsSource is set. I have logged this behavior on your behalf here: https://feedback.telerik.com/maui/1614332-treeview-items-are-coerced-immediately-when-selecteditems-and-checkeditems-collections-are-set-before-itemssource
As a solution set first the ItemsSource, then the SelectedItems/CheckedItems
<telerik:RadTreeView x:Name="treeView"
CheckBoxMode="Recursive" Grid.Row="1"
ItemsSource="{Binding Items}"
SelectedItems="{Binding CheckedItems, Mode=TwoWay}">
I have tested inside the .NET MAUI CollectionView and adding items programmatically to the SelectedItems, still there isn't an indication for selection and collections is empty.
Regards,
Didi
Progress Telerik
Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.
Hi Didi,
This doesn't appear to fix the problem for the "CheckedItems" collection when the Treeview contains nested items (eg. Items/Children class used in the examples) and then programatically populating the CheckedItems collection.
Only the parent items are checked and none of the nested items are checked even though the CheckedItems collection contains the Parent and Children items.
Hi Paul,
When programmatically adding one item to the collection, it doesn't add its children automatically. This operation is not recursive. You have to add them manually to the bound collection.
When checking items through the UI and for example bind the checked items collection to the ListView, The control is populated with the items, for example:
<Grid RowDefinitions="*,*">
<telerik:RadTreeView x:Name="treeView"
ItemsSource="{Binding Items}"
CheckedItems="{Binding Checked}"
CheckBoxMode="Recursive">
<telerik:TreeViewDescriptor ItemsSourcePath="Children" DisplayMemberPath="Name"
TargetType="{x:Type local:Item}"/>
</telerik:RadTreeView>
<telerik:RadListView Grid.Row="1" x:Name="list" ItemsSource="{Binding CheckedItems, Source={x:Reference treeView}}"/>
</Grid>
Hi Didi,
Thanks for you response. I'm still not sure how I can achieve what I need to do with populating the TreeView from your example.
My use case is that when the page loads I need to display a treeview with a list of items where some of the items are already checked when the control has rendered onscreen. The user will then have the ability to check more items or uncheck items and then save the list back to a database.
At the moment I can only get the treeview to display a checked item at the parent level, I can not workout how to also display child items that are checked.
This image shows what I currently get:
This is an example of the sort of thing I am trying to achieve:
This is my XAML page:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:telerik="http://schemas.telerik.com/2022/xaml/maui"
xmlns:local="clr-namespace:InterfaceAdminToolV2.ViewModel"
x:DataType="local:MainPageViewModel"
x:Class="InterfaceAdminToolV2.View.MainPage">
<VerticalStackLayout Spacing="30">
<telerik:RadTreeView x:Name="treeView"
HeightRequest="500"
ItemsSource="{Binding Items}"
CheckBoxMode="Recursive"
CheckedItems="{Binding CheckedItems, Mode=TwoWay}">
<telerik:TreeViewDescriptor TargetType="{x:Type local:Item}"
DisplayMemberPath="Name"
ItemsSourcePath="Children" />
</telerik:RadTreeView>
<!-- control just to debug to see what has been checked-->
<telerik:RadTreeView x:Name="checkedTree"
HeightRequest="500"
ItemsSource="{Binding CheckedItems, Source={x:Reference treeView}}"
CheckBoxMode="Recursive">
<telerik:TreeViewDescriptor TargetType="{x:Type local:Item}"
DisplayMemberPath="Name"
ItemsSourcePath="Children" />
</telerik:RadTreeView>
</VerticalStackLayout>
</ContentPage>
This is my viewmodel:
using System.Collections.ObjectModel;
namespace InterfaceAdminToolV2.ViewModel;
public partial class MainPageViewModel : ViewModelBase
{
[ObservableProperty]
private ObservableCollection<Item> items;
[ObservableProperty]
private ObservableCollection<Item> checkedItems;
public MainPageViewModel()
{
Items = new ObservableCollection<Item>();
Items.Add(new Item
{
Name = "My Projects",
Children = new List<Item>()
{
new Item()
{
Name = "Using latest Telerik .NET MAUI controls",
Children = new ObservableCollection<Item>()
{
new Item { Name = "TreeView"},
new Item() {Name = "Calendar"},
new Item() {Name = "RichTextEditor"},
new Item(){Name = "PdfViewer"},
new Item(){Name = "SlideView"},
new Item() {Name = "Chat"},
}
},
new Item { Name = "Blank project" }
}
});
Items.Add(new Item()
{
Name = "Shared Documents",
Children = new List<Item>()
{
new Item()
{
Name = "Reports",
Children = new List<Item>()
{
new Item(){Name = "October"},
new Item() {Name = "November"},
new Item(){Name = "December"}
}
}
}
});
var existingSelectedItems = Items.SkipLast(1).ToList();
CheckedItems = new ObservableCollection<Item>(existingSelectedItems);
}
}
public class Item
{
public string Name { get; set; }
public IList<Item> Children { get; set; } = new List<Item>();
public bool HasChildren => Children.Any();
}
Hi Paul,
You have to iterate through the child nodes for the concrete parent item and add them to the CheckedItems collection. You have to manually populate the collection with the items.
I have the same situation, I would like to know how is possible to do that.
Hi all,
TreeView control provides CheckedItems collection. You can add/remove items programmatically. You have to iterate through the child nodes for the concrete parent item and add them to the CheckedItems collection. You have to manually populate the collection with the items.
Here is a sample
<Grid RowDefinitions="Auto,*">
<VerticalStackLayout >
<Button Clicked="Button_Clicked" Text="Add Item"/>
<Button Clicked="Button_Clicked_1" Text="Remove item"/>
</VerticalStackLayout>
<telerik:RadTreeView x:Name="treeView" Grid.Row="1"
AutomationId="treeView" CheckBoxMode="Recursive"
CheckedItems="{Binding CheckedItems}"
ItemsSource="{Binding Items}">
<telerik:TreeViewDescriptor DisplayMemberPath="Name"
ItemsSourcePath="Children"
TargetType="{x:Type local:Item}" />
</telerik:RadTreeView>
</Grid>
public partial class MainPage : ContentPage
{
ViewModel vm;
public MainPage()
{
InitializeComponent();
this.vm = new ViewModel();
this.BindingContext = this.vm;
}
private void Button_Clicked(object sender, EventArgs e)
{
this.vm.CheckedItems.Add(this.vm.Items[0]);
foreach (var node in this.vm.Items)
{
this.vm.CheckedItems.Add(node);
foreach (var cityNode in node.Children)
{
this.vm.CheckedItems.Add(cityNode);
}
}
}
private void Button_Clicked_1(object sender, EventArgs e)
{
if (this.vm.CheckedItems.Count > 0)
{
this.vm.CheckedItems.Remove(this.vm.Items[0]);
}
else return;
}
}
public class ViewModel : NotifyPropertyChangedBase
{
public ViewModel()
{
Items = new ObservableCollection<Item>();
CheckedItems = new ObservableCollection<Item>();
Items.Add(new Item("My Projects")
{
Children = new List<Item>()
{
new Item("Using latest Telerik .NET MAUI controls")
{
Children = new ObservableCollection<Item>()
{
new Item("TreeView"),
new Item("Calendar"),
new Item("RichTextEditor"),
new Item("PdfViewer"),
new Item("SlideView"),
new Item("Chat"),
}
},
new Item("Blank project")
}
});
Items.Add(new Item("Shared Documents")
{
Children = new List<Item>()
{
new Item("Reports")
{
Children = new List<Item>()
{
new Item("October"),
new Item("November"),
new Item("December")
}
}
}
});
}
public ObservableCollection<Item> Items { get; set; }
private ObservableCollection<Item> checkedItems;
public ObservableCollection<Item> CheckedItems
{
get => this.checkedItems;
set
{
if (this.checkedItems != value)
{
this.checkedItems = value;
this.OnPropertyChanged();
}
}
}
}
public class Item
{
public Item(string name)
{
this.Name = name;
this.Children = new ObservableCollection<Item>();
}
public string Name { get; set; }
public IList<Item> Children { get; set; }
}
Note that how to iterate through items is a custom development. You have the items collection and checked items collection. How to manage these collections requires custom logic implementation.
Regards,
Didi
Progress Telerik
I'm trying to set the CheckedItems on initial load of the TreeView items and have the CheckBox for selected nodes ticked when the page loads.
I am able to set the CheckedItems collection but it does not set the TreeView node CheckBoxes to true and if I click the checkbox for an item already in the CheckedItems collection it adds the same item again.
This is example code of the result I'm getting.
I want the children items to have their checkbox ticked if there are part of the CheckedItems collection.
Page xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:telerik="http://schemas.telerik.com/2022/xaml/maui"
xmlns:viewmodel="clr-namespace:InterfaceAdminToolV2.ViewModel"
x:Class="InterfaceAdminToolV2.View.DropDownPage"
x:DataType="viewmodel:DropDownPageViewModel">
<Grid RowDefinitions="Auto,*, *">
<telerik:RadTreeView x:Name="treeView" Grid.Row="1"
AutomationId="treeView"
SelectionMode="None"
ItemsSource="{Binding Books}"
CheckBoxMode="Independent"
CheckedItems="{Binding CheckedItems}">
<telerik:TreeViewDescriptor DisplayMemberPath="Title"
ItemsSourcePath="Authors"
TargetType="{x:Type viewmodel:Book}" />
</telerik:RadTreeView>
<VerticalStackLayout Grid.Row="2">
<Label Text="CheckedItem's collection" />
<telerik:RadListView ItemsSource="{Binding CheckedItems}">
<telerik:RadListView.ItemTemplate>
<DataTemplate>
<telerik:ListViewTextCell x:DataType="viewmodel:Book" Text="{Binding Id}"
Detail="{Binding Title}"
TextColor="Black"
DetailColor="Gray" />
</DataTemplate>
</telerik:RadListView.ItemTemplate>
</telerik:RadListView>
</VerticalStackLayout>
</Grid>
</ContentPage>
ViewModel and TreeView object
using System.Collections.ObjectModel;
using Telerik.Maui.Controls;
namespace InterfaceAdminToolV2.ViewModel;
public partial class DropDownPageViewModel : NotifyPropertyChangedBase
{
public DropDownPageViewModel()
{
Books = new ObservableCollection<Book>();
Books.Add(new Book(1, "First Book")
{
Authors = new List<Book>
{
new Book(99, "Sam Dob"),
new Book(23, "Mark Semt")
}
}
);
Books.Add(new Book(2, "Shared Documents"));
Initialize();
}
private void Initialize()
{
CheckedItems = new ObservableCollection<Book>();
foreach (var node in Books)
{
CheckedItems.Add(node);
foreach (var nodeAuthor in node.Authors)
{
CheckedItems.Add(nodeAuthor);
}
}
}
private ObservableCollection<Book> checkedItems;
public ObservableCollection<Book> CheckedItems
{
get => this.checkedItems;
set
{
if (this.checkedItems != value)
{
this.checkedItems = value;
this.OnPropertyChanged();
}
}
}
public ObservableCollection<Book> Books { get; set; }
}
public class Book
{
public Book(int id, string title)
{
this.Id = id; this.Title = title;
this.Authors = new List<Book>();
}
public int Id { get; set; }
public string Title { get; set; }
public IList<Book> Authors { get; set; }
}