I have a winforms control derived from Button, whose purpose is to display some dialog.
So, the control can be "activated" by tabbing or by a mouse click according to the application state. Forcing tab access is required under some circumstances, to ensure previous fields are filled.
Therefore, I need to "capture" mouse clicks on it, so to allow or disallow the mouse event according to the application state.
To trap the tab key, I override OnEnter() and it works ok.
I also overrided OnClick(), OnMouseDown() and OnMouseClick() at no avail: the program control is always silently passed to the OnEnter() method.
The debugger never stops in the OnClick() or OnMouse*() methods, so I cannot signal the event origin and prevent the core execution when needed.
Could someone help? Of course, I don´t like to trap the windows message queue; some previous experiences were not happy.
TIA
Using simple message pump filtering, as described here (https://www.daniweb.com/programming/software-development/threads/304777/how-to-temporarily-disabling-mouse-clicks-on-a-vb-net-form) with a class like this
// relevant usings...
using System.Security.Permissions;
...
using System.Windows.Forms;
namespace YourNameSpace
{
public class MouseFilter : IMessageFilter
{
protected static bool inEffect = false;
public static bool ActiveFiltering { set { inEffect = value; } }
const int LButtonDown = 0x201;
const int LButtonUp = 0x202;
const int LButtonDoubleClick = 0x203;
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public bool PreFilterMessage( ref Message m )
{
if (!inEffect)
return false;
bool result = false;
switch (m.Msg) {
case LButtonDown:
case LButtonUp:
case LButtonDoubleClick:
result = true;
break;
}
return result;
}
}
}
The static property ActiveFiltering provides a way to enable/disable mouse clicks filtering as required.
Of course, the Program class must execute
Application.AddMessageFilter(new MouseFilter());
but, being inEffect = false, it don´t interfere normal general mouse operation.
My control disables the mouse when required, and takes care to left it enabled otherwise.
The solution is not perfect, but is what´s possible. The worst drawback is that while the control cancels the mouse clicks, no one can go to other control or the window self.
Related
I am developing a game in MonoGame and decided to create my own button class. In order to determine if these buttons were clicked, I determined if they were in the bounds of the button, and if the left mouse button was down. This has lead to two problems.
The mouse click doesn't need to start inside the button, and it registers immediately rather than after the mouse button is released (Inside the bounds of the button.)
When multiple buttons on different screens are in the same area, it results in clicking them both as the mouse button did not release fast enough.
How can I make the clicking behave more like the WinForm buttons?
To make mouseclicking work effectively you should do the following :
First thing we do is create a MouseInput class, this should track stuff like, mouseState, lastMouseState, MouseX, MouseY. The mouseState and lastMouseState work together to handle 1 single click. But for now you can just add this class to your project :
class MouseInput
{
private static MouseState mouseState;
private static MouseState lastMouseState;
public static MouseState MouseState
{
get { return mouseState; }
set { mouseState = value; }
}
public static MouseState LastMouseState
{
get
{
return lastMouseState;
}
set
{
lastMouseState = value;
}
}
public MouseInput()
{
}
public static int getMouseX()
{
return Mouse.GetState().X;
}
public static int getMouseY()
{
return Mouse.GetState().Y;
}
}
After you've done that you want to start loooking for a mouseclick everytime your update method gets executed :
MouseInput.LastMouseState = MouseInput.MouseState;
// Get the mouse state relevant for this frame
MouseInput.MouseState = Mouse.GetState();
After you've completed these 2 crucial steps you can continue and use your code anywhere you want like this :
if (MouseInput.LastMouseState.LeftButton == ButtonState.Released && MouseInput.MouseState.LeftButton == ButtonState.Pressed) {
//Execute code here
}
Ofcourse as the above if statement only checks if the player pressed the left mouse button, you'll still have to check if the mouse positions are inside the button.
If you have any questions, feel free to ask
You can use RoutedEvent that can invoke handlers on multiple listeners in an element tree, rather than just on the object that raised the event. you can read more about RoutedEvents because The concept of an attached event enables you to add a handler for a particular event to an arbitrary element rather than to an element that actually defines or inherits the event. When those events are not related to the element which, like in case of MouseDown, both raises and listens to the event.
So RoutedEvents might help you to solve your problem
Here is a sample code
public static readonly RoutedEvent SelectedEvent =
EventManager.RegisterRoutedEvent( "Selected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(MyCustomControl));
// .NET wrapper
public event RoutedEventHandler Selected
{
add { AddHandler(SelectedEvent, value); }
remove { RemoveHandler(SelectedEvent, value); }
}
// Raise the routed event "selected"
RaiseEvent(new RoutedEventArgs(MyCustomControl.SelectedEvent));
UI for XNA 4.0 like WinForms, written from scratch.
Create form and, button to it, call Form.Update() from Game.Update() method.
In Game.Draw() first call Form.Refresh(), then Form.Draw().
For processing mouse and keyboard input used managers from GameHelper.Input library.
I need to close the application (C#) when user doesn't use it - let's say that when there is no Click event on any form of the program (there are about 100 forms). Is there any way to do that without handling Click even on each form of the app. (I have the thread running each minute, where it could be checked)?
Thanks in advance!
You can hook into the application message loop using the Application.AddMessageFilter function. Write a message filter that inspects all mouse click messages and/or keyboard messages, or anything you're interested in.
For instance:
public class DetectActivityMessageFilter : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN)
{
// The left mouse button was pressed
}
return false;
}
}
I need to disable just the close button (minimize and maximize should be allowed) temporarily.
Every solution I've tried disables all the buttons or just disables the close button permanently. Is there a way to do it temporarily?
The way to permanently disable the close button is to set the CS_NOCLOSE style for the form's window class. To do this from a WinForms application, you override the form's CreateParams property and add the SC_NOCLOSE flag using the | operator, e.g.:
protected override CreateParams CreateParams
{
get
{
const int CS_NOCLOSE = 0x200;
CreateParams cp = base.CreateParams;
cp.ClassStyle = cp.ClassStyle | CS_NOCLOSE;
return cp;
}
}
This is a permanent solution, though, since you cannot update window class styles on-the-fly. You would have to destroy and recreate the window class.
However, you can instead disable the "Close" command in the system menu, which also also automatically disables the close button in the title bar.
internal static class NativeMethods
{
public const int SC_CLOSE = 0xF060;
public const int MF_BYCOMMAND = 0;
public const int MF_ENABLED = 0;
public const int MF_GRAYED = 1;
[DllImport("user32.dll")]
public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool revert);
[DllImport("user32.dll")]
public static extern int EnableMenuItem(IntPtr hMenu, int IDEnableItem, int enable);
}
public class MyForm : Form
{
// ...
// If "enable" is true, the close button will be enabled (the default state).
// If "enable" is false, the Close button will be disabled.
bool SetCloseButton(bool enable)
{
IntPtr hMenu = NativeMethods.GetSystemMenu(this.Handle, false);
if (hMenu != IntPtr.Zero)
{
NativeMethods.EnableMenuItem(hMenu,
NativeMethods.SC_CLOSE,
NativeMethods.MF_BYCOMMAND | (enable ? NativeMethods.MF_ENABLED : NativeMethods.MF_GRAYED));
}
}
}
Note that this really is a transient operation. If you do anything that causes the system menu to be modified by the framework (such as maximizing or minimizing the form), your modifications will be obliterated. More details are in my related answer here. This is normally a problem, and why you'd prefer to use the first solution. But in this case, since you want to dynamically disable and re-enable, it is no big deal.
Finally, do be mindful of the fact that what you're proposing runs counter to the Windows UI Guidelines for dialog boxes. They say, in essence, that users expect to see a close button and that its presence gives them a feeling of security that they can always safely "get out" of whatever popped up of the screen. Thus, you should not disable it. It does call out a progress dialog as an exception, but it goes on to say that a progress dialog should always have a "Cancel" button that allows aborting the operation. In that case, you can simply make the close button in the title bar invoke this "Cancel" button—no need to disable it.
Although it may be possible, I have never seen it. That's just not how programs do it and your program should follow known patterns so it's users know how to use it.
If closing a program is temporarily not possible, show a message explaining why, when your user tries. This way you can present a solution ("you must first do...") instead of simply presenting a problem ("cannot be closed").
In addition, there are multiple ways to close a form. You are only looking at one of them. Disabling one will still leave the others and you would want to prevent all options that lead to closing your window, so it's best to handle the Closing event appropriately.
isprocessing = true;
form.FormClosing += new FormClosingEventHandler(form_cancel);
private void form_cancel(object sender, FormClosingEventArgs e)
{
if (isprocessing)
{
e.Cancel = true; //disables the form close when processing is true
}
else
{
e.Cancel = false; //enables it later when processing is set to false at some point
}
}
This worked for me
The Close Button Is Disabled On Below Conditon :
If We Add : MessageBoxButtons.YesNo
DialogResult Dr = MessageBox.Show(this, "", "", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
You can't hide it, but you can disable it:
private const int CP_NOCLOSE_BUTTON = 0x200;
protected override CreateParams CreateParams
{
get
{
CreateParams myCp = base.CreateParams;
myCp.ClassStyle = myCp.ClassStyle | CP_NOCLOSE_BUTTON ;
return myCp;
}
}
Source: http://www.codeproject.com/Articles/20379/Disabling-Close-Button-on-Forms
If you absolutely need to hide it, the only way to do it is to create a borderless form, then draw your own minimize and maximize buttons. Here is an article on how to do this: http://www.codeproject.com/Articles/42223/Easy-Customize-Title-Bar
Before considering either of these solutions, you should maybe rethink why you need to do this. Depending on what you are trying to do, there is probably a much better way to present the UI to the user other than taking away the familiar 'X' close button.
As per other answers you are working against the guidelines and framework but, if you really must, one work around would be to put all your form content into a Usercontrol and then pass that between form instances that either have the close button enabled or disabled on load.
So as you trigger the disable or enable of the close button you create a new form instance in which the close button is toggled and then pass in the reference to your usercontrol, then dereference the usercontrol in the current form and transfer to the new form.
You'd likely get a some flicker as you did this. Terrible idea IMHO but it is "an" option.
Using Win32 API, you can do this the following way:
[DllImport("User32.dll")]
private static extern uint GetClassLong(IntPtr hwnd, int nIndex);
[DllImport("User32.dll")]
private static extern uint SetClassLong(IntPtr hwnd, int nIndex, uint dwNewLong);
private const int GCL_STYLE = -26;
private const uint CS_NOCLOSE = 0x0200;
private void Form1_Load(object sender, EventArgs e)
{
var style = GetClassLong(Handle, GCL_STYLE);
SetClassLong(Handle, GCL_STYLE, style | CS_NOCLOSE);
}
You will need to use GetClassLong / SetClassLong to enable the CS_NOCLOSE style. You then can remove it with the same operations, just use (style & ~CS_NOCLOSE) in SetClassLongPtr.
Actually, you can do this in WPF apps as well (yeah, I know, the question is about WinForms, but maybe someone will need this some day):
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var hwnd = new WindowInteropHelper(this).Handle;
var style = GetClassLong(hwnd, GCL_STYLE);
SetClassLong(hwnd, GCL_STYLE, style | CS_NOCLOSE);
}
Still, you should consider what others have suggested: just show a MessageBox or another kind of message to indicate that user should not close the window right now.
Edit:
Since window class is just a UINT, you can use GetClassLong and SetClassLong functions instead of GetClassLongPtr and SetClassLongPtr (as says MSDN):
If you are retrieving a pointer or a handle, this function has been superseded by the GetClassLongPtr function. (Pointers and handles are 32 bits on 32-bit Windows and 64 bits on 64-bit Windows.)
This resolves the problem described by Cody Gray regarding the absense of *Ptr funcs in 32-bit OS.
As i understand it, when a keyboard button is pressed it should invoke the KeyDown event for the control which has focus. Then, the KeyDown for the parent control, so on and so forth until it reaches main form. UNLESS - along the chain one of the EventHandlers did:
e.SuppressKeyPress = true;
e.Handled = true;
In my case, KeyDown events never get to the main form.
I have Form -> Panel -> button for example.
Panel doesn't offer a KeyDown Event, but it shouldn't stop it from reaching the main form right?
Right now as a work around I set every single control to call an event handler I wrote. I'm basically trying to prevent Alt-F4 from closing the application and instead minimize it.
[Edit]
If you want to trap Alt-F4 then there's no point trying at the control level as that keystroke is handled by the application - see How to Disable Alt + F4 closing form?
You can use an application message filter:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.AddMessageFilter(new TestMessageFilter());
Application.Run(new Form1());
}
}
public class TestMessageFilter : IMessageFilter
{
private int WM_SYSKEYDOWN = 0x0104;
private int F4 = 0x73;
public bool PreFilterMessage(ref Message i_Message)
{
Console.WriteLine("Msg: {0} LParam: {1} WParam: {2}", i_Message.Msg, i_Message.LParam, i_Message.WParam);
if (i_Message.Msg == WM_SYSKEYDOWN && i_Message.WParam == (IntPtr)F4)
return (true); // Filter the message
return (false);
} // PreFilterMessage()
} // class TestMessageFilter
}
Try creating an observer to capture your events:
http://ondotnet.com/pub/a/dotnet/2002/04/15/events.html
I've got a C# winforms application that runs in the background, listening for hotkeys to be pressed. When a hotkey is pressed, my form makes a brief appearance. The form is always running, but set to hidden until I receive a hotkey event, at which time I set the visible property to true. The code looks like this:
void hook_volumeDown(object sender, KeyPressedEventArgs e)
{
this.Visible = true;
}
It should be noted that the topmost property of this form is set to true.
The really odd part is, after my C# app has stolen focus from another application, it will never do it again. For example: I launch my app, then launch some fullscreep app like Team Fortress 2. Then I press my hotkey. Team Fortress 2 minimizes, and I see my form. Then, however, I can restore TF2, and press my hotkey again all I want (with the desired effect), and TF2 will remain focused.
At any rate, I'm looking for a way to fix this. I've found a lot of questions here covering similar problems, but all of them are related to creating/launching a new form, not making an existing one visible (unless I missed something). I could rework the application to create a new form every time I need one, but that would entail creating yet another form to be invisible all the time just to wait for hotkey events, so I'd rather leave it as it is.
Any ideas?
I think you problem is related to the fact that Visible = true behaves differently between the first and subsequent calls. The first time visible is called and the window handle has not been created, the Window is created by calling CreateWindowEx which has some style parameters which controls how the window should behave. I think you need to make sure that the window is created with the style WS_EX_NOACTIVATE, which you can do by overriding CreateParams.
Other things to try out:
1) The ShowWindow function (used by Visible = true) ignores the focus parameter the first time it is called (http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx) if the program provides a STARTUPINFO structure. Dig into reflector and find out if the Form class provides a STARTUPINFO structure and if so, how to manipulate it.
2) The Form has a ShowWithoutActivation property than can be overriden and set to true, have you overriden this?
Sorry for the "no exact answer", but I hope this at least gives you some starting points for further investigation. Good luck.
Seeing KeyPressedEventArgs being used in your function looks really strange. Hot keys can be implemented by P/Invoking the RegisterHotKey() API function. It sends a message to your window when the hot key is pressed. Here's an example of a form that's invisible at start up, springs alive when you press the hot key. Ctrl+Alt+U in this case:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
private const int MYKEYID = 0; // In case you want to register more than one...
public Form1() {
InitializeComponent();
this.FormClosing += (s, args) => UnregisterHotKey(this.Handle, MYKEYID);
}
protected override void SetVisibleCore(bool value) {
if (value && !this.IsHandleCreated) {
this.CreateHandle();
RegisterHotKey(this.Handle, MYKEYID, MOD_CONTROL + MOD_SHIFT, Keys.U);
value = false;
}
base.SetVisibleCore(value);
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_HOTKEY && m.WParam.ToInt32() == MYKEYID) {
this.Visible = true;
if (this.WindowState == FormWindowState.Minimized)
this.WindowState = FormWindowState.Normal;
SetForegroundWindow(this.Handle);
}
base.WndProc(ref m);
}
// P/Invoke declarations
private const int WM_HOTKEY = 0x312;
private const int MOD_ALT = 1;
private const int MOD_CONTROL = 2;
private const int MOD_SHIFT = 4;
[DllImport("user32.dll")]
private static extern int RegisterHotKey(IntPtr hWnd, int id, int modifier, Keys vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
}
}
Note that the SetForegroundWindow() function is the rub, possibly also the source of the problem you describe in your question. Windows doesn't permit an app to shove a window in the user's face when the user is actively using another window. At least several seconds of inactivity must expire before it will allow the window to steal the focus. With the given code, that is easy enough to see, the taskbar button of your form will be blinking. Avoid setting the ShowInTaskbar property to false. It isn't necessary to do so with this code, the taskbar button won't show up until the hot key is pressed.