Simplified event handling for combo box, without message filter

This commit is contained in:
Patrick Simpson 2017-06-28 11:58:25 +02:00
parent ddec4f5bfc
commit 2490952c0b
3 changed files with 95 additions and 98 deletions

View File

@ -12,7 +12,7 @@ using System.Windows.Forms;
namespace Acacia.Controls
{
public abstract class KAbstractComboBox : ContainerControl, IMessageFilter
public abstract class KAbstractComboBox : ContainerControl
{
#region Properties
@ -63,11 +63,7 @@ namespace Acacia.Controls
_state.AddControl(_edit);
_edit.TextChanged += _edit_TextChanged;
_edit.LostFocus += _edit_LostFocus;
}
private void _edit_LostFocus(object sender, EventArgs e)
{
DroppedDown = false;
_edit.PreviewKeyDown += _edit_PreviewKeyDown;
}
@ -75,15 +71,15 @@ namespace Acacia.Controls
#region Text edit
private void _edit_TextChanged(object sender, EventArgs e)
{
OnTextChanged(new EventArgs());
}
override public string Text
{
get { return _edit.Text; }
set { _edit.Text = value; }
set
{
_edit.Text = value;
// Set the cursor after the text
_edit.Select(_edit.Text.Length, 0);
}
}
public void FocusEdit()
@ -91,6 +87,63 @@ namespace Acacia.Controls
_edit.Select();
}
private void _edit_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Escape:
// Escape closes the dropdown
if (DroppedDown)
{
DroppedDown = false;
e.IsInputKey = false;
return;
}
break;
case Keys.Down:
// Down opens the drop down
if (!DroppedDown)
{
DroppedDown = true;
e.IsInputKey = false;
return;
}
break;
}
OnPreviewKeyDown(e);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr GetFocus();
private Control GetFocusedControl()
{
Control focusedControl = null;
// To get hold of the focused control:
IntPtr focusedHandle = GetFocus();
if (focusedHandle != IntPtr.Zero)
// Note that if the focused Control is not a .Net control, then this will return null.
focusedControl = Control.FromHandle(focusedHandle);
return focusedControl;
}
private void _edit_LostFocus(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("_edit_LostFocus: " + GetFocusedControl()?.Name);
DroppedDown = false;
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
System.Diagnostics.Trace.WriteLine("OnGotFocus");
}
private void _edit_TextChanged(object sender, EventArgs e)
{
OnTextChanged(new EventArgs());
}
#endregion
#region Drop down
@ -118,6 +171,7 @@ namespace Acacia.Controls
_dropListHost.Padding = new Padding(0);
_dropListHost.Margin = new Padding(0);
_dropListHost.AutoSize = false;
_dropListHost.GotFocus += (s, e) => System.Diagnostics.Trace.WriteLine("_dropListHost.GotFocus");
_dropDown = new ToolStripDropDown();
_dropDown.Padding = new Padding(0);
@ -127,89 +181,7 @@ namespace Acacia.Controls
_dropDown.Items.Add(_dropListHost);
_dropDown.Closed += _dropDown_Closed;
_dropDown.AutoClose = false;
Application.AddMessageFilter(this);
}
protected override void OnHandleDestroyed(EventArgs e)
{
Application.RemoveMessageFilter(this);
base.OnHandleDestroyed(e);
}
public bool PreFilterMessage(ref Message m)
{
switch ((WM)m.Msg)
{
case WM.KEYDOWN:
switch((VirtualKeys)m.WParam.ToInt32())
{
case VirtualKeys.Escape:
// Escape closes the popup
if (DroppedDown)
{
DroppedDown = false;
return true;
}
break;
case VirtualKeys.Down:
// Down opens the drop down
if (!DroppedDown)
{
DroppedDown = true;
return true;
}
break;
}
ForwardKeyMessage(m);
break;
case WM.CHAR:
case WM.KEYUP:
ForwardKeyMessage(m);
break;
case WM.LBUTTONDOWN:
case WM.RBUTTONDOWN:
case WM.MBUTTONDOWN:
case WM.NCLBUTTONDOWN:
case WM.NCRBUTTONDOWN:
case WM.NCMBUTTONDOWN:
if (_dropDown.Visible)
{
//
// When a mouse button is pressed, we should determine if it is within the client coordinates
// of the active dropdown. If not, we should dismiss it.
//
int i = unchecked((int)(long)m.LParam);
short x = (short)(i & 0xFFFF);
short y = (short)((i >> 16) & 0xffff);
Point pt = new Point(x, y);
// Map to global coordinates
User32.MapWindowPoints(m.HWnd, IntPtr.Zero, ref pt, 1);
System.Diagnostics.Trace.WriteLine(string.Format("MOUSE: {0} - {1}", pt, _dropDown.Bounds));
if (!_dropDown.Bounds.Contains(pt))
{
// the user has clicked outside the dropdown
User32.MapWindowPoints(m.HWnd, Handle, ref pt, 1);
if (!ClientRectangle.Contains(pt))
{
// the user has clicked outside the combo
DroppedDown = false;
}
}
}
break;
}
return false;
}
private void ForwardKeyMessage(Message m)
{
System.Diagnostics.Trace.WriteLine("KEY: " + m);
if ((WM)m.Msg == WM.KEYDOWN)
{
OnPreviewKeyDown(new PreviewKeyDownEventArgs((Keys)m.WParam.ToInt32()));
}
_dropDown.GotFocus += (s, e) => System.Diagnostics.Trace.WriteLine("_dropDown.GotFocus");
}
// Cannot use visibility of _dropDown to keep the open state, as clicking on the button already
@ -249,10 +221,12 @@ namespace Acacia.Controls
_dropControl.Height = Util.Bound(prefSize.Height, minHeight, maxHeight);
// Show the drop down below the current control
_dropDown.Show(this.PointToScreen(new Point(0, Height)));
_dropControl.Capture = true;
}
else
{
_dropDown.Close();
_dropControl.Capture = false;
}
_isDroppedDown = value;
}

