To detect rotation of the mouse wheel in .NET/WinForms, I can override OnMouseWheel. Clicking can be detected by overriding OnMouseDown (it's just the Middle button). But how do I detect tilting of the wheel (tilt to the left/right for horizontal scrolling)? Neither OnMouseWheel, not OnMouseDown is being called when I tilt the mouse wheel.
Covered here; in short, you need to handle the windows message manually (at it isn't handled directly in .NET - code is from the linked article):
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.HWnd != this.Handle)
{
return;
}
switch (m.Msg)
{
case Win32Messages.WM_MOUSEHWHEEL:
FireMouseHWheel(m.WParam, m.LParam);
m.Result = (IntPtr)1;
break;
default:
break;
}
}
...
abstract class Win32Messages
{
public const int WM_MOUSEHWHEEL = 0x020E;//discovered via Spy++
}
Based on this article, if you have the IntelliPoint drivers, you will get WM_MOUSEHWHEEL messages.
Related
I am making a windows Winform with a custom resizer(no border). I draw a resizer grip and handle some messages in WndProc. Just a heads up: I don't know what happens inside the WndProc, I just know it does what I want it to do(I pasted as snippet from somehwere).
Now I want to add a panel that is docked to the bottom of my form. When I do this, however, my resizing functionality is gone. Is there some way to restore this functionality without having to program my own resizer in.
I would think the functionality is lost because it only handles the resizing on the main form and not on any of its controls.
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x84)
{ // Trap WM_NCHITTEST
Point pos = new Point(m.LParam.ToInt32());
pos = this.PointToClient(pos);
if (
pos.X >= this.ClientSize.Width - cGrip &&
pos.Y >= this.ClientSize.Height - cGrip)
{
m.Result = (IntPtr)17; // HTBOTTOMRIGHT
return;
}
}
base.WndProc(ref m);
}
As I said in previous questions, I am not an experienced C# programmer at all. I have very little experience and the methods I use can probably be insanely optimized, so a detailed description of your answer would be very appreciated.
I am having the Problem, that if I alter the LParam of a WM_MOVING message to keep my Form at a certain position, which is legal according to this , the Windows Aero Shake feature gets triggered and all other Windows minimize. The behaviour can be reproduced by creating a Windows Forms Project in Visual Studio and pasting the following code into the Form:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace FormsTest
{
public partial class ShakeTest : Form
{
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
public const int WM_MOVING = 0x0216;
public ShakeTest()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_MOVING:
{
RECT rec;
rec.bottom = 500;
rec.left = 100;
rec.top = 100;
rec.right = 500;
Marshal.StructureToPtr(rec, m.LParam, true);
m.Result = new IntPtr(1);
}
break;
}
base.WndProc(ref m);
}
}
}
If you now grab the Window's title bar and move the mouse around a bit, the Shake gesture should get triggered, even though the Window isn't moving at all.
I tested this on Windows 10 only so far.
So my question is, can i disable the Shake feature for a certain Window or Process? If not, can i prevent Windows from thinking that im shaking the Window any other Way?
Thanks!
Frankly, I don't know if that is possible to disable Aero Shake for a particular window. But I can suggest a workaround that will prevent the Aero Shake from triggering.
There's a possibility to direct all the mouse input to only one window (and this will also hide the mouse events from the Aero Shake handler). Check the SetCapture description at MSDN. Also we will need a GetCapture and ReleaseCapture functions.
There's one remark: once you have set the mouse capture to a window - you are responsible for handling all the mouse input. So to achieve the goal you'll need to implement your own handler that will move the window. Fortunately, it's not that difficult.
Here's a code sample with a dialog that changes its size when it gets moving and restores the size when the moving is finished. Aero Shake is also never triggered.
public partial class ShakeTest : Form
{
[DllImport("user32.dll")]
static extern IntPtr SetCapture(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetCapture();
[DllImport("user32.dll")]
static extern bool ReleaseCapture();
public const int WM_LBUTTONUP = 0x0202;
public const int WM_MOUSEMOVE = 0x0200;
public const int WM_NCLBUTTONDOWN = 0x00A1;
public const int HTCAPTION = 2;
private Point _lastCursorPos;
private Size _origianlSize;
public ShakeTest()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
// We need to get the moment when the user clicked on a non-client area
case WM_NCLBUTTONDOWN:
// We are interested only in a click on a title bar
if ((int) m.WParam == HTCAPTION)
{
// Set the capture so all the mouse input will be handled by this window
SetCapture(Handle);
// Keep the current cursor position to use it during the moving.
_lastCursorPos = Cursor.Position;
// Keep the original window size.
_origianlSize = Size;
// And change the dialog size to whatever you want
Size = new Size(300, 300);
}
break;
// Once we got the capture, we need to handle mouse moving by ourself
case WM_MOUSEMOVE:
// Check that our window has the capture
if (GetCapture() == Handle)
{
// Change the position of a window
Left += Cursor.Position.X - _lastCursorPos.X;
Top += Cursor.Position.Y - _lastCursorPos.Y;
_lastCursorPos = Cursor.Position;
}
break;
// When the left mouse button is released - it's time to release the mouse capture
case WM_LBUTTONUP:
// Check that our window has the capture
if (GetCapture() == Handle)
{
// Release the mouse capture
ReleaseCapture();
// Restore the size
Size = _origianlSize;
}
break;
}
base.WndProc(ref m);
}
}
I want to make mousescroll volume up and down event when the player is playing, but when the player starting the videofile, the focus is gone. How can i "force focus" to the volume trackbar?
What you could do is install a MessageFilter on your form. This will get you any mouse input before VLC's control.
Have your form implement IMessageFilter and the PreMessageFilter method which will get you your mousewheel. The paramater is a Win32 message. Here's some info on the WM_MOUSEWHEEL event.
public partial class Form1 : Form, IMessageFilter
public bool PreFilterMessage(ref Message m)
{
const int WM_MOUSEWHEEL = 0x020A;
if (m.Msg == WM_MOUSEWHEEL)
{
// The wheeldelta is stored in the highorder of WParam.
// The value of which will be 120 (positive for forward, negative for backward).
int hiWord = m.WParam.ToInt32() >> 16;
// So use the delta as a way to increment/decrement the volume
this.axVLCPlugin21.volume += 10 * (hiWord / 120);
Trace.WriteLine("vol: " + this.axVLCPlugin21.volume);
}
return false;
}
And also don't forget to register the message filter with the Application. You can do this in your form's constructor.
public Form1()
{
Application.AddMessageFilter(this);
InitializeComponent();
}
All three of my screen/states works fine, however, i implemented a fourth one to act as an information screen. Fine so far, but when i run the game, and press the 'H' key, it doesn't change the screen to another background (what I have done so far). Below is the code:
public void UpdateInformation(GameTime currentTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.H))
{
GameState = 4;
} // GAMESTATE 4 which is the instruction/Information screen.
}
This is the code for the game state in the update method:
protected override void Update(GameTime gameTime)
{
switch (GameState)
{
case 1: UpdateStarted(gameTime);
break;
case 2: UpdatePlaying(gameTime);
break;
case 3: UpdateEnded(gameTime);
break;
case 4: UpdateInformation(gameTime);
break;
}
base.Update(gameTime);
}
Here i am drawing the screen.
public void DrawInformation(GameTime currentTime)
{
spriteBatch.Begin();
spriteBatch.Draw(InfoBackground, Vector2.Zero, Color.White);
spriteBatch.End();
}
Below is the draw information code for the states:
protected override void Draw(GameTime gameTime)
{
switch (GameState)
{
case 1: DrawStarted(gameTime);
break;
case 2: DrawPlaying(gameTime);
break;
case 3: DrawEnded(gameTime);
break;
case 4: DrawInformation(gameTime);
break;
}
}
I hope this helps, it's just my H key is not responding, but my S key responds well and starts the game. Are four state/screens compatible with 'Gamestate'?
Thank you.
The H key will not work, because your Update code for the H key is in UpdateInformation...
What it actually does is: If you're in the Information screen, press H to go to the Information screen (which doesn't make sense)
You should move your H detection code somewhere more appropriate. Where is your S detection code?
Also, I would recommend using an enum instead of numbers for your game states.
enum gameStates
{
Started,
Playing,
Ended,
Information,
}
That way, it's much easier to maintain and understand. (See example below)
switch(GameState)
{
case gameStates.Started:
//Do something
break;
}
I'd like to know how can I perform for example a right mouse click, or press ENTER with XNA, is it possible? I want the XNA prog to do the action, not to check if I clicked it.
Thanks.
You can use the following to make the cursor of the mouse visible (if this is necessary):
protected override void Initialize()
{
// Make mouse visible
this.IsMouseVisible = true;
base.Initialize();
}
Create the following variables, this way you can save the current and previous mouseState:
MouseState mouseStateCurrent, mouseStatePrevious;
In the Update function you can use the following:
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
{
this.Exit();
}
// Get current mouseState
mouseStateCurrent = Mouse.GetState();
// Left MouseClick
if (mouseStateCurrent.LeftButton == ButtonState.Pressed)
{
// TODO when left mousebutton clicked
}
// Right MouseClick
if (mouseStateCurrent.RightButton == ButtonState.Pressed && mouseStatePrevious.RightButton == ButtonState.Released)
{
//TODO when right mousebutton clicked
}
mouseStatePrevious = mouseStateCurrent;
// Update
base.Update(gameTime);
}
Hopefully this is of some use to you!
Just call the function that clicking would call
So leftClick gets called when the user clicks, but you can call that function whenever you want
edit: Another thing you can do if you can do if you want to call mouseClick yourself (instead of leftClick) is to have your own variable sent in, and have an enum for different states you want to define.
enum CLICKS{left=0, right, middle);
public void mouseClick(){
mouseClick(-1);
}
public void mouseClick(int manualClick){
if(mouseStateCurrent.LeftButton == ButtonState.Pressed || manualClick == CLICKS.left)
leftClick();
}
public void leftClick(){
//do stuff
}
public void randomFunction(){
//doing stuff
mouseClick(CLICKS.left); //<-- will simulate a left click
}
If you'd like to simulate real mouse pressed, you can use the SendInput Win32 method.
Ths is not as easy as the technique already offered here, but the game will not be able to distinguish whether these clicks originated from real user clicks or simulated ones.
Also check out this StackOverflow question that discussed the same issue.