Editor for FilterRow - Delay Filtering

8 posts, 0 answers
  1. Paul
    Paul avatar
    35 posts
    Member since:
    Aug 2015

    Posted 13 Jan Link to this post

    I have a 'Person' column which I'm wanting to filter. The problem is, I'm trying to load on demand a suggestion drop down list as the user types in the filter box. But I don't want the VirtualGrid's filter to be updated as the user is typing. I want the VirtualGrid's filter to updated by the user selecting an option from a suggerstion drop down. After the user has entered at least 4 characters in the filter textbox, I query 50 items from the server to populate the suggestion drop down. As the user continues typing, the suggestion drop down will be reloaded with the new set of results. Again, no filter should be added to the VirtualGrid until the user clicks an item in the suggestion drop down. And the user should be able to use the up/down arrows to scroll through the suggestion list without it changing the text in the filter textbox as that would cause a requery and reload of the suggestion items.

     

    I tried this using VirtualGridDropDownListEditor and creating a custom AutoCompleteSuggestHelper. This worked great in displaying the suggested list and reloading as the user typed. I could arrow up and down without reloading the suggestion list. But once I clicked on the item in the list, I could get the VirtualGrid to be notified on the filter being set.

    I also tried using VirtualGridAutoCompleteBoxEditor but couldn't prevent it from immediately notifying the VirtualGrid of the change in the textbox without cancelling the textbox value change completely.

     

    Requirements:

    • Enter text in filter textbox without adding a filter the VirtualGrid
    • OnTextChange, reload a suggestion drop down list
    • Navigate items in suggestion drop down list using arrow keys without changing textbox's text
    • On suggestion drop down list item clicked (or pressing enter) set filter to VirtualGrid
  2. Dess
    Admin
    Dess avatar
    2101 posts

    Posted 16 Jan Link to this post

    Hello Paul,

    Thank you for writing.  

    By design, RadVirtualGrid will trigger the filtering operation immediately after entering a value in the filter cell. This basic behavior can't be stopped. However, note that you have the overall control of the filter operation in the FilterChanged event which is fired after changing the filter criterion. Hence, you can determine when exactly to execute the filter query considering the text in the filter cell. In order to force the FilterChanged event, it is just necessary to change the FilterDescriptors collection. Additional information how the filtering is executed in RadVirtualGrid is explained in the following help article: http://docs.telerik.com/devtools/winforms/virtualgrid/filtering/filtering 

    I hope this information helps. Should you have further questions I would be glad to help.

    Regards,
    Dess
    Telerik by Progress
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  3. Paul
    Paul avatar
    35 posts
    Member since:
    Aug 2015

    Posted 16 Jan in reply to Dess Link to this post

    I've almost got it working. For the DropDownList in a AutoCompleteSuggestHelper, how can I detect when the up/down arrow key is pressed and when an item in the list is clicked?
  4. Paul
    Paul avatar
    35 posts
    Member since:
    Aug 2015

    Posted 17 Jan in reply to Dess Link to this post

    Never mind. I found a way to create exactly what I described above using a custom VirtualGridDropDownListEditor and a custom AutoCompleteSuggestHelper. Is there somewhere I can share this code?
  5. Dess
    Admin
    Dess avatar
    2101 posts

    Posted 18 Jan Link to this post

    Hello Paul, 

    Thank you for writing back. 

    I am glad that you have found a suitable solution for your case. Feel free to post you code here in the forum. Thus, the community can benefit from it.

    An alternative approach is to provide a complete working project in our Code Library section which is intended to be used by our community for code sharing: http://www.telerik.com/support/code-library/winforms 

    I hope this information helps. If you have any additional questions, please let me know. 

    Regards,
    Dess
    Telerik by Progress
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  6. Paul
    Paul avatar
    35 posts
    Member since:
    Aug 2015

    Posted 19 Jan in reply to Dess Link to this post

    I abstracted the classes to make it easier for others to reuse my code

    VirtualAutoCompleteSuggestHelperBase.cs

    using System.Collections.Generic;
    using System.Windows.Forms;
    using Telerik.WinControls.UI.Data;
     
    namespace Telerik.WinControls.UI
    {
        public abstract class VirtualAutoCompleteSuggestHelperBase : AutoCompleteSuggestHelper
        {
            private bool _indexChanged; // Used to track and stop text changing due to index change
            private bool _mouseClicked; // Used to detect if a suggestion item was selected by a click
     
            protected VirtualGridEntityControllerEditor Editor { get; }
     
            /// <summary>
            /// The current selected object in the suggestion drop down list
            /// </summary>
            protected object SelectedValue { get; private set; }
     
            /// <summary>
            /// The current string entered to filter the drop down list by
            /// </summary>
            protected string CurrentText { get; private set; }
     
            /// <summary>
            /// The minimum number of characters requierd before the drop down list is displayed
            /// </summary>
            public int SearchMinLength { get; set; } = 4;
     
            public string DisplayMember { get; set; }
     
            public string ValueMember { get; set; }
     
            protected VirtualAutoCompleteSuggestHelperBase(VirtualGridEntityControllerEditor editor) : base(editor.DropDownList)
            {
                this.Editor = editor;
     
                this.DropDownList.ListElement.MouseDown += this.ListElement_OnMouseDown;
                this.DropDownList.SelectedIndexChanging += this.DropDownList_OnSelectedIndexChanging;
                this.Owner.EditableElement.TextChanging += this.EditableElement_OnTextChanging;
                this.Owner.EditableElement.KeyUp += this.EditableElement_OnKeyUp;
            }
     
            /// <summary>
            /// Gets a list of objects to populate the suggestion drop down list with.
            /// </summary>
            /// <param name="filter">A string to filter the objects being returned.</param>
            /// <returns>A list of filtered objects.</returns>
            protected abstract IEnumerable<object> GetItems(string filter);
     
            /// <summary>
            /// Reloads the suggestion drop down list with the provided list of objects.
            /// </summary>
            /// <param name="items">The list of objects to load into the suggestion list.</param>
            protected virtual void LoadItems(IEnumerable<object> items)
            {
                using (this.DropDownList.ListElement.DeferRefresh())
                {
                    this.DropDownList.ListElement.Items.Clear();
     
                    foreach (var o in items)
                    {
                        string text;
                        if (!string.IsNullOrWhiteSpace(this.DisplayMember))
                        {
                            try
                            {
                                text = o.GetType().GetProperty(this.DisplayMember).GetValue(o).ToString();
                            }
                            catch
                            {
                                text = o.ToString();
                            }
                        }
                        else
                        {
                            text = o.ToString();
                        }
     
                        this.DropDownList.ListElement.Items.Add(new RadListDataItem(o.ToString())
                        {
                            Tag = o,
                            Value = o,
                            TextWrap = false,
                            Enabled = true
                        });
                    }
                }
            }
     
            // Used to detect when an item in the suggestion list has been selected by a click
            private void ListElement_OnMouseDown(object sender, MouseEventArgs e)
            {
                // Check if left click
                if (e.Button == MouseButtons.Left)
                {
                    // Set toggle to true so the OnSelectedIndexChanging event
                    // will notify the Editor of the selected object
                    this._mouseClicked = true;
                }
            }
     
            // Used to store the current selected object
            // Notifies the Editor of the selection if the selection was made by a click
            private void DropDownList_OnSelectedIndexChanging(object sender, PositionChangingCancelEventArgs e)
            {
                // if no items selected, set selectedValue to null and return
                if (e.Position < 0)
                {
                    this.SelectedValue = null;
                    return;
                }
     
                // Store the selected object
                this.SelectedValue = this.DropDownList.ListElement.Items[e.Position].Value;
     
                // Set toggle to true so the OnTextChanging event
                // will know not to allow the text change
                this._indexChanged = true;
     
                // If the selection change occurred from a click,
                // notify the Editor
                if (this._mouseClicked)
                {
                    // Set the toggle back to false
                    this._mouseClicked = false;
                    // Notify the Editor
                    this.Editor.SetValue(this.SelectedValue);
                }
            }
     
            // Used to prevent the text from changing when scrolling through the drop down list
            private void EditableElement_OnTextChanging(object sender, TextChangingEventArgs e)
            {
                // Check if text is changing because of an index change
                if (this._indexChanged)
                {
                    // Reset the displayed text to the last string typed
                    this.Owner.EditableElement.Text = e.NewValue = this.CurrentText;
                    // Cancel the change
                    e.Cancel = true;
                    // Set the toggle back to false
                    this._indexChanged = false;
                    return;
                }
     
                // Return if no change
                if (this.CurrentText == e.NewValue) return;
     
                // Store the new value
                this.CurrentText = e.NewValue;
     
                // Check if we've reached the required number of
                // characters to display the suggestion list
                if (string.IsNullOrWhiteSpace(e.NewValue) || e.NewValue.Length < this.SearchMinLength)
                {// if not
                    // set select object to null
                    this.SelectedValue = null;
                    // clear the suggestion list
                    this.DropDownList.ListElement.Items.Clear();
                    // hide the suggestion list
                    this.DropDownList.ClosePopup();
                    return;
                }
     
                // get the list of items to display in the suggestion list
                var items = this.GetItems(this.CurrentText);
     
                // Load the items into the drop down list
                this.LoadItems(items);
     
                // display the suggerstion list if hidden
                if (!this.DropDownList.IsPopupOpen)
                    this.DropDownList.ShowPopup();
            }
     
            // Used to determine if the Enter of Escape key is pressed while
            // scrolling through the suggestion list
            private void EditableElement_OnKeyUp(object sender, KeyEventArgs e)
            {
                switch (e.KeyCode)
                {
                    // if Enter is pressed, notify the Editor of the selection
                    case Keys.Enter:
                        this.Editor.SetValue(this.SelectedValue);
                        break;
     
                    // if Escape is pressed, set the selection to null
                    case Keys.Escape:
                        this.DropDownList.SelectedIndex = -1;
                        break;
     
                    // another key was pressed, set the caret to the end of
                    // the textbox to allow the user to keep typing
                    default:
                        this.Owner.SelectionStart = this.CurrentText.Length;
                        this.Owner.SelectionLength = 0;
                        break;
                }
            }
     
            // Prevent the base class from updating the suggestion list
            protected override void SyncItemsCore() { }
        }
    }

     

    VirtualGridVirtualListEditorBase.cs

    using System.Windows.Forms;
     
    namespace Telerik.WinControls.UI
    {
        public abstract class VirtualGridVirtualListEditorBase : VirtualGridDropDownListEditor
        {
            private bool _allowChangeValue; // Used to prevent a value change unless set by SetValue method
     
            public RadDropDownListElement DropDownList => this.EditorElement as RadDropDownListElement;
     
            public VirtualAutoCompleteSuggestHelperBase VirtualAutoCompleteSuggestHelper { get; }
     
            public string DisplayMember
            {
                get { return this.VirtualAutoCompleteSuggestHelper.DisplayMember; }
                set { this.VirtualAutoCompleteSuggestHelper.DisplayMember = value; }
            }
     
            public string ValueMember
            {
                get { return this.VirtualAutoCompleteSuggestHelper.ValueMember; }
                set { this.VirtualAutoCompleteSuggestHelper.ValueMember = value; }
            }
     
            protected VirtualGridVirtualListEditorBase(params object[] args)
            {
                this.DropDownList.ArrowButton.Visibility = ElementVisibility.Collapsed;
                this.DropDownList.AutoCompleteSuggest = this.VirtualAutoCompleteSuggestHelper = this.CreateSuggestHelper(args);
                this.DropDownList.AutoCompleteMode = AutoCompleteMode.Suggest;
            }
     
            // Gets a new instance of a SuggestHelper
            protected abstract VirtualAutoCompleteSuggestHelperBase CreateSuggestHelper(params object[] args);
     
            // Set the value which is returned to the VirtualGrid
            public void SetValue(object value)
            {
                // Set toggle to true to allow the change
                this._allowChangeValue = true;
     
                // create a new item for the object
                var item = new RadListDataItem("", value);
                // add the item to the Editor's drop down list
                this.DropDownList.Items.Add(item);
                // select the item so the Editor acknowledges the value change
                this.DropDownList.SelectedItem = item;
            }
     
            public override void OnValueChanging(ValueChangingEventArgs e)
            {
                // prevent a value change unless invoked by the SetValue method
                if (this._allowChangeValue)
                {
                    this._allowChangeValue = false;
                    e.Cancel = false;
                }
                else
                {
                    e.Cancel = true;
                }
     
                base.OnValueChanging(e);
            }
        }
    }

     

    Then you can implement them like so:

    EntityControllerAutoCompleteSuggestHelper.cs

    using System.Collections.Generic;
    using System.Windows.Forms;
    using Foundation.Controllers.Interfaces;
    using Foundation.WCF;
    using Telerik.WinControls.Data;
    using Telerik.WinControls.UI.Data;
     
    namespace Telerik.WinControls.UI
    {
        public class EntityControllerAutoCompleteSuggestHelper : VirtualAutoCompleteSuggestHelperBase
        {
            // a func wrapping a proxy call
            private readonly Function<SearchParam, IEnumerable<IEntityController>> _proxyFunc;
     
            public EntityControllerAutoCompleteSuggestHelper(VirtualGridEntityControllerEditor editor, Function<SearchParam, IEnumerable<IEntityController>> proxyFunc) : base(editor)
            {
                // a func wrapping a proxy call
                this._proxyFunc = proxyFunc;
            }
     
            /// <summary>
            /// Creates the param required by the proxy
            /// </summary>
            /// <param name="filter">A string to filter the objects being returned.</param>
            /// <returns>A proxy param object.</returns>
            private SearchParam CreateSearchParam(string filter)
            {
                return new SearchParam
                {
                    FilterDescriptors = new[]
                    {
                        new FilterParam
                        {
                            PropertyName = this.ValueMember,
                            Operator = FilterOperator.Contains,
                            Value = filter
                        },
                    },
                    SortDescriptors = new[]
                    {
                        new SortParam
                        {
                            PropertyName = this.ValueMember,
                        },
                    },
                    Take = 50
                };
            }
     
            /// <summary>
            /// Gets a list of objects to populate the suggestion drop down list with.
            /// </summary>
            /// <param name="filter">A string to filter the objects being returned.</param>
            /// <returns>A list of filtered objects.</returns>
            protected override IEnumerable<object> GetItems(string filter)
            {
                // create param for proxyFunc
                var sParam = this.CreateSearchParam(filter);
     
                // get the list of items form the proxy
                return this._proxyFunc(sParam);
            }
        }
    }

     

    VirtualGridEntityControllerEditor.cs

    using System.Collections.Generic;
    using Foundation.Controllers.Interfaces;
    using Foundation.WCF;
     
    namespace Telerik.WinControls.UI
    {
        public class VirtualGridEntityControllerEditor : VirtualGridVirtualListEditorBase
        {
            public VirtualGridEntityControllerEditor(Function<SearchParam, IEnumerable<IEntityController>> proxyFunc) : base(proxyFunc)
            {
            }
     
            protected override VirtualAutoCompleteSuggestHelperBase CreateSuggestHelper(params object[] args)
            {
                var proxyFunc = args[0] as Function<SearchParam, IEnumerable<IEntityController>>;
     
                return new EntityControllerAutoCompleteSuggestHelper(this, proxyFunc);
            }
        }
    }

     

  7. Paul
    Paul avatar
    35 posts
    Member since:
    Aug 2015

    Posted 19 Jan in reply to Paul Link to this post

    Just realized a typo. In my implementations, Function<SearchParam, IEnumerable<IEntityController>> should be Func<SearchParam, IEnumerable<IEntityController>> 

    Got to love 3rd party intellisense auto-fill

  8. Dess
    Admin
    Dess avatar
    2101 posts

    Posted 20 Jan Link to this post

    Hello Paul, 

    Thank you for writing back. 

    In addition to the provided code snippet, it is necessary to replace the default editor with the custom one in the EditorRequired event as it is demonstrated in the following help article: http://docs.telerik.com/devtools/winforms/virtualgrid/editing/using-custom-editors

    I have also updated your Telerik points for the community effort.


    If you have any additional questions, please let me know. 

    Regards,
    Dess
    Telerik by Progress
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Back to Top