This is a migrated thread and some comments may be shown as answers.

Get selected node?

6 Answers 408 Views
TreeView
This is a migrated thread and some comments may be shown as answers.
Ed
Top achievements
Rank 1
Iron
Veteran
Iron
Ed asked on 12 Mar 2020, 02:25 PM

Seems like this should be simple, but it's eluding me. Basically, I have a treeview and  button. If teh user has selected a node and then clicks on my button,

how do I tell which node is active?

Thanks … Ed

 

6 Answers, 1 is accepted

Sort by
0
Accepted
Svetoslav Dimitrov
Telerik team
answered on 12 Mar 2020, 03:55 PM

Hello Ed,

On our Feedback Portal there is a thread for a Feature Request regarding this functionality. You can Follow the status updates for its implementation from this URL: https://feedback.telerik.com/blazor/1427582-select-and-multiple-select-of-items. I have given a Vote on your behalf already.

Also, there you can find a workaround solution for the time-being.

Regards,
Svetoslav Dimitrov
Progress Telerik

 UI for Blazor
0
Accepted
Joel
Top achievements
Rank 2
Iron
Iron
Iron
answered on 12 Mar 2020, 03:58 PM

Try this:

@using System.Collections.ObjectModel
@using Palmer.Doc.Models
 
@page "/tree"
 
 
<TelerikButton Primary="true" OnClick="CreateItem">Create item</TelerikButton>
 
<TelerikButton Primary="true" OnClick="DeleteItem">Delete item</TelerikButton>
 
<TelerikTreeView Data="@TreeData">
    <TreeViewBindings>
        <TreeViewBinding IdField="Id"
                         TextField="Text">
            <ItemTemplate>
                @{
                    <TelerikButton OnClick="@(() => OnClickHandler(context as TreeItem))">@((context as TreeItem).Text)</TelerikButton>
                }
            </ItemTemplate>
        </TreeViewBinding>
    </TreeViewBindings>
</TelerikTreeView>
 
@code{
 
    public class TreeItem
    {
        public string Id { get; set; } = $"{Guid.NewGuid()}";
        public string Text { get; set; }
        public string ContextId { get; set; }
        public ObservableCollection<TreeItem> Items { get; set; } = new ObservableCollection<TreeItem>();
        public bool Expanded { get; set; }
        public bool HasChildren { get; set; }
        public bool Selected { get; set; }
    }
 
    #region Properties
 
    public TreeItem SelectedItem { get; set; }
    public ObservableCollection<TreeItem> TreeData { get; set; }
 
    #endregion
 
    #region Event Handlers
 
    protected override void OnInitialized()
    {
        LoadHierarchical();
    }
 
    private void OnClickHandler(TreeItem context)
    {
        SelectedItem = context;
    }
 
    #endregion
 
    #region Methods
 
    private void CreateItem()
    {
        if (SelectedItem != null)
        {
            AddNode(SelectedItem, "New Item");
        }
    }
 
    private void DeleteItem()
    {
        if (SelectedItem != null)
        {
            TreeItem parent = GetById(SelectedItem.ContextId);
            if (parent != null)
            {
                parent.Items.Remove(SelectedItem);
 
                SelectedItem = parent;
                SelectedItem.HasChildren = SelectedItem.Items.Count > 0;
                SelectedItem.Expanded = SelectedItem.HasChildren;
            }
        }
    }
 
    private void LoadHierarchical()
    {
        ObservableCollection<TreeItem> roots =
        new ObservableCollection<TreeItem>()
        {
            new TreeItem { Text = "Item 1", Expanded = true },
            new TreeItem { Text = "Item 2" }
            };
 
        AddNode(roots[0], "Item 1 first child");
        AddNode(roots[0], "Item 1 second child");
        AddNode(roots[1], "Item 2 first child");
        AddNode(roots[1], "Item 2 second child");
 
        TreeData = roots;
    }
 
    private void AddNode(TreeItem parent, string childText)
    {
        AddNode(parent, new TreeItem()
        {
            Text = childText
        });
    }
 
    private void AddNode(TreeItem context, TreeItem child)
    {
        child.ContextId = context.Id;
 
        context.Items.Add(child);
        context.HasChildren = true;
        context.Expanded = true;
 
        SelectedItem = child;
    }
 
    public TreeItem GetById(string id)
    {
        TreeItem result = null;
 
        // perform a recursive search starting with the root nodes
        foreach (var treeItem in TreeData)
        {
            result = getById(id, treeItem);
            if (result != null) break;
        }
 
        return result;
    }
 
    /// <summary>
    /// Perform a recursive search using the given node
    /// </summary>
    /// <param name="id"></param>
    /// <param name="parent"></param>
    /// <returns></returns>
    private TreeItem getById(string id, TreeItem node)
    {
        TreeItem result = null;
 
        if (node.Id == id)
        {
            result = node;
        }
        else
        {
            foreach (TreeItem child in node.Items)
            {
                result = getById(id, child);
                if (result != null) break;
            }
        }
 
        return result;
    }
 
    #endregion
}
0
Svetoslav Dimitrov
Telerik team
answered on 13 Mar 2020, 09:07 AM

