I'm working on a large C# winforms project. After explaining thousands of times to my end users that they have to press tab instead of enter in textboxes, datagrids, and wherever, I decided to add a checkbox somewhere, so users can optionally set if they want to replace enter with tab. I don't like it myself, because I think weird stuff will happen, but I'd like to try it.
The thing is that I have lots of forms, and lots of places where I would have to set a keydown event or similar. I would like to put all of this in one place, on application level. Is there a way for this?
I guess this is not possible, since some controls will expose the keydown event diverently (for example in cells of the gridview). You could iterate through all controls in a form recursively and assign the event for the basic controls though.
The event itself then could be handled in a central place
I'll advice you to create a separate class that the constructor accept the parameters you need (like the textbox), You create global variables and assign the parameters to the variables in the constructor.
Then Create the event handler in the class and then you can put your code in the event handler using the variables.
You can then call the class wherever you need the keydown event
Form level (you can implement the behaviour in the base form and inherit from it):
this.KeyPreview = true;
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
PressedEnter();
base.OnKeyDown(e);
}
private bool PressedEnter()
{
bool res = false; // true if handled
Control ctr = GetFocusedControl();
if (ctr != null && ctr is TextBox)
{
res = this.SelectNextControl(ctr, true, true, true, true);
}
return res;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr GetFocus();
private Control GetFocusedControl()
{
Control focusedControl = null;
IntPtr focusedHandle = GetFocus();
if (focusedHandle != IntPtr.Zero)
// if control is not a .Net control will return null
focusedControl = Control.FromHandle(focusedHandle);
return focusedControl;
}
It probably can be done on application level too: In your main form you'll have to prefilter messages from the message loop (using message filter: Application.AddMessageFilter(your filter)), check for message WM_KEYDOWN = 0x100, check if the pressed key was ENTER, then handle it same as above.You do it only once, in your main form, it'll work on all your child forms.
In your main form class:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.mouseMessageFilter = new MouseMoveMessageFilter();
this.mouseMessageFilter.TargetForm = this;
Application.AddMessageFilter(this.mouseMessageFilter);
}
protected override void OnClosed(EventArgs e)
{
Application.RemoveMessageFilter(this.mouseMessageFilter);
base.OnClosed(e);
}
private class MouseMoveMessageFilter : IMessageFilter
{
public FormMain TargetForm { get; set; }
public bool PreFilterMessage(ref Message m)
{
if (TargetForm.IsDisposed) return false;
int numMsg = m.Msg;
int VK_RETURN=0x0D;
if (m.Msg == 0x100 &&(int)m.WParam == VK_RETURN) // WM_KEYDOWN and enter pressed
{
if (TargetForm.PressedEnter()) return true;
}
return false;
}
}
sources:
https://stackoverflow.com/a/435510/891715
http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
http://www.autohotkey.com/docs/misc/SendMessageList.htm
It's much simpler to use a MessageFilter in combination with SendKeys:
public partial class Form1 : Form, IMessageFilter
{
public Form1()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x100)//WM_KEYDOWN
{
if (m.WParam.ToInt32() == 0xd)//VK_RETURN = 0xd
{
SendKeys.Send("{TAB}");
return true; //Discard the Enter key
}
}
return false;
}
}
Related
I have to validate a Usercontrol (textbox) from inserting special characters. In keypress event I use this code to handle this.
OnKeyPress Overwrite:
protected override void OnKeyPress(KeyPressEventArgs e) {
base.OnKeyPress(e);
MethodToPrevent(e);
}
MethodToPrevent Function:
private void MethodToPrevent(KeyPressEventArgs e) {
var regex = new Regex(#"[^a-zA-Z0-9\s]");
if (regex.IsMatch(e.KeyChar.ToString())) {
e.Handled = true;
}
}
Now this is working fine. But if user copy paste the string with special characters this is not working. How can I handle that?
Tried
protected override void OnTextChanged(EventArgs args)
But can not catch the Sender part here.
Actualy your code prevent to ctrl, but it not prevent to mouse right click and paste. Maybe you can prevent mouse right click or you can use TextChanged event.
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
base.OnKeyPress(e);
MethodToPrevent(e);
}
private void MethodToPrevent(KeyPressEventArgs e)
{
var regex = new Regex(#"[^a-zA-Z0-9\s]");
if (regex.IsMatch(e.KeyChar.ToString()))
{
e.Handled = true;
}
}
// If the user presses the wrong key, it is already blocked with the
//MethodToPrevent() before the TextChanged().
//it is only prevent mouse right click and paste
private void textBox1_TextChanged(object sender, EventArgs e)
{
var regex = new Regex(#"[^a-zA-Z0-9\s]");
if (regex.IsMatch(textBox1.Text.ToString()))
{
textBox1.Text = "";
}
}
Since you're apparently using a Custom Control (TextBox derived), some suggestions to handle the User Paste actions and filtering text edits in your Control (the filter part is what you provided, more work is required in this department to make this bullet-proof - not just in relation to error handling).
This Custom Control adds some features to the standard TextBox:
Filters chars handled by OnKeyPress, allowing cursor movements, Delete an BackSpace (I've added \b to the filter Regex, I think it was missing).
Filters WM_PASTE events in 3 different ways, using the PasteAction Enum, linked to the Public UserPaste Property, which defines the Control behavior in reaction to a Paste operation (modify as required):
Allow: a User can paste anything inside the TextBox
Partial: a User can paste only what the Regex filter allows, the rest is removed
Disallow: a User cannot paste anything
Has an option to allow Numbers only, at the base class level (no Regex). This is coupled by the feedback provided by the ErrorProvider class.
To allow the partial paste feature, the WndProc override intercepts WM_PASTE, filters the Text read from the Clipboard, using the Clipboard.GetText() method (with TextDataFormat.UnicodeText), then sends a EM_REPLACESEL message to the edit control, to add the modified Text (to the User, it appears as an actual paste operation).
► base.WndProc() is not called in any case and the Clipboard is untouched.
→ If you mean to notify a User of the action taken, please don't show a MessageBox in the WndProc method override.
Note: this is a modified version of the Custom Control I've already posted here (with some other methods that may come in handy), for the same exact matter. → If the two questions are actually related, let me know.
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Windows.Forms;
[ToolboxItem(true)]
[DesignerCategory("Code")]
public class TextBoxEx : TextBox
{
private bool m_NumbersOnly = false;
private Regex regex = new Regex(#"[^a-zA-Z0-9\s\b]", RegexOptions.Compiled);
public TextBoxEx() { }
public enum PasteAction
{
Allow,
Disallow,
Partial
}
public PasteAction UserPaste { get; set; }
public override string Text {
get => base.Text;
set {
if (!base.Text.Equals(value)) {
base.Text = regex.Replace(value, "");
}
}
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (regex.IsMatch(e.KeyChar.ToString())) {
e.Handled = true;
}
base.OnKeyPress(e);
}
protected override CreateParams CreateParams
{
[SecurityPermission(SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.UnmanagedCode)]
get {
CreateParams cp = base.CreateParams;
if (m_NumbersOnly) {
cp.Style |= NativeMethods.ES_NUMBER;
}
else {
cp.Style &= ~NativeMethods.ES_NUMBER;
}
return cp;
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg) {
case NativeMethods.WM_PASTE:
switch (UserPaste) {
case PasteAction.Disallow:
return;
case PasteAction.Partial:
string text = Clipboard.GetText(TextDataFormat.UnicodeText);
text = regex.Replace(text, "");
NativeMethods.SendMessage(this.Handle, NativeMethods.EM_REPLACESEL, 1, text);
return;
case PasteAction.Allow:
break;
}
break;
}
base.WndProc(ref m);
}
private class NativeMethods
{
internal const int WM_PASTE = 0x0302;
internal const int ES_NUMBER = 0x2000;
internal const int EM_REPLACESEL = 0xC2;
[DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, string lParam);
}
}
How should I override ShortcutsEnabled property of textbox to accept some of shortcuts and reject others?
clearify: I tried to create a component base on textbox and also changed its context menu to a more beautiful one. in order to show my context menu I tested 2 methods first using contextMenu/contextMenuStrip property, but it has a problem (if user presses the right mouse button and move out and leave button, this method won't work) other way was using ShortcutsEnabled property that works great. but I like to shortcuts like ctrl+C ,... remain active. here says that it is possible to override this property to specify shortcuts, but I can not find any example about how it can be done.
Edit: I did not find good way to do this. but for solving my problem (disable context menu while shortcuts are enable) I find a solution base on links that #AliTheOne and #x5657 provided. you can catch 0x007B message in WndProc , the good thing is you can still catch right mouse button click in mouse down event and use it for showing custom context menu and normal context menu will not show. Note that this would enable all normal shortcuts.
protected override void WndProc(ref Message m)
{
if (m.Msg != 0x007B)
{
base.WndProc(ref m);
}
}
and
void textBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
//show custom context menu
}
}
Based on the code for textbox I think you need to override ProcessCmdKey.
The base ShortcutsEnabled setter sets which shortcuts are enabled in a shortcutsToDisable field, but unfortunately that's private, so you'd have to "roll you own" implementation.
ProcessCmdKey would look something like this:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (this.ShortcutsEnabled == false) {
foreach (int shortcutValue in shortcutsToDisable) {
if ((int)keyData == shortcutValue ||
(int)keyData == (shortcutValue | (int)Keys.Shift)) {
return true;
}
}
}
//
// There are a few keys that change the alignment of the text, but that
// are not ignored by the native control when the ReadOnly property is set.
// We need to workaround that.
if (textBoxFlags[readOnly]) {
int k = (int)keyData;
if (k == (int)Shortcut.CtrlL // align left
|| k == (int)Shortcut.CtrlR // align right
|| k == (int)Shortcut.CtrlE // align centre
|| k == (int)Shortcut.CtrlJ) { // align justified
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
Base on this: TextBoxBase.cs
You can do this: we want to set alignment by Ctrl+R and Ctrl+L
If you want to disable any built-in shortcuts Like Ctrl+Z and assign new method to it just delete it from public override bool ShortcutsEnabled {} and put it in protected override bool ProcessCmdKey(..) and switch..case
public class MyTextBox : TextBox
{
private static readonly int _readOnly = BitVector32.CreateMask();
private static readonly int _shortcutsEnabled = BitVector32.CreateMask(_readOnly);
private static int[] _shortcutsToDisable;
private BitVector32 _textBoxFlags;
public override bool ShortcutsEnabled
{
get { return _textBoxFlags[_shortcutsEnabled]; }
set
{
if (_shortcutsToDisable == null)
{
_shortcutsToDisable = new int[]
{
(int) Shortcut.CtrlZ,(int) Shortcut.CtrlC, (int) Shortcut.CtrlX,
(int) Shortcut.CtrlV, (int) Shortcut.CtrlA, (int) Shortcut.CtrlL, (int) Shortcut.CtrlR,
(int) Shortcut.CtrlE, (int) Shortcut.CtrlY, (int) Keys.Control + (int) Keys.Back,
(int) Shortcut.CtrlDel, (int) Shortcut.ShiftDel, (int) Shortcut.ShiftIns, (int) Shortcut.CtrlJ
};
}
_textBoxFlags[_shortcutsEnabled] = value;
}
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (_shortcutsToDisable == null)
{
return false;
}
var k = (int)keyData;
switch (k)
{
case (int)Shortcut.CtrlL:
TextAlign=HorizontalAlignment.Left;
break;
case (int)Shortcut.CtrlR:
TextAlign = HorizontalAlignment.Right;
break;
default:
return base.ProcessCmdKey(ref msg, keyData);
}
return true;
}
}
I needed functionality that doesn't exist in the standard ComboBox, so I wrote my own from a TextBox and a form. When the user types in the TextBox, it shows a dropdown as a separate form.
Here's some of the relevant code:
internal class FilteredDropDown : Form
{
public Control OwnerControl { get; set; }
public bool CloseOnLostFocus { get; set; }
protected override OnLostFocus(EventArgs e)
{
if (CloseOnLostFocus && !OwnerControl.IsFocused)
this.Close();
}
protected override OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e)
// highlight the moused over item in the list
}
...
}
public class FilteredCombo : TextBox
{
private FilteredDropDown dropDown;
public FilteredCombo()
{
dropDown = new FilteredDropDown();
dropDown.OwnerControl = this;
}
public void ShowDropDown()
{
if (dropDown.Visible)
return;
dropDown.RefreshFilter();
var loc = PointToScreen(new Point(0, this.Height));
dropDown.Location = loc;
dropDown.CloseOnLostFocus = false;
int selectionStart = this.SelectionStart;
int selectionLength = this.SelectionLength;
dropDown.Show(this);
this.Focus();
this.SelectionStart = selectionStart;
this.SelectionLength = selectionLength;
dropDown.CloseOnLostFocus = false;
}
protected override OnLostFocus(EventArgs e)
{
if (dropDown.Visible && !dropDown.ContainsFocus())
dropDown.Close();
}
protected override OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
ShowDropDown();
}
...
}
There's obviously a whole lot more code than that to deal with all kinds of stuff irrelevent to my question.
The problem is when I put the FilteredCombo on a modal dialog. Somehow the FilteredDropDown form doesn't receive mouse events at all when it is parented by a modal dialog.
I've read something about WinForms filtering out events on all except the current modal dialog, I suspect that is what's going on, but I have no ideas of how to fix it. Is there some way to get the mouse up/down/move/click/etc. events to work when parented by a model dialog?
I had to go digging through the ShowDialog source code, and I found that it calls user32.dll EnableWindow(Handle, false) on all the windows except the shown one. The problem was that the FilteredDropDown already existed by the time the ShowDialog() method got called. I discovered two different ways to fix this:
Don't allow the DropDown to be shown until the parent form is shown. This is a bit trickier to guarantee, so I also implemented the second way.
Re-enable the DropDown window when it is made visible:
[DllImport("user32.dll")]
private static extern bool EnableWindow(IntPtr hWnd, bool enable);
protected override void OnVisibleChanged(EventArg e)
{
base.OnVisibleChanged(e);
if (this.Visible)
{
EnableWindow(this.Handle, true);
}
}
In Windows Forms, you can know, at any time, the current position of the cursor thanks to the Cursors class.
The same thing doesn't seem to be available for the keyboard. Is it possible to know if, for example, the Shift key is pressed?
Is it absolutely necessary to track down every keyboard notification (KeyDown and KeyUp events)?
if ((Control.ModifierKeys & Keys.Shift) != 0)
This will also be true if Ctrl+Shift is down. If you want to check whether Shift alone is pressed,
if (Control.ModifierKeys == Keys.Shift)
If you're in a class that inherits Control (such as a form), you can remove the Control.
The code below is how to detect almost all currently pressed keys, not just the Shift key.
private KeyMessageFilter m_filter = new KeyMessageFilter();
private void Form1_Load(object sender, EventArgs e)
{
Application.AddMessageFilter(m_filter);
}
public class KeyMessageFilter : IMessageFilter
{
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private bool m_keyPressed = false;
private Dictionary<Keys, bool> m_keyTable = new Dictionary<Keys, bool>();
public Dictionary<Keys, bool> KeyTable
{
get { return m_keyTable; }
private set { m_keyTable = value; }
}
public bool IsKeyPressed()
{
return m_keyPressed;
}
public bool IsKeyPressed(Keys k)
{
bool pressed = false;
if (KeyTable.TryGetValue(k, out pressed))
{
return pressed;
}
return false;
}
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
KeyTable[(Keys)m.WParam] = true;
m_keyPressed = true;
}
if (m.Msg == WM_KEYUP)
{
KeyTable[(Keys)m.WParam] = false;
m_keyPressed = false;
}
return false;
}
}
You can also look at the following if you use WPF or reference System.Windows.Input
if (Keyboard.Modifiers == ModifierKeys.Shift)
The Keyboard namespace can also be used to check the pressed state of other keys with Keyboard.IsKeyDown(Key), or if you are subscribing to a KeyDownEvent or similar event, the event arguments carry a list of currently pressed keys.
Most of these answers are either far too complicated or don't seem to work for me (e.g. System.Windows.Input doesn't seem to exist). Then I found some sample code which works fine:
http://www.switchonthecode.com/tutorials/winforms-accessing-mouse-and-keyboard-state
In case the page disappears in the future I am posting the relevant source code below:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace MouseKeyboardStateTest
{
public abstract class Keyboard
{
[Flags]
private enum KeyStates
{
None = 0,
Down = 1,
Toggled = 2
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern short GetKeyState(int keyCode);
private static KeyStates GetKeyState(Keys key)
{
KeyStates state = KeyStates.None;
short retVal = GetKeyState((int)key);
//If the high-order bit is 1, the key is down
//otherwise, it is up.
if ((retVal & 0x8000) == 0x8000)
state |= KeyStates.Down;
//If the low-order bit is 1, the key is toggled.
if ((retVal & 1) == 1)
state |= KeyStates.Toggled;
return state;
}
public static bool IsKeyDown(Keys key)
{
return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
}
public static bool IsKeyToggled(Keys key)
{
return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
}
}
}
Since .NET Framework version 3.0, it is possible to use the Keyboard.IsKeyDown method from the new System.Windows.Input namespace. For instance:
if (((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && Keyboard.IsKeyDown(Key.F))
{
// CTRL + F is currently pressed
}
Even though it's part of WPF, that method works fine for WinForm applications (provided that you add references to PresentationCore.dll and WindowsBase.dll). Unfortunately, however, the 3.0 and 3.5 versions of the Keyboard.IsKeyDown method did not work for WinForm applications. Therefore, if you do want to use it in a WinForm application, you'll need to be targeting .NET Framework 4.0 or later in order for it to work.
You can P/Invoke down to the Win32 GetAsyncKeyState to test any key on the keyboard.
You can pass in values from the Keys enum (e.g. Keys.Shift) to this function, so it only requires a couple of lines of code to add it.
if ((ModifierKeys == Keys.Control) && ((e.KeyChar & (char)Keys.F) != 0))
{
// CTRL+F pressed !
}
if (Control.ModifierKeys == Keys.Shift)
//Shift is pressed
The cursor x/y position is a property, and a keypress (like a mouse click/mousemove) is an event. Best practice is usually to let the interface be event driven. About the only time you would need the above is if you're trying to do a shift + mouseclick thing.
The best way I have found to manage keyboard input on a Windows Forms form is to process it after the keystroke and before the focused control receives the event. Microsoft maintains a built-in Form-level property named .KeyPreview to facilitate this precise thing:
public frmForm()
{
// ...
frmForm.KeyPreview = true;
// ...
}
Then the form's _KeyDown, _KeyPress, and / or _KeyUp events can be marshaled to access input events before the focused form control ever sees them, and you can apply handler logic to capture the event there or allow it to pass through to the focused form control.
Although not as structurally graceful as XAML's event-routing architecture, it makes management of form-level functions in Winforms far simpler. See the MSDN notes on KeyPreview for caveats.
if (Form.ModifierKeys == Keys.Shift)
does work for a text box if the above code is in the form's keydown event and no other control captures the keydown event for the key down.
Also one may wish stop further key processing with:
e.Handled = true;
In WinForms:
if( Form.ModifierKeys == Keys.Shift )
It sounds like a duplicate of Stack Overflow question Detect Shift key is pressed without using events in Windows Forms?.
If you need to listen to keys in any generic class what are pressed when a 'Form' Window, this is your code. It doesnt listen to global windows key events, so it cannot be used to see keys when the window is not active.
Form.cs
public partial class Form1 : Form
{
public Form1()
{
// Some other Code
// Register all Keys pressed
this.KeyPreview = true;
KeyHandler.Instance.Init();
this.KeyDown += Form1_KeyDown;
this.KeyUp += Form1_KeyUp;
// Some other Code in the constructor
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
// Fire event when a key is released
KeyHandler.Instance.FireKeyUp(sender, e);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
// Fire event when a key is pressed
KeyHandler.Instance.FireKeyDown(sender, e);
}
}
KeyHandler.cs
KeyHandler is a Singleton Class and can be accessed in any other Object through Handler.Instance... Easy right.
public class KeyHandler
{
#region Singleton
private static KeyHandler instance;
private KeyHandler()
{
currentlyPressedKeys = new List<Keys>();
}
public static KeyHandler Instance
{
get
{
if (instance is null)
{
instance = new KeyHandler();
}
return instance;
}
}
#endregion Singleton
private List<Keys> currentlyPressedKeys;
public List<Keys> GetCurrentlyPressedKeys { get { return currentlyPressedKeys; } }
public void FireKeyDown(object sender, KeyEventArgs e)
{
if (!currentlyPressedKeys.Contains(e.KeyCode))
{
currentlyPressedKeys.Add(e.KeyCode);
KeyEventKeyPressed(sender, e);
}
}
public void FireKeyUp(object sender, KeyEventArgs e)
{
currentlyPressedKeys.Remove(e.KeyCode);
KeyEventKeyReleased(sender, e);
}
public event EventHandler<KeyEventArgs> KeyPressed;
protected virtual void KeyEventKeyPressed(object sender, KeyEventArgs e)
{
EventHandler<KeyEventArgs> handler = KeyPressed;
handler?.Invoke(sender, e);
}
public event EventHandler<KeyEventArgs> KeyReleased;
protected virtual void KeyEventKeyReleased(object sender, KeyEventArgs e)
{
EventHandler<KeyEventArgs> handler = KeyReleased;
handler?.Invoke(sender, e);
}
public void Init()
{
// Nothing to initialize yet
}
}
// In any other Class/Object its now possible to receive KeyEvents that are fired when the 'Form' is active. So its possible to listen to key events in any Control object or anything else. Its possible to see if e.g. multiple keys are pressed like Shift+Ctrl+Q or something like that.
public class SomeClass
{
public SomeClass()
{
KeyHandler.instance.KeyPressed += Instance_KeyPressed
KeyHandler.Instance.KeyReleased += Instance_KeyReleased;
}
public void SomeMethod()
{
if (KeyHandler.Instance.GetCurrentlyPressedKeys.Contains(Keys.ShiftKey))
{
// Do Stuff when the method has a key (e.g. Shift/Control...) pressed
}
}
private void Instance_KeyPressed(object sender, KeyEventArgs e)
{
// Any Key was pressed, do Stuff then
}
private void Instance_KeyReleased(object sender, KeyEventArgs e)
{
// Do Stuff when a Key was Released
}
}
I'm attempting to create a custom AutoCompleteTextBox control in WinForms. The AutoComplete provided by the basic TextBox provides results only with (string).StartsWith as opposed to (string).Contains.
I am displaying the Auto-Complete search results in a ListBox docked in a separate Form. I can prevent the Form from stealing focus initially using via:
protected override bool ShowWithoutActivation
{
get { return true; }
}
Then, I can prevent the Form from gaining focus entirely by overriding the WndProc method via:
protected override void WndProc( ref Message m )
{
base.WndProc( ref m );
switch ( m.Msg )
{
case WM_MOUSEACTIVATE:
m.Result = ( IntPtr ) MA_NOACTIVATEANDEAT;
break;
default:
break;
}
When I do this, the ListBox contained in the Form will receive MouseMoved events, but not MouseClicked events.
Chaning MA_NOACTIVATEANDEAT to just MA_NOACTIVATE will pass the mouse events to the ListBox, but then will cause the clicks on the ListBox to steal focus from the Form the ListBox resides in - passing it to the 'floating' Form the ListBox is in.
Is there any way I can prevent the 'floating' Form from steeling focus from the 'main' Form while still getting MouseClick events within the ListBox?
Convert your floating form into a Usercontrol as below :-
Pull a ListBox on it to & hook onto Click event to simulate your scenario of Focus.
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication3
{
public partial class InactiveForm : UserControl
{
private const int WS_EX_TOOLWINDOW = 0x00000080;
private const int WS_EX_NOACTIVATE = 0x08000000;
private const int WS_EX_TOPMOST = 0x00000008;
[DllImport("user32")]
public static extern int SetParent
(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32")]
public static extern int ShowWindow
(IntPtr hWnd, int nCmdShow);
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
p.Parent = IntPtr.Zero;
return p;
}
}
public InactiveForm()
{
InitializeComponent();
}
public new void Show()
{
if (this.Handle == IntPtr.Zero) base.CreateControl();
SetParent(base.Handle, IntPtr.Zero);
ShowWindow(base.Handle, 1);
}
private void OnListBoxClicked(object sender, EventArgs e)
{
MessageBox.Show("Clicked List Box on floating control");
}
}
}
Code in the MainForm (with a button and its click handler attached):-
This invokes the floating control with ListBox.
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
InactiveForm f = new InactiveForm();
f.Show();
}
}
}
When you show the UserControl (borderless form) as your floating form, you will notice that it wont receive focus even if you clicked (selected) any of its children. In this case, the child is a ListBox on the usercontrol.
Refer here and here.
In addtion to previous answer by Angshuman Agarwal:
If you want the main form does not deactive when the UserControl show as floating form, modify some code to your UserControl:
private Control _mControl;
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
public new void Show(Control c)
{
if (c == null) throw new ArgumentNullException();
_mControl = c;
if (this.Handle == IntPtr.Zero) base.CreateControl();
SetParent(base.Handle, IntPtr.Zero);
ShowWindow(base.Handle, 1);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x86) //WM_NCACTIVATE
{
if (m.WParam != IntPtr.Zero) //activate
{
SendMessage(_mControl.Handle, 0x86, (IntPtr)1, IntPtr.Zero);
}
this.DefWndProc(ref m);
return;
}
base.WndProc(ref m);
}
I've created something similiar and my solution is similar to what lAbstract suggests. The floatin form has an event DoReturnFocus wich the custom TextBox (the AutoCompleteTextBox control in this case) subscribes to, and simply sets focus back to itself. To prevent the main form to appear in front of the floating form I set the main form as the Owner of the floating form, as owned forms are never displayed behind their owner form..
More on Form.Owner Property