Get selected node?

7 posts, 3 answers
  1. Ed
    Ed avatar
    27 posts
    Member since:
    Dec 2019

    Posted 12 Mar Link to this post

    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

     

  2. Answer
    Svetoslav Dimitrov
    Admin
    Svetoslav Dimitrov avatar
    120 posts

    Posted 12 Mar Link to this post

    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
  3. Answer
    Joel
    Joel avatar
    190 posts
    Member since:
    Jun 2018

    Posted 12 Mar Link to this post

    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
    }
  4. Svetoslav Dimitrov
    Admin
    Svetoslav Dimitrov avatar
    120 posts

    Posted 13 Mar Link to this post

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

    Regards,
    Svetoslav Dimitrov
    Progress Telerik

     UI for Blazor
  5. Ed
    Ed avatar
    27 posts
    Member since:
    Dec 2019

    Posted 18 Mar Link to this post

    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;
        }
    }
  6. Answer
    Ed
    Ed avatar
    27 posts
    Member since:
    Dec 2019

    Posted 18 Mar in reply to Ed Link to this post

    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;
        }
    }

     

     

  7. Svetoslav Dimitrov
    Admin
    Svetoslav Dimitrov avatar
    120 posts

    Posted 19 Mar Link to this post

    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
Back to Top