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