I have a custom component for WinForms, on which graphics are drawn. Using the Ctrl right/left mouse buttons, I can add or remove objects.
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.KeyCode == Keys.ControlKey)
this.EditorMode = true;
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if (e.KeyCode == Keys.ControlKey)
this.EditorMode = false;
}
protected override void onm ouseDown(MouseEventArgs e)
{
if (!this.EditorMode)
{
base.OnMouseDown(e);
return;
}
if (e.Button == MouseButtons.Left)
{
// adding new object
}
else if (e.Button == MouseButtons.Right)
{
// deleting object
}
}
Everything works fine until I add something else to the custom control. The problem is that pressing the Ctrl key will no longer be handled by the controller, but by the element on which the focus is currently set.
And I need my keyboard shortcut to work regardless of which element the focus is on... What is the best way to do this?
I tried to redefine Processcmdkey, but it does not allow me to know if the key was pressed or released
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.ControlKey | Keys.Control))
{
MessageBox.Show("ctrl");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
what should I do to get the desired result: regardless of focusing on child controls, I can always add new objects to the drawing?
CodePudding user response:
Adding and Removing the MessageFilter
The message filter will be added on the OnHandleCreated
override and removed in the Dispose
method of MainForm
.
public partial class MainForm : Form, IMessageFilter
{
public MainForm() => InitializeComponent();
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if(!(DesignMode ) || _isHandleInitialized)
{
_isHandleInitialized = true; ;
Application.AddMessageFilter(this);
}
}
bool _isHandleInitialized = false;
// In MainForm.Designer.cs
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
Application.RemoveMessageFilter(this);
}
base.Dispose(disposing);
}
}
Handling PreFilterMessage
The MainForm will provide three event
hooks for ControlLeft
, ControlRight
, and NoCommand
. These will be fired in the PreFilterMessage
method.
public partial class MainForm : Form, IMessageFilter
{
const int WM_KEYDOWN = 0x100;
public bool PreFilterMessage(ref Message m)
{
if (Form.ActiveForm == this)
{
switch (m.Msg)
{
case WM_KEYDOWN:
var key = (Keys)m.WParam | ModifierKeys;
switch (key)
{
case Keys.Control | Keys.Left:
ControlLeft?.Invoke(this, EventArgs.Empty);
Text = "Control.Left";
break;
case Keys.Control | Keys.Right:
ControlRight?.Invoke(this, EventArgs.Empty);
Text = "Control.Right";
break;
default:
// Don't event if it's "just" the Control key
if(ModifierKeys == Keys.None)
{
Text = "Main Form";
NoCommand?.Invoke(this, EventArgs.Empty);
}
break;
}
break;
}
}
return false;
}
public event EventHandler ControlLeft;
public event EventHandler ControlRight;
public event EventHandler NoCommand;
}
Responding to events in the UserControl
The MainForm
events will be subscribed to in the OnHandleCreated
override of UserControlResponder
.
public partial class UserControlResponder : UserControl
{
public UserControlResponder()
{
InitializeComponent();
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if(!(DesignMode || _isHandleInitialized))
{
_isHandleInitialized = true;
var main = (MainForm)Parent;
main.ControlLeft = onControlLeft;
main.ControlRight = onControlRight;
main.NoCommand = onNoCommand;
}
}
private bool _isHandleInitialized = false;
char _tstCount = 'A';
private void onControlRight(object sender, EventArgs e)
{
BackColor = Color.LightBlue;
BorderStyle = BorderStyle.None;
var button = new Button
{
Text = $"Button {_tstCount }",
Size = new Size(150, 50),
};
button.Click = onAnyButtonClick;
flowLayoutPanel.Controls.Add(button);
}
private void onControlLeft(object sender, EventArgs e)
{
BackColor = Color.LightGreen;
BorderStyle = BorderStyle.None;
if(flowLayoutPanel.Controls.Count != 0)
{
var remove = flowLayoutPanel.Controls[flowLayoutPanel.Controls.Count - 1];
if(remove is Button button)
{
button.Click -= onAnyButtonClick;
}
flowLayoutPanel.Controls.RemoveAt(flowLayoutPanel.Controls.Count - 1);
}
}
private void onNoCommand(object sender, EventArgs e)
{
BackColor = Color.Transparent;
BorderStyle = BorderStyle.FixedSingle;
}
private void onAnyButtonClick(object sender, EventArgs e)
{
((MainForm)Parent).Text = $"{((Button)sender).Text} Clicked";
}
}