OnItemsChanged and hierarchical source

15 posts, 1 answers
  1. Goran
    Goran avatar
    77 posts
    Member since:
    Feb 2011

    Posted 09 May 2012 Link to this post

    I have a TreeView that is bound to hierarchical data. The requirement is to do some custom logic whenever a new item is is added to underlying collection. New item is always added through ViewModel command.

    I am using AsHierachy() extension method to make the hierarchical collection, since data is retrieved from table in flat collection.

    var list = a.Results.AsHierarchy(x => x.Id, x => x.ParentId); // a.Results is IEnumerable<Group>
    List = new PagedCollectionView(new ObservableCollection<HierarchyNode<Group>>(list));

    The only method I found that Is fired on TreeView when new item is added is OnItemsChaned. However, this method is fired only when root item is added, and it is not fired when child item is added. However, TreeView control somehow detects that child item is added, and displays it correctly. How does the TreeView know that child item is added, and why OnItemsChanged is nor fired? And what event/method can notify that new item (root or child) is added?

  2. Petar Mladenov
    Admin
    Petar Mladenov avatar
    2891 posts

    Posted 14 May 2012 Link to this post

    Hi Goran,

     RadTreeView is an ItemsControl and its items  - the RadTreeViewItems are ItemControls too. This means that when you add an item (RadTreeViewItem or business item in databinding scenarios) the ItemsChanged of the RadTreeView fires if the added item is root level item, otherwise the ItemsChanged of the parent of the added item fires. An important rule here is that the RadTreeViewItems (items) are generated (created) when:
    1)Tree is virtualized and the items are root level items in the ViewPort, or non root levels in the ViewPort and their parent item is expanded. 
    2)Tree is Not virtualized and items are root level or non-root with expanded parent item.
    When an item is generated - the RadTreeView.ItemPrepared event fires.
    In your case, you can add a business item in the collections that are bound to the RadTreeView, but the ItemsChanged may not fire if the corresponding parent RadTreeViewItem is not yet generated. What exactly do you need to do in the event handler that you are looking for ? 

    All the best,
    Petar Mladenov
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  3. DevCraft banner
  4. Goran
    Goran avatar
    77 posts
    Member since:
    Feb 2011

    Posted 14 May 2012 Link to this post

    Hi Petar,

    as I have said in my first post, the requirement is to do some custom logic whenever a new item is added to TreeList, no matter in which position (root or child). Custom logic could be like: expand added node, select it automatically, edit it automatically. I am trying to improve your RadTreeView, because I am having redundant code on each View that a TreeView is hosted on. Programming custom logic itself is not a problem.

    So, when I am subclassing TreeView, how can I know that a new item was added on any level?

    Regards,
    Goran
  5. Petar Mladenov
    Admin
    Petar Mladenov avatar
    2891 posts

    Posted 17 May 2012 Link to this post

    Hi Goran ,

     There is no event to always fire when you add a RadTreeViewItem / business object in the RadTreeView(or particular RTItem) / in the ViewModel hierarchy. But when it comes to these operations:
    • expand added node
    • select added node it automatically
    • edit added node it automatically
    you can force the creating of the newly added item and then perform these operation. The best way to force this creation is RadTreeView.BringPathIntoView(string path). It automatically expands the parent items of the item in question. You can also get a particular RadTreeViewItem with the GetItemByPath() method and then set its IsExpanded / IsSelected / IsInEditMode properties to True. You can check out this useful blog post.
    Let us know if you need further assistance.

    Greetings,
    Petar Mladenov
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  6. Goran
    Goran avatar
    77 posts
    Member since:
    Feb 2011

    Posted 17 May 2012 Link to this post

    Hi Petar,

    this is why I didn't specify in the first post what kind of custom operations I need to do. I already know how to them, the problem is WHEN should I perform them. By the way, the links you have provided request that UI knows the path to item which was added, which it doesnt, since Adding is done by ViewModel. In this case TreeList is a UnitOfWork, which should contain all necessary information to perform next tasks.

    - an item is added to its collection, which it was able to understand and update UI
    - by setting simple property on TreeView like BringFocusOnNewItem = True, I should be able to auto focus new item
    - by settings simple property on TreeView like AutoEditNewItem = True, I should be able to Edit it automatically

    Lets take a look at Windows explorer. When you want to add a new folder, it adds new folder, brings it to focus, and starts editing it. Whenever user wants to add an item to TreeView, I would dare to say that these operations are the most common scenario.

    Now, in order to achieve this with Telerik RadTreeView, I would need to write some code in the View, and for each view the same code. So, to repeat my question, looking only from the RadTreeView angle only, if there is no out of the box solution, can we somehow use some base methods and override them, I just need to know when a new item is added.

    Regards,
    Goran
  7. Goran
    Goran avatar
    77 posts
    Member since:
    Feb 2011

    Posted 22 May 2012 Link to this post

    Hi Petar,

    still no resposne from you? As I have said, provided links are not helpfull, since adding node is done from the ViewModel, so the UI doesn't know the path to the new node.

    I know there is no out of the box event to catch adding new item on each level, but can we somesthing to modify TreeView behavior?

    Regards,
    Goran
  8. Hristo
    Admin
    Hristo avatar
    352 posts

    Posted 22 May 2012 Link to this post

    Hi,

    The TreeView uses lazy mode of creating its items (only when they are required). This means the TreeView will not be notified when arbitrary non required item is added to the collection. In other words there is no TreeView method or behavior you could override when items are inserted deep in the hierarchy.

    I'm not quite sure what you mean by "since adding node is done from the ViewModel, so the UI doesn't know the path to the new node". But I'm going to suggest you two approaches you could chose from depending on your case:

    1) If you are adding the item on user action (for example "insert" button click) and using code behind click handler you could instruct the TreeView to bring the item in this click handler.

    2) If you are using a command to the view model to insert the item, you could use an interface injected in the view model (for example a constructor injection) to call TreeView bring into view method. The approach is similar as if you want to open a MessageBox from your view model. You should be able to identify the path of newly created item at some point in the view model and there you should call the interface (something like ISupportBringToView.BringIntoView(string path) ).

    Hope this helps. Please let us know if you need more info on the topic or the suggested approaches are not applicable.

    Greetings,
    Hristo
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  9. Goran
    Goran avatar
    77 posts
    Member since:
    Feb 2011

    Posted 22 May 2012 Link to this post

    Hi Hristo,

    The TreeView uses lazy mode of creating its items (only when they are required). This means the TreeView will not be notified when arbitrary non required item is added to the collection. In other words there is no TreeView method or behavior you could override when items are inserted deep in the hierarchy.


    I am not sure what you mean by arbitrary-non required, probably you are referring to nested item, not the root level item. If this is true, then how does TreeView know that it needs to paint an arrow in front of root view item, when a child item is added to it? For example, if I have 3 root items, and none of them contains children, and then I insert a child item inside a 2nd root item, we have a visual notification that this item not contains children. Something has informed it that it now contains children. Cant we subscribe to this notification also?

    I am able to do this from the code behind, but its just that I have repetitive code on lots of places, so I am trying to avoid that, and make clean code.

    Regards,
    Goran
  10. Answer
    Hristo
    Admin
    Hristo avatar
    352 posts

    Posted 22 May 2012 Link to this post

    Hi,

    Sorry for the missleading explanation. The TreeView will receive notification for added items as children to already realized items (i.e. if the item "Root2" is visible, it will receive a notification about the inserted children).
    However, if you have a visible Root2 item, which is collapsed and contains child "Chlid_X" (meaning the "Child_X" is not visible) adding child to "Child_X" will not fire a notification because "Child_X" does not have a realized container.

    Generally, you will receive notifications about changing properties of only realized containers. By adding children you are actually manipulating the items collection which causes a notification to be fired for the item which owns the collection.

    You could attach an event to the collection changed event for every realized TreeView item. You could do this in the ItemPrepared event of the TreeView.

    All the best,
    Hristo
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  11. Goran
    Goran avatar
    77 posts
    Member since:
    Feb 2011

    Posted 22 May 2012 Link to this post

    Hi Hristo,

    I would like to keep this part of focusing the new item outside of the ViewModel, since it naturally doesn't belong there, Currently I have tried this:
    1) in ItemPrepared I subscribe to CollectionChanged.
    2) In CollectionChanged and OnItemsChanged I receive a new Data Item, not TreeViewItem, and I could not find any way to force preparation for this new item. Therefore I store this new data item for later use in ItemPrepared.

    private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            _newItem = e.NewItems[0];
      
            if (this.SelectedContainer != null)
                this.SelectedContainer.IsExpanded = true;
        }
    }

    The same thing I do for the root item

    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);
               // AutoEditNewItem is a custom property set in xaml
        if (AutoEditNewItem && e.Action == NotifyCollectionChangedAction.Add)
        {
            _newItem = e.NewItems[0];
        }
    }

    Finally I want to bring new item into view and start editing it.
    protected override void OnItemPrepared(RadTreeViewItemPreparedEventArgs e)
    {
        base.OnItemPrepared(e);
      
        if (AutoEditNewItem)
        {
            if (ReferenceEquals(e.PreparedItem.Item, _newItem))
            {
                _newItem = null;
                e.PreparedItem.BringIntoView();

                    e.PreparedItem.Loaded += (s, a) =>
                        {
                            // start editing node
                            ((RadTreeViewItem)a.Source).IsInEditMode = true;
                        };
            }
      
            var list = (INotifyCollectionChanged)e.PreparedItem.ItemsSource;
            list.CollectionChanged += CollectionChanged;
        }
    }

    So let me summarize now:

    1) Do you find my implementation in any scenario could create problems, so far I have not found any?
    2) Do you think I can simplify something here?
    3) It seems that BringIntoView doesnt work well when my parent node is not expanded. In this case it expands it, but while it starts showing the children, my new item, since it is last child, gets out of view when there are many children - it doesnt scroll to it. When parent is expanded, then it scrolls correctly to new item. How to solve this problem?

    Regards,
    Goran
  12. Hristo
    Admin
    Hristo avatar
    352 posts

    Posted 24 May 2012 Link to this post

    Hi,

    As far as I can see, your implementation looks clean and should work.

    Regarding the issue you are facing: you could expand the item's parent by setting IsExpanded=true and call the bring method in a dispatcher. This should give enough time to the TreeView (or expanding TreeViewItem) to prepare its children. Then the bring operation should be executed correctly.

    All the best,
    Hristo
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  13. Goran
    Goran avatar
    77 posts
    Member since:
    Feb 2011

    Posted 24 May 2012 Link to this post

    Hi Hristo,

    I am already expanding the parent (SelectedContainer is currently selected item to which I am adding child node)

    if (this.SelectedContainer != null)
                this.SelectedContainer.IsExpanded = true;

    The problem is, if parent node has 20 children items, for example, and I am adding another child item, this would result in next sequence of events:

    - Node is added to Parent collection, so I set IsExpanded = true, to expand parent node
    - items are firing ItemPrepared event, and it could happen that at this point only new item is going to fire this event (other children where already prepared before)
    - at this point I am calling BringIntoVIew method (tried with Dispatcher.BeginInvoke, same result), which brings new node into the view, and I see it in the treelist - everything is ok so far.
    - however, other children items are starting to show in treelist, so new item (which currently is shown on screen and is in edit mode, as expected), is moving down on the children list. This is because other items are being shown, one by one, above new item, so new item is smoothly getting out of the view.

    Any ideas?

    Regards,
    Goran
  14. Hristo
    Admin
    Hristo avatar
    352 posts

    Posted 29 May 2012 Link to this post

    Hi,

    Reading your explanation make me think this is caused by the expand collapse animations. Could you try to disable the animations and check if the newly created item is going to go outside of the view after it has just been brought (just set  telerik:AnimationManager.IsAnimationEnabled="False").

    If that is not helping, could you please send us a sample project.

    Kind regards,
    Hristo
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  15. Goran
    Goran avatar
    77 posts
    Member since:
    Feb 2011

    Posted 29 May 2012 Link to this post

    Hi Hristo,

    yes, this problem exists when animation is enabled.

    Regards,
    Goran
  16. Hristo
    Admin
    Hristo avatar
    352 posts

    Posted 30 May 2012 Link to this post

    Hello,

    You should disable the animations in this case. If you would like to have to default animated behavior during normal user interaction you should re-enable them after the item is brought into view or when the edit has been completed/canceled.

    Greetings,
    Hristo
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

Back to Top
DevCraft banner