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

Editor for FilterRow - Delay Filtering

7 Answers 175 Views
VirtualGrid
This is a migrated thread and some comments may be shown as answers.
Paul
Top achievements
Rank 1
Paul asked on 13 Jan 2017, 06:08 PM

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

7 Answers, 1 is accepted

Sort by
0
Dess | Tech Support Engineer, Principal
Telerik team
answered on 16 Jan 2017, 12:16 PM
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.
0
Paul
Top achievements
Rank 1
answered on 16 Jan 2017, 09:53 PM
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?
0
Paul
Top achievements
Rank 1
answered on 17 Jan 2017, 05:37 PM
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?
0
Dess | Tech Support Engineer, Principal
Telerik team
answered on 18 Jan 2017, 07:29 AM
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.
0
Paul
Top achievements
Rank 1
answered on 19 Jan 2017, 09:56 PM

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

 

0
Paul
Top achievements
Rank 1
answered on 19 Jan 2017, 11:06 PM

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

0
Dess | Tech Support Engineer, Principal
Telerik team
answered on 20 Jan 2017, 09:52 AM
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.
Tags
VirtualGrid
Asked by
Paul
Top achievements
Rank 1
Answers by
Dess | Tech Support Engineer, Principal
Telerik team
Paul
Top achievements
Rank 1
Share this question
or