using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; namespace JD.UserControls.QuickOpenDialogs { /// /// The control used to prompt user. /// /// Internal to clarify the usage of the form, we only expose what we want from Control. internal partial class QuickOpenControl : UserControl { #region Variables private readonly HashSet m_downKeys = new HashSet(); #endregion #region Constructor public QuickOpenControl() { InitializeComponent(); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); SetStyle(ControlStyles.Selectable, false); SetStyle(ControlStyles.SupportsTransparentBackColor, true); UpdateStyles(); textBox.KeyDown += textBox_KeyDown; textBox.KeyUp += textBox_KeyUp; textBox.MouseWheel += textBox_MouseWheel; listBox.SelectedIndexChanged += listBox_SelectionChanged; } #endregion #region Properties public ListBox ListBox { get { return listBox; } } public int VisibleItemCount { get { return listBox.ClientSize.Height / listBox.ItemHeight; } } #endregion #region Methods protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, Keys keyData) { keyData &= ~Keys.Modifiers; if (keyData == Keys.Enter) { CancelEventArgs e = new CancelEventArgs(); OnActivated(e); // Returning false will let the textbox handle Enter, which will make a 'ding' sound // which provides a nice feedback that there is no valid item to activate return !e.Cancel; } return base.ProcessCmdKey(ref msg, keyData); } private bool HandleNavigationKeyPress(Keys key, bool repeating) { key &= ~Keys.Modifiers; switch (key) { case Keys.Up: MoveSelection(-1, repeating); break; case Keys.Down: MoveSelection(1, repeating); break; case Keys.PageUp: MoveSelection(-VisibleItemCount, repeating); break; case Keys.PageDown: MoveSelection(VisibleItemCount, repeating); break; default: return false; } return true; } private void MoveSelection(int count, bool repeating) { int listCount = listBox.Items.Count; if (listCount > 0) { int max = listCount - 1; int current = listBox.SelectedIndex; int nextIndex = current + count; bool wrapSelection = !repeating && (current == 0 || current == max); if (nextIndex < 0) nextIndex = wrapSelection ? max : 0; else if (nextIndex > max) nextIndex = wrapSelection ? 0 : max; // Clear before so it works in a multi select scenario // see ItemPickerExtension listBox.SelectedItems.Clear(); listBox.SelectedIndex = nextIndex; } } private bool IsKeyDown(Keys key) { return m_downKeys.Contains(key & ~Keys.Modifiers); } private void SetKeyDown(Keys key) { m_downKeys.Add(key & ~Keys.Modifiers); } private void SetKeyUp(Keys key) { m_downKeys.Remove(key & ~Keys.Modifiers); } #endregion #region Events public event EventHandler Activated; private void OnActivated(CancelEventArgs e) { Activated?.Invoke(this, e); } public Converter ProcessDialogKeyEvent; protected override bool ProcessDialogKey(Keys keyData) { var dialogKey = ProcessDialogKeyEvent; if (dialogKey != null && dialogKey(keyData)) { return true; } return base.ProcessDialogKey(keyData); } #endregion #region Event Handlers private void listBox_SelectionChanged(object sender, EventArgs e) { if (!textBox.Focused) { textBox.Select(); } } private void listBox_MouseDoubleClick(object sender, MouseEventArgs e) { OnActivated(new CancelEventArgs()); } private void textBox_KeyDown(object sender, KeyEventArgs e) { var repeating = IsKeyDown(e.KeyData); e.Handled = HandleNavigationKeyPress(e.KeyData, repeating); SetKeyDown(e.KeyData); } private void textBox_KeyUp(object sender, KeyEventArgs e) { SetKeyUp(e.KeyData); } void textBox_MouseWheel(object sender, MouseEventArgs e) { var scrollCount = (int)((float)-e.Delta / SystemInformation.MouseWheelScrollDelta) * SystemInformation.MouseWheelScrollLines; listBox.TopIndex += scrollCount; } #endregion } }