how to get to newly inserted row MVVM

3 posts, 1 answers
  1. Randy Hompesch
    Randy Hompesch avatar
    148 posts
    Member since:
    Nov 2008

    Posted 21 Dec 2016 Link to this post

    In response to a button command my VM inserts a record at index 0 into  the observablecollection that is bound to the gridview.

    Now that I have it in the collection a new row automagically appears in the radgrid at the beginning. I'm trying to be a good boy and keep knowledge of my UI out of the VM. My gridview is marked as IsReadOnly = true. I only  allow editing via raddataform attached to the rowdetails. My questions are:

    1. How to automatically expand the row details of a newly inserted row (using MVVM)?

    2. Once they hit update the newly added row seems to be moved by the gridview to it's rightful place in the sort order. This is great.However, if you are using datapager the row could be moved to an unknown page leaving the user wondering where their new record went. I know there is a ScrollIntoView function, but I'm not sure I should be using that in a VM. Even if I did, how would I know what row to scroll to after it's been resorted? So, how can I get a newly added, newly sorted row to scroll to be the first row visible regardless of which page in the pager it lands and have it's rowdetail editor expanded using MVVM?

    Any help would be greatly appreciated.

    Thanks ... Ed

     

     

     

  2. Answer
    Stefan
    Admin
    Stefan avatar
    1073 posts

    Posted 22 Dec 2016 Link to this post

    Hi Ed,

    I will get straight to your questions.

    1.   A possible approach for this would be to define a boolean IsExpanded property in your business object. When adding the new item to the source collection you should set it to True. Based on its value, the details of the row will be expanded or collapsed. This can be achieved through the DetailsVisibility property of GridViewRow and an IValueConverter. Please, take a look at the two snippets below.
    <Style TargetType="{x:Type telerik:GridViewRow}">
        <Setter Property="DetailsVisibility"
                Value="{Binding IsExpanded, Converter={StaticResource conv}}" />
    </Style>

    public object Convert(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
    {
        var shouldExpand = (bool)value;
        if (shouldExpand)
        {
            return System.Windows.Visibility.Visible;
        }
     
        return System.Windows.Visibility.Collapsed;
    }

    2.   There is no out of the box mechanism for satisfying such requirement. For determining at which page the newly item is resorted you need to perform some manual calculations. You will need the index of the item from the Items collection of RadGridView. The data operations of the control are performed over this collection, thus when a sort operation is processed, the order of the elements in this collection will be as they appear in the UI. Having the index of the item and the overall item count, you can get the PageCount value of RadDataPager and set its PageIndex based on the performed calculation.

    When having the needed page being set, you can scroll to the newly added item using the built-in scrolling mechanism of RadGridView. Though I cannot suggest a particular implementation, a possible solution for keeping the MVVM environment intact would be to utilize an Attached Behavior.

    Hopefully, this helps. Feel free to update me should you need further assistance or have any concerns.

    Regards,
    Stefan X1
    Telerik by Progress
    Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
  3. Randy Hompesch
    Randy Hompesch avatar
    148 posts
    Member since:
    Nov 2008

    Posted 22 Dec 2016 in reply to Stefan Link to this post

    Got it! Here's my solution and it works great.

    The attached behavior is the right way to do this. Basically, when my "AddCommand" gets fired in my vm,

    I add a new row to my ObservableCollection which is bound to the gridview.

    I pick up the collection changed event in my behavior, find the new row, select it and expand it.

    Tastes great! Less Filling!

    I think I may finally be getting the hang of this MVVM thing.

    Thanks for all the guidance ... Ed

     

    <-- Add this to you namespaces -->
    xmlns:support="clr-namespace:Yourapp.Support"
     
    <-- add this just below the opening tag of the RadGridView
    <i:Interaction.Behaviors>
        <support:AddNewDetailsItemBehavior/>
    </i:Interaction.Behaviors>
     
    // Next, create a class for your behavior, I made mine as generic as possible.
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Interactivity;
    using Telerik.Windows.Controls;
    using Telerik.Windows.Controls.GridView;
     
    namespace YourApp.Support
    {
        public class AddNewDetailsItemBehavior : Behavior<RadGridView>
        {
     
            #region Overrides
            protected override void OnAttached()
            {
                base.OnAttached();
                AssociatedObject.Unloaded += OnUnloaded;
                AssociatedObject.Items.CollectionChanged  += OnCollectionChanged;
            }
            #endregion
     
            #region Implementation
            private void OnUnloaded(object sender, System.Windows.RoutedEventArgs e)
            {
                AssociatedObject.Unloaded -= OnUnloaded;
                AssociatedObject.Items.CollectionChanged -= OnCollectionChanged;
            }
     
            private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (e.Action.ToString() != "Add") // I'm only interested in inserts.
                    return;
                RadGridView gv = (RadGridView) AssociatedObject;
                var info = e.NewItems[0];
     
                RadDataPager Pager = gv.Parent.FindChildByType<RadDataPager>();
                int page = 0;
                // find the info we just inserted. Note that I always have an IsNew bool for my objects
                 info = (from dynamic a in gv.Items
                      where a.IsNew == true
                      select a).FirstOrDefault();
                if (info == null)
                {
                    // they  have some sorting going on and it's on another page
                    // wade through and find it.
                    for (int i = 1; i < Pager.PageCount; i++)
                    {
                        Pager.PageIndex = i;
                        info = (from dynamic a in gv.Items
                              where a.IsNew == true
                              select a).FirstOrDefault();
                        page++;
     
                        if (info != null)
                            break;
     
                    }
                }
                if (info == null)
                    throw new Exception("Assertion failed in AddNewDetailsItemBehavior. Please contact support.");
     
                Pager.MoveToPage(page);
                gv.ScrollIntoViewAsync(info, (f) =>
                {
                    int idx = gv.Items.IndexOf(info);
                    gv.SelectedItem = gv.Items[idx];
                     
                    gv.GetRowForItem(info).IsExpanded = true;
     
                });
           }
     
            #endregion
     
        }
    }
Back to Top