Thank you Joel for sharing your working example with the community!

Regards,
Svetoslav Dimitrov
Progress Telerik

 UI for Blazor
0
Ed
Top achievements
Rank 1
Iron
Veteran
Iron
answered on 18 Mar 2020, 10:03 AM

Thanks to all who responded to my query. Based on what I have learned I thought I would share my version. It has a feature that acts as a selected node so that when you click on a node it stays selected until you click on another node. Hope someone finds it useful. It's not perfect, but seems to do the job. 

Ed

@page "/CategoryTree"
@using Telerik.Blazor.Components
@using AlderneyTreasures.Data
@using Telerik.Blazor.Components.Common.Editors
@using Telerik.DataSource.Extensions
@using Microsoft.EntityFrameworkCore;
@using System.Collections.ObjectModel
@inject ATDBContext db
@inject Blazored.Toast.Services.IToastService toastService;
 
 
<TelerikTreeView @ref="tv" Data="@tvData" OnExpand="@LoadChildren">
    <TreeViewBindings>
        <TreeViewBinding TextField="ItemName" ItemsField="Items" IconField="Icon">
            <ItemTemplate Context="ctx">
                @{
 
                    TvItem item = ctx as TvItem;
                    <span @onclick="@( _ => OnNodeClick(item) )" style="@(item.NodeBackColor)">
                        <img src="/images/folder.png" /> <strong>@item.ItemName</strong>
                    </span>
                }
            </ItemTemplate>
        </TreeViewBinding>
        <TreeViewBinding Level="1" TextField="ItemName" ItemsField="Items" IconField="Icon">
            <ItemTemplate Context="ctx">
                @{
                    TvItem item = ctx as TvItem;
                    <span @onclick="@( _ => OnNodeClick(item) )" style="@(item.NodeBackColor)">
                        <img src="/images/folder.png" /> <strong>@item.ItemName</strong>
                    </span>
                }
            </ItemTemplate>
        </TreeViewBinding>
    </TreeViewBindings>
</TelerikTreeView>
@code {
    private int categoryId;
    [Parameter] public int CategoryId
    {
        get { return categoryId; }
        set
        {
            categoryId = value;
             
        }
    }
    [Parameter]
    public EventCallback<int> CategoryIdChanged { get; set; }
 
    public bool ShowInactive { get; set; } = false;
    TelerikTreeView tv { get; set; }
    public ObservableCollection<TvItem> tvData { get; set; }
    public class TvItem
    {
        public string ItemName { get; set; }
 
        public string Icon { get; set; }
        public int ItemId { get; set; } //will be used to identify the node, not for rendering in this example
        public int? ParentItemId { get; set; }
        public ObservableCollection<TvItem> Items { get; set; }
        public bool Expanded { get; set; }
        public bool HasChildren { get; set; }
        public bool IsActive { get; set; }
        public bool Selected { get; set; }
        public int Level { get; set; }
        public string NodeBackColor { get; set; }
    }
 
    public TvItem SelectedNode { get; set; }
    public TvItem PrevSelectedNode { get; set; }
    private void OnNodeClick(TvItem item)
    {
        if (SelectedNode != null)
        {
            PrevSelectedNode = SelectedNode;
            PrevSelectedNode.NodeBackColor = "background-color:white;";
        }
        SelectedNode = item;
        SelectedNode.NodeBackColor = "background-color:lightpink;";
    }
    protected override void OnInitialized()
    {
        
 
    }
    protected override void OnParametersSet()
    {
        base.OnParametersSet();
         
        LoadtvData();
        OnNodeClick(FindNode(tvData, CategoryId));
    }
    public void LoadtvData()
    {
 
 
        var q = from a in db.Categories
                where a.ParentCategoryId == null
                orderby a.CategoryName
                select new TvItem()
                {
                    IsActive = (bool)a.IsActive,
                    ItemName = a.CategoryName,
                    ItemId = a.Id,
                    Icon = "folder",
                    //ParentId = null,
                    HasChildren = (from b in db.Categories
                                   where b.ParentCategoryId == a.Id
                                   select b).Any(),
                    //Expanded = false,
                    Level = 1
 
                };
        if (!ShowInactive)
        {
            q = q.Where(a => a.IsActive == true);
        }
        tvData = new ObservableCollection<TvItem>(q.ToList());
        foreach (var item in tvData)
        {
            if (item.HasChildren == true)
                item.Icon = "folder";
        }
    }
 
    private async Task LoadChildren(TreeViewExpandEventArgs args)
    {
 
        // check if the item is expanding, we don't need to do anything if it is collapsing
        // in this example we will also check the type of the model to know how to identify the node and what data to load. If you use only one model for all levels, you don't have to do this
        if (args.Expanded && args.Item is TvItem)
        {
            TvItem currItem = args.Item as TvItem;
            if (currItem.Items?.Count > 0)
            {
                return; // item has been expanded before so it has data, don't load data again
                        // alternatively, load it again but make sure to handle the child items correctly
                        // either overwrite the entire collection, or use some other logic to append/merge
            }
            int itemIdentifier = currItem.ItemId;
            if (currItem.HasChildren == false)
            {
                StateHasChanged(); // inform the UI that the data is changed
                return;
            }
            var q = from a in db.Categories
                    where a.ParentCategoryId == currItem.ItemId
                    orderby a.CategoryName
                    select new TvItem()
                    {
                        IsActive = (bool)a.IsActive,
                        ItemName = a.CategoryName,
                        ItemId = a.Id,
                        ParentItemId = currItem.ItemId,
                        HasChildren = (from b in db.Categories
                                       where b.ParentCategoryId == a.Id
                                       select b).Any(),
                        Icon = a.Icon,
                        Expanded = false,
                        Level = currItem.Level + 1
                    };
            if (!ShowInactive)
            {
                q = q.Where(a => a.IsActive == true);
 
            }
 
 
            currItem.Items = new ObservableCollection<TvItem>(q.ToList());
            foreach (var item in currItem.Items)
            {
                if (item.HasChildren == true)
                    item.Icon = "folder";
            }
 
 
            StateHasChanged(); // inform the UI that the data is changed
        }
    }
    public TvItem FindNode(ObservableCollection<TvItem> data, int catId)
    {
        foreach (TvItem item in data)
        {
            if (item.ItemId == catId)
                return item;
            else
            {
                if (item.Items != null)
                {
                    foreach (TvItem subcat in item.Items)
                    {
                        if (subcat.ItemId == catId)
                            return subcat;
                        else
                        {
                            if (subcat.Items != null)
                            {
                                return FindNode(subcat.Items, catId);
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
}
0
Accepted
Ed
Top achievements
Rank 1
Iron
Veteran
Iron
answered on 18 Mar 2020, 12:56 PM

Minor bug fix

@page "/CategoryTree"
@using Telerik.Blazor.Components
@using AlderneyTreasures.Data
@using Telerik.Blazor.Components.Common.Editors
@using Telerik.DataSource.Extensions
@using Microsoft.EntityFrameworkCore;
@using System.Collections.ObjectModel
@inject ATDBContext db
@inject Blazored.Toast.Services.IToastService toastService;
 
 
<TelerikTreeView @ref="tv" Data="@tvData" OnExpand="@LoadChildren">
    <TreeViewBindings>
        <TreeViewBinding TextField="CategoryName" ItemsField="Categories" IconField="Icon">
            <ItemTemplate Context="ctx">
                @{
 
                    CategoryItem item = ctx as CategoryItem;
                    <span @onclick="@( _ => OnNodeClick(item) )" style="@(item.NodeBackColor)">
                        <img src="/images/folder.png" /> <strong>@item.CategoryName</strong>
                    </span>
                }
            </ItemTemplate>
        </TreeViewBinding>
        <TreeViewBinding Level="1" TextField="CategoryName" ItemsField="Categories" IconField="Icon">
            <ItemTemplate Context="ctx">
                @{
                    CategoryItem item = ctx as CategoryItem;
                    <span @onclick="@( _ => OnNodeClick(item) )" style="@(item.NodeBackColor)">
                        <img src="/images/folder.png" /> <strong>@item.CategoryName</strong>
                    </span>
                }
            </ItemTemplate>
        </TreeViewBinding>
    </TreeViewBindings>
</TelerikTreeView>
@code {
    private int categoryId;
    [Parameter] public int CategoryId
    {
        get { return categoryId; }
        set
        {
            categoryId = value;
 
        }
    }
    [Parameter]
    public EventCallback<int> CategoryIdChanged { get; set; }
 
    public bool ShowInactive { get; set; } = false;
    TelerikTreeView tv { get; set; }
    public ObservableCollection<CategoryItem> tvData { get; set; }
    public class CategoryItem
    {
        public string CategoryName { get; set; }
 
        public string Icon { get; set; }
        public int ItemId { get; set; } //will be used to identify the node, not for rendering in this example
        public int? ParentItemId { get; set; }
        public ObservableCollection<CategoryItem> Categories { get; set; }
        public bool Expanded { get; set; }
        public bool HasChildren { get; set; }
        public bool IsActive { get; set; }
        public bool Selected { get; set; }
        public int Level { get; set; }
        public string NodeBackColor { get; set; }
    }
 
    public CategoryItem SelectedNode { get; set; }
    public CategoryItem PrevSelectedNode { get; set; }
    private void OnNodeClick(CategoryItem item)
    {
        if (SelectedNode != null)
        {
            PrevSelectedNode = SelectedNode;
            PrevSelectedNode.NodeBackColor = "background-color:white;";
 
        }
        if (item != null)
        {
            SelectedNode = item;
            SelectedNode.NodeBackColor = "background-color:lightpink;";
        }
    }
    protected override void OnInitialized()
    {
 
 
    }
    protected override void OnParametersSet()
    {
        base.OnParametersSet();
 
        LoadtvData();
        OnNodeClick(FindNode(tvData, CategoryId));
    }
    public void LoadtvData()
    {
 
 
        var q = from a in db.Categories
                where a.ParentCategoryId == null
                orderby a.CategoryName
                select new CategoryItem()
                {
                    IsActive = (bool)a.IsActive,
                    CategoryName = a.CategoryName,
                    ItemId = a.Id,
                    Icon = "folder",
                    //ParentId = null,
                    HasChildren = (from b in db.Categories
                                   where b.ParentCategoryId == a.Id
                                   select b).Any(),
                    //Expanded = false,
                    Level = 1
 
                };
        if (!ShowInactive)
        {
            q = q.Where(a => a.IsActive == true);
        }
        tvData = new ObservableCollection<CategoryItem>(q.ToList());
        foreach (var item in tvData)
        {
            if (item.HasChildren == true)
                item.Icon = "folder";
        }
    }
 
    private async Task LoadChildren(TreeViewExpandEventArgs args)
    {
 
        // check if the item is expanding, we don't need to do anything if it is collapsing
        // in this example we will also check the type of the model to know how to identify the node and what data to load. If you use only one model for all levels, you don't have to do this
        if (args.Expanded && args.Item is CategoryItem)
        {
            CategoryItem currItem = args.Item as CategoryItem;
            if (currItem.Categories?.Count > 0)
            {
                return; // item has been expanded before so it has data, don't load data again
                        // alternatively, load it again but make sure to handle the child items correctly
                        // either overwrite the entire collection, or use some other logic to append/merge
            }
            int itemIdentifier = currItem.ItemId;
            if (currItem.HasChildren == false)
            {
                StateHasChanged(); // inform the UI that the data is changed
                return;
            }
            var q = from a in db.Categories
                    where a.ParentCategoryId == currItem.ItemId
                    orderby a.CategoryName
                    select new CategoryItem()
                    {
                        IsActive = (bool)a.IsActive,
                        CategoryName = a.CategoryName,
                        ItemId = a.Id,
                        ParentItemId = currItem.ItemId,
                        HasChildren = (from b in db.Categories
                                       where b.ParentCategoryId == a.Id
                                       select b).Any(),
                        Icon = a.Icon,
                        Expanded = false,
                        Level = currItem.Level + 1
                    };
            if (!ShowInactive)
            {
                q = q.Where(a => a.IsActive == true);
 
            }
 
 
            currItem.Categories = new ObservableCollection<CategoryItem>(q.ToList());
            foreach (var item in currItem.Categories)
            {
                if (item.HasChildren == true)
                    item.Icon = "folder";
            }
 
 
            StateHasChanged(); // inform the UI that the data is changed
        }
    }
    public CategoryItem FindNode(ObservableCollection<CategoryItem> data, int catId)
    {
        foreach (CategoryItem item in data)
        {
            if (item.ItemId == catId)
                return item;
            else
            {
                if (item.Categories != null)
                {
                    foreach (CategoryItem subcat in item.Categories)
                    {
                        if (subcat.ItemId == catId)
                            return subcat;
                        else
                        {
                            if (subcat.Categories != null)
                            {
                                return FindNode(subcat.Categories, catId);
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
}

 

 

0
Svetoslav Dimitrov
Telerik team
answered on 19 Mar 2020, 09:53 AM

Hello Ed,

Thank you for sharing your solution with the community! I have marked your last post as an answer to this topic.

Regards,
Svetoslav Dimitrov
Progress Telerik

 UI for Blazor
Tags
TreeView
Asked by
Ed
Top achievements
Rank 1
Iron
Veteran
Iron
Answers by
Svetoslav Dimitrov
Telerik team
Joel
Top achievements
Rank 2
Iron
Iron
Iron
Ed
Top achievements
Rank 1
Iron
Veteran
Iron
Share this question
or