Implemented basic filtering for combo box items

This commit is contained in:
Patrick Simpson 2017-06-29 15:01:35 +02:00
parent acfc47f018
commit 27f1eb7b8a
5 changed files with 203 additions and 26 deletions

View File

@ -229,6 +229,7 @@
<Compile Include="Controls\KCopyLabel.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\KDataSource.cs" />
<Compile Include="Controls\KDialogButtons.cs">
<SubType>UserControl</SubType>
</Compile>

View File

@ -175,10 +175,12 @@ namespace Acacia.Controls
public Control Control
{
get
{
return ((ToolStripControlHost)Items[0]).Control;
}
get { return ControlHost.Control; }
}
public ToolStripControlHost ControlHost
{
get { return (ToolStripControlHost)Items[0]; }
}
public DropDown(KAbstractComboBox owner, Control control)
@ -303,6 +305,17 @@ namespace Acacia.Controls
private void ShowDropDown()
{
UpdateDropDownLayout();
// Show the drop down below the current control
_dropDown.Show(this.PointToScreen(new Point(0, Height - 1)));
}
protected void UpdateDropDownLayout()
{
if (_dropDown == null)
return;
// Calculate the dimensions of the drop-down
int maxHeight = GetDropDownHeightMax();
int minHeight = GetDropDownHeightMin();
@ -313,8 +326,13 @@ namespace Acacia.Controls
DropControl.MaximumSize = DropControl.MinimumSize = new Size(width, height);
// Show the drop down below the current control
_dropDown.Show(this.PointToScreen(new Point(0, Height - 1)));
_dropDown.Control.Bounds = _dropDown.ControlHost.Bounds;
System.Diagnostics.Trace.WriteLine(string.Format(
"Layout: {0}, host: {1}, control: {2}",
height,
_dropDown.ControlHost.Bounds,
_dropDown.Control.Bounds
));
}
protected abstract int GetDropDownHeightMax();

View File

@ -45,13 +45,17 @@ namespace Acacia.Controls
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
SelectedIndex = _committedIndex;
ResetSelectedIndex();
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
SelectedIndex = _committedIndex;
}
private void ResetSelectedIndex()
{
SelectedIndex = _committedIndex >= Items.Count ? -1 : _committedIndex;
}
protected override void OnMouseDown(MouseEventArgs e)
@ -90,12 +94,18 @@ namespace Acacia.Controls
{
// Don't notify until committed
}
public void ItemsChanged(int selectIndex)
{
_committedIndex = SelectedIndex = selectIndex;
}
}
#endregion
private readonly DropList _list;
#endregion
#region Items properties
[DefaultValue(true)]
@ -108,13 +118,6 @@ namespace Acacia.Controls
[Category("Behavior")]
public int ItemHeight { get { return _list.ItemHeight; } set { _list.ItemHeight = value; } }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[Localizable(true)]
[MergableProperty(false)]
[Category("Behavior")]
public ListBox.ObjectCollection Items { get { return _list.Items; } }
[DefaultValue(8)]
[Localizable(true)]
[Category("Behavior")]
@ -122,6 +125,8 @@ namespace Acacia.Controls
#endregion
private DisplayItem _selectedItem;
public KComboBox()
{
MaxDropDownItems = 8;
@ -136,11 +141,13 @@ namespace Acacia.Controls
{
if (_list.SelectedIndex >= 0)
{
Text = _list.SelectedItem.ToString();
_selectedItem = (DisplayItem)_list.SelectedItem;
Text = _selectedItem.ToString();
}
else
{
Text = "";
_selectedItem = null;
}
}
@ -154,24 +161,96 @@ namespace Acacia.Controls
_list.EndUpdate();
}
public object DataSource
/// <summary>
/// Wrapper for list items to use custom string formatting
/// </summary>
private class DisplayItem
{
get
private readonly KComboBox _owner;
private readonly object _item;
public DisplayItem(KComboBox owner, object item)
{
return _list.DataSource;
this._owner = owner;
this._item = item;
}
public override string ToString()
{
return _owner.DataSource.GetItemText(_item);
}
public override bool Equals(object obj)
{
bool result = obj is DisplayItem && ((DisplayItem)obj)._item == _item;
return result;
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
}
private KDataSourceRaw _dataSource;
public KDataSourceRaw DataSource
{
get { return _dataSource; }
set
{
_list.BindingContext = new BindingContext();
_list.DataSource = value;
_list.SelectedIndex = -1;
if (_dataSource != value)
{
_dataSource = value;
UpdateItems();
}
}
}
private void UpdateItems()
{
int oldCount = _list.Items.Count;
_list.BeginUpdate();
try
{
_list.Items.Clear();
int selected = -1;
foreach (object item in _dataSource.FilteredItems)
{
DisplayItem displayItem = new DisplayItem(this, item);
if (displayItem.Equals(_selectedItem))
selected = _list.Items.Count;
_list.Items.Add(displayItem);
}
System.Diagnostics.Trace.WriteLine(string.Format("FILTER: {0}", _list.Items.Count, selected));
// Select the current item only if new number of items is smaller. This means we don't keep selection
// when the user is removing text, only when they are typing more.
_list.ItemsChanged(_list.Items.Count < oldCount ? selected : -1);
}
finally
{
_list.EndUpdate();
}
UpdateDropDownLayout();
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
// Update the filter
if (DataSource != null)
{
DataSource.Filter = new KDataFilter(Text);
UpdateItems();
DroppedDown = true;
}
}
protected override int GetDropDownHeightMax()
{
return Util.Bound(Items.Count, 1, MaxDropDownItems) * ItemHeight + _list.Margin.Vertical;
return Util.Bound(_list.Items.Count, 1, MaxDropDownItems) * ItemHeight + _list.Margin.Vertical;
}
protected override int GetDropDownHeightMin()

View File

@ -0,0 +1,79 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Acacia.Controls
{
public class KDataFilter
{
public readonly string FilterText;
public KDataFilter(string filterText)
{
this.FilterText = filterText;
}
}
public interface KDataSourceRaw
{
System.Collections.IEnumerable Items { get; }
System.Collections.IEnumerable FilteredItems { get; }
KDataFilter Filter { get; set; }
string GetItemText(object item);
}
abstract public class KDataSource<T> : KDataSourceRaw
{
/// <summary>
/// Returns all the items
/// </summary>
abstract public IEnumerable<T> Items
{
get;
}
public IEnumerable<T> FilteredItems
{
get
{
if (string.IsNullOrWhiteSpace(Filter?.FilterText))
return Items;
return ApplyFilter();
}
}
private IEnumerable<T> ApplyFilter()
{
foreach (T item in Items)
{
if (MatchesFilter(item))
yield return item;
}
}
virtual protected bool MatchesFilter(T item)
{
return GetItemText(item).StartsWith(Filter.FilterText);
}
abstract protected string GetItemText(T item);
public string GetItemText(object item)
{
return GetItemText((T)item);
}
public KDataFilter Filter
{
get;
set;
}
IEnumerable KDataSourceRaw.Items { get{return Items;}}
IEnumerable KDataSourceRaw.FilteredItems { get { return FilteredItems; } }
}
}

View File

@ -216,7 +216,7 @@ namespace Acacia.UI
// Setting the datasource will trigger a select if there is a match
BeginUpdate();
DataSource = users;
//DataSource = users;
//SetItemsCore(users);
if (dropDown && text.Length != 0)
DroppedDown = true;