View File

@ -47,14 +47,25 @@ namespace Acacia.Controls
MaxDropDownItems = 8;
_list = new KListBox();
_list.IntegralHeight = true;
_list.TabStop = false;
DropControl = _list;
_list.DisplayMember = "DisplayName"; // TODO: remove from here
_list.SelectedIndexChanged += _list_SelectedIndexChanged;
_list.GotFocus += _list_GotFocus;
}
private void _list_GotFocus(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("_list_GotFocus");
}
private void _list_SelectedIndexChanged(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("SELECTED: " + _list.SelectedIndex);
if (_list.SelectedIndex >= 0)
{
Text = _list.SelectedItem.ToString();
}
}
public void BeginUpdate()
@ -95,9 +106,11 @@ namespace Acacia.Controls
{
switch(e.KeyCode)
{
// Forward cursor keys to the list
case Keys.Down:
case Keys.Up:
User32.SendMessage(_list.Handle, (int)WM.KEYDOWN, new IntPtr((int)e.KeyCode), IntPtr.Zero);
e.IsInputKey = false;
break;
default:
base.OnPreviewKeyDown(e);

View File

@ -16,18 +16,20 @@ namespace Acacia.Controls
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
DrawMode = DrawMode.OwnerDrawFixed;
SetStyle(ControlStyles.Selectable, true);
}
protected override void OnMouseMove(MouseEventArgs e)
{
Point point = PointToClient(Cursor.Position);
int newIndex = IndexFromPoint(point);
int newIndex = IndexFromPoint(PointToClient(Cursor.Position));
if (newIndex != _hoverIndex)
{
int oldIndex = _hoverIndex;
_hoverIndex = newIndex;
InvalidateItem(oldIndex);
InvalidateItem(_hoverIndex);
if (SelectedIndex != oldIndex && SelectedIndex != _hoverIndex)
InvalidateItem(SelectedIndex);
}
}
@ -37,6 +39,14 @@ namespace Acacia.Controls
_hoverIndex = -1;
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
// Perform the select here.
// TODO: this really is for ComboBox, where the list hides before the event is handled
SelectedIndex = IndexFromPoint(PointToClient(Cursor.Position));
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);