A standard Windows dialog will flash if its owner window is clicked. The effect is similar to activating and deactivating the window.
When implementing a custom window border on my dialog, however, I can't figure out when I should flash the window. Windows does not flash the dialog for me.
Here's what I tried:
I watched all of the messages going to both the owner and dialog, but was unable to find any messages which exist solely to tell the window to flash.
I hooked Spy++ onto a default Windows dialog, but was also unable to find a "flash" message.
Looking in WinUser.h I couldn't find a "flash" message, so I am assuming it is some sort of combination of one or more messages with lParam and wParam specified.
Does anyone have any experience with this, or perhaps can point me to some pages which explain this? Any ideas are appreciated, since I have been working on this problem for several months now.
EDIT
In response to comments, here is the code for what I am currently using:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x0020)
{
if ((short)((long)lParam & 0xffff) == (-2))
{
short hiword = (short)((((long)lParam) >> 16) & 0xffff);
if (hiword == 0x0201 || hiword == 0x0204)
Flash(); // My function which simulates a window flash
}
}
return IntPtr.Zero;
}
There isn't a message that tells you that Windows wants your window to flash. What you can do, however, is watch for the same trigger that Windows uses to start flashing your window in the first place.
Your window will flash when it has an owner window that's disabled (the WS_DISABLED style bit is set) and the user clicks a mouse button on any part of the disabled window.
Internally, this is handled by DefWindowProc in response to the WM_SETCURSOR message:
If the low-order word of lParam is HTERROR, and
the high-order word of lParam is one of the mouse button down messages (WM_LBUTTONDOWN, etc), and
the window has an enabled owned popup window, then
DefWindowProc will call FlashWindowEx on the popup window
So to identify the trigger for when you should flash your dialog yourself, all you have to do is the same thing as Windows does. In the owner window's window procedure, handle the WM_SETCURSOR message, perform the above three tests, and if all three are true then you can trigger your own custom flashing for your dialog. And of course, in that situation you wouldn't pass the message back to DefWindowProc to handle.
Related
I hope to simulate a left mouse button click on another window, and hold the button for about 2 seconds. I have tried the following code:
int WM_LBUTTONDOWN = 0x0201;
int WM_LBUTTONUP = 0x0202;
SendMessage(hd, WM_LBUTTONDOWN, new IntPtr(1), lParam);
Thread.Sleep(2000);
SendMessage(hd, WM_LBUTTONUP, new IntPtr(1), lParam);
The parameter "hd" is the handle of another window and "lParam" contains coordinate infomation. But it didn't work as my expectation.I used breakpoint to debug the code. When "WM_LBUTTONDOWN" message was sent to another window, the push button in another window was clicked immediately, rather than be held and wait for the message "WM_LBUTTONUP".
When I used real mouse to click and hold the button, spy++ showed that there are not any other messages except "WM_MOUSEMOVE" between "WM_LBUTTONDOWN" and "WM_LBUTTONUP".
Picture of Spy++ showed
So, how to simulate mouse button down and being hold in C#? Any advice would be helpful, thank you!
You can’t simulate keyboard input by sending window messages. You need to use SendInput() instead (C# declaration).
See: Send keys through SendInput in user32.dll.
"mouse_event" API function can solve the problem.But the side effect is the mouse poiter will move actually, when the program is running you cannot move your mouse or unexpected wrong position would be clicked.
Is it possible to do the following with WinForms/C#?
Dynamically detect window size and position of a running program (for example Notepad.exe)?
Snap WinForm to specific position within Notepad.exe?
Minimize and maximize WinForm window with other process (so when Notepad is minimize, so is WinForm window)?
See for example (black shape would be WinForm window):
Essentially I need to create a toolbar for a program, and the toolbar should "snap" to that program in the same place regardless of position or size of window.
First find the handle of the notepad window:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
Just pass null for the first parameter and the caption ("Notepad"?) of the window as the second parameter.
An alternative would be to enumerate all windows and select the best match based on the caption:
using System.Runtime.InteropServices;
public delegate bool CallBackPtr(int hwnd, int lParam);
private CallBackPtr callBackPtr;
public class EnumReport
{
[DllImport("user32.dll")]
private static extern int EnumWindows(CallBackPtr callPtr, int lPar);
public static bool Report(int hwnd, int lParam)
{
Console.WriteLine("Window handle is "+hwnd);
return true;
}
}
static void Main()
{
// note in other situations, it is important to keep
// callBackPtr as a member variable so it doesnt GC while you're calling EnumWindows
callBackPtr = new CallBackPtr(EnumReport.Report);
EnumReport.EnumWindows(callBackPtr, 0);
}
Then attach a WndProc to it:
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.AddHook(new HwndSourceHook(WndProc));
In the WndProc respond to the resizing and moving of the window.
I am not sure about setting the toolbar as a child of the notepad window; that might have unexpected effects when Notepad tries to manage it and order its z-depth.
At the same time I doubt this to be a good thing; the user will be able to type 'below' the overlay and lose his cursor/text.
Find Notepad's window (FindWindow).
Create your window without borders.
Set your window as a child of Notepad's window (SetParent).
Your window will be anchored to the top left corner of Notepad's window. Minimizing will be handled automatically, but you'll need to resize your window when Notepad's window is resized (or maximized). You may also want to move Notepad's edit control.
WinForms can be used, but you'll need some interop calls.
I have to warn that this is not a very good idea. Your controls may conflict with controls inside host process's window, host process may rearrange controls the way you don't like, draw over your controls. In general, be ready to fight with numerous issues without a good clean solution, and to accept that there may be glitches when resizing etc.
See also:
Attach form window to another window in C#.
I'm working on my own software to operate the mouse on my computer using C# and the kinect SDK. I really want to try using it to play a game like Red Alert, or some sort of RTS, or even just for general navigation.
The problem that I've found is that when using a program with a different mouse, like red alert or going into a virtual machine where mouse integration isn't supported, the program will not pick up on the calls that the C# program is making to the System.Windows.Forms.Cursor calls, let alone the mouse_event calls. I'm new to interfacing with windows and what is happening here, can someone explain/pose a solution?
--UPDATE--
As an update, I'm still not entirely sure what's going on, but I seem to have found a workaround for red alert in particular;
Since red alert is a fairly low graphics program, it is trivial to run it within a virtual machine specifically for me, vmware workstation with an XP client. If you use the mouse_event code it works well, HOWEVER, something that I struggled with was finding the correct code to represent mouse movement. It would seem that the MOVE flag moves the mouse relatively, which I didn't want, and the absolute tag didn't move the mouse at all. It is, in fact, the OR of them that produces absolute movement on the screen, so my code for mouse movement and clicking emulation ended up looking like this:
mouse_event((int)0x00000002, cursor.X, cursor.Y, 0, 0);
for clicking and
mouse_event((int)(0x00000001 | 0x00008000), x, y, 0 0);
for mouse movement, where x and y are the new coordinates out of 65535 (the absolute range). Is it perfect? Nah. But it works for now. I think there's something to do with the way windows ignores certain programs when it runs ra, maybe because of compatibility mode? I don't have another game to test it with right now, but I'll post results with a non-compatibility mode in the future.
Pete
(It wouldn't let me post as an answer for another two hours and I have to sleep to catch a flight in the morning!)
You will have to do some low level windows messages to get this to work properly. Games using DirectX like Red Alert will not look at the System.Windows.Forms.Cursor at all. You will need to interface with the Windows User32.dll to send the appropriate messages to windows so it can route them appropriately to the other applications.
Here is some code to get you started looking in to sending messages via the User32 DLL in C#:
[DllImport("USER32.DLL")]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);
// Activate an application window.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
I hope this gets you started, but I don't have the time to go through each mouse message, what the wParam and lParam are, and what the Msg should be for each. I'm sure if you search around you can find the specific messages to send for each event such as mouse move, left click and right click.
Good luck.
I'm building a WPF application with some hackery involved to embed Form elements on the same render layer. Essentially what I do is I render the form off-screen and have a mock-up of the form in a WPF image. I also have a low level mouse hook which steals events from the WPF application if they are destined for the Form mock-up and instead use PostMessage(...) to send the events off to the hidden form element.
If I return a non-zero value from my hook procedure indicating to eat the event (even if I still call all the mouse hooks in the queue), the cursor gets stuck in one position. I'm assuming this is because the cursor position gets handled in some sort of WPF application layer that the event isn't reaching.
I figured that it was fine to prevent the WPF application from knowing about the event at all because I could just set the cursor position myself-- there are coordinates attached to a mouse event after all. Unfortunately, it seems that these mouse coordinates are horribly incorrect. In fact, no matter where my cursor is located, I always receive the same coordinates.
Here is my code:
if (nCode >= 0){
MOUSEHOOKSTRUCT_LL mousehookstruct_ll1 =
((MOUSEHOOKSTRUCT_LL)Marshal.PtrToStructure(((IntPtr)lParam), typeof(MOUSEHOOKSTRUCT_LL)));
if (mousehookstruct_ll1 != null) {
if ((user != null) && user.SystemMouseHookProc(nCode, wParam, lParam, new Point(mousehookstruct_ll1.pt_x, mousehookstruct_ll1.pt_y), mousehookstruct_ll1.dwExtraInfo)) {
return new IntPtr(1);// CallNextHookEx(this.MessageHookHandle, 1, wParam, lParam);// It doesn't matter that I don't call CallNextHook here.
}
}
}
GC.KeepAlive(this);
return CallNextHookEx(this.MessageHookHandle, nCode, wParam, lParam);
}
Then in user.SystemMouseHookProc(...) I print out the correct cursor position followed by the coordinates pulled by the mouse hook, and the output is always something like the following:
Cursor: 523,578
{X=1985777551,Y=1985777602} //This coordinate never changes
That output is clearly wrong. What can I do to get the correct mouse coordinates from a mouse hook?
Thank you.
P.S. This solution is derived from a popular one online. Unfortunately, that solution didn't meet my needs so I've had to alter it to this form.
Any reason the static property System.Windows.Forms.Control.MousePosition won't work?
In applications like Windows Explorer and Internet Explorer, one can grab the extended frame areas beneath the title bar and drag windows around.
For WinForms applications, forms and controls are as close to native Win32 APIs as they can get; one would simply override the WndProc() handler in their form, process the WM_NCHITTEST window message and trick the system into thinking a click on the frame area was really a click on the title bar by returning HTCAPTION. I've done that in my own WinForms apps to delightful effect.
In WPF, I can also implement a similar WndProc() method and hook it to my WPF window's handle while extending the window frame into the client area, like this:
// In MainWindow
// For use with window frame extensions
private IntPtr hwnd;
private HwndSource hsource;
private void Window_SourceInitialized(object sender, EventArgs e)
{
try
{
if ((hwnd = new WindowInteropHelper(this).Handle) == IntPtr.Zero)
{
throw new InvalidOperationException("Could not get window handle for the main window.");
}
hsource = HwndSource.FromHwnd(hwnd);
hsource.AddHook(WndProc);
AdjustWindowFrame();
}
catch (InvalidOperationException)
{
FallbackPaint();
}
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case DwmApiInterop.WM_NCHITTEST:
handled = true;
return new IntPtr(DwmApiInterop.HTCAPTION);
default:
return IntPtr.Zero;
}
}
The problem is that, since I'm blindly setting handled = true and returning HTCAPTION, clicking anywhere but the window icon or the control buttons causes the window to be dragged. That is, everything highlighted in red below causes dragging. This even includes the resize handles at the sides of the window (the non-client area). My WPF controls, namely the text boxes and the tab control, also stop receiving clicks as a result:
What I want is for only
the title bar, and
the regions of the client area...
... that aren't occupied by my controls
to be draggable. That is, I only want these red regions to be draggable (client area + title bar):
How do I modify my WndProc() method and the rest of my window's XAML/code-behind, to determine which areas should return HTCAPTION and which shouldn't? I'm thinking something along the lines of using Points to check the location of the click against the locations of my controls, but I'm not sure how to go about it in WPF land.
EDIT [4/24]: one simple way about it is to have an invisible control, or even the window itself, respond to MouseLeftButtonDown by invoking DragMove() on the window (see Ross's answer). The problem is that for some reason DragMove() doesn't work if the window is maximized, so it doesn't play nice with Windows 7 Aero Snap. Since I'm going for Windows 7 integration, it's not an acceptable solution in my case.
Sample code
Thanks to an email I got this morning, I was prompted to make a working sample app demonstrating this very functionality. I've done that now; you can find it on GitHub (or in the now-archived CodePlex). Just clone the repository or download and extract an archive, then open it in Visual Studio, and build and run it.
The complete application in its entirety is MIT-licensed, but you'll probably be taking it apart and putting bits of its code around your own rather than using the app code in full — not that the license stops you from doing that either. Also, while I know the design of the application's main window isn't anywhere near similar to the wireframes above, the idea is the same as posed in the question.
Hope this helps somebody!
Step-by-step solution
I finally solved it. Thanks to Jeffrey L Whitledge for pointing me in the right direction! His answer was accepted because if not for it I wouldn't have managed to work out a solution. EDIT [9/8]: this answer is now accepted as it's more complete; I'm giving Jeffrey a nice big bounty instead for his help.
For posterity's sake, here's how I did it (quoting Jeffrey's answer where relevant as I go):
Get the location of the mouse click (from the wParam, lParam maybe?), and use it to create a Point (possibly with some kind of coordinate transformation?).
This information can be obtained from the lParam of the WM_NCHITTEST message. The x-coordinate of the cursor is its low-order word and the y-coordinate of the cursor is its high-order word, as MSDN describes.
Since the coordinates are relative to the entire screen, I need to call Visual.PointFromScreen() on my window to convert the coordinates to be relative to the window space.
Then call the static method VisualTreeHelper.HitTest(Visual,Point) passing it this and the Point that you just made. The return value will indicate the control with the highest Z-Order.
I had to pass in the top-level Grid control instead of this as the visual to test against the point. Likewise I had to check whether the result was null instead of checking if it was the window. If it's null, the cursor didn't hit any of the grid's child controls — in other words, it hit the unoccupied window frame region. Anyway, the key was to use the VisualTreeHelper.HitTest() method.
Now, having said that, there are two caveats which may apply to you if you're following my steps:
If you don't cover the entire window, and instead only partially extend the window frame, you have to place a control over the rectangle that's not filled by window frame as a client area filler.
In my case, the content area of my tab control fits that rectangular area just fine, as shown in the diagrams. In your application, you may need to place a Rectangle shape or a Panel control and paint it the appropriate color. This way the control will be hit.
This issue about client area fillers leads to the next:
If your grid or other top-level control has a background texture or gradient over the extended window frame, the entire grid area will respond to the hit, even on any fully transparent regions of the background (see Hit Testing in the Visual Layer). In that case, you'll want to ignore hits against the grid itself, and only pay attention to the controls within it.
Hence:
// In MainWindow
private bool IsOnExtendedFrame(int lParam)
{
int x = lParam << 16 >> 16, y = lParam >> 16;
var point = PointFromScreen(new Point(x, y));
// In XAML: <Grid x:Name="windowGrid">...</Grid>
var result = VisualTreeHelper.HitTest(windowGrid, point);
if (result != null)
{
// A control was hit - it may be the grid if it has a background
// texture or gradient over the extended window frame
return result.VisualHit == windowGrid;
}
// Nothing was hit - assume that this area is covered by frame extensions anyway
return true;
}
The window is now movable by clicking and dragging only the unoccupied areas of the window.
But that's not all. Recall in the first illustration that the non-client area comprising the borders of the window was also affected by HTCAPTION so the window was no longer resizable.
To fix this I had to check whether the cursor was hitting the client area or the non-client area. In order to check this I needed to use the DefWindowProc() function and see if it returned HTCLIENT:
// In my managed DWM API wrapper class, DwmApiInterop
public static bool IsOnClientArea(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam)
{
if (uMsg == WM_NCHITTEST)
{
if (DefWindowProc(hWnd, uMsg, wParam, lParam).ToInt32() == HTCLIENT)
{
return true;
}
}
return false;
}
// In NativeMethods
[DllImport("user32.dll")]
private static extern IntPtr DefWindowProc(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam);
Finally, here's my final window procedure method:
// In MainWindow
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case DwmApiInterop.WM_NCHITTEST:
if (DwmApiInterop.IsOnClientArea(hwnd, msg, wParam, lParam)
&& IsOnExtendedFrame(lParam.ToInt32()))
{
handled = true;
return new IntPtr(DwmApiInterop.HTCAPTION);
}
return IntPtr.Zero;
default:
return IntPtr.Zero;
}
}
Here's something you could try:
Get the location of the mouse click (from the wParam, lParam maybe?), and use it to create a Point (possibly with some kind of coordinate transformation?).
Then call the static method VisualTreeHelper.HitTest(Visual,Point) passing it this and the Point that you just made. The return value will indicate the control with the highest Z-Order. If that's your window, then do your HTCAPTION voodoo. If it's some other control, then...don't.
Good luck!
Looking to do the same thing (make my extended Aero glass draggable in my WPF app), I just came across this post via Google. I read through your answer, but decided to keep searching to see if there was anything simpler.
I found a much less code-intensive solution.
Simply create a transparent item behind your controls, and give it a left mouse button down event handler which calls the window's DragMove() method.
Here's the section of my XAML which appears over my extended Aero glass:
<Grid DockPanel.Dock="Top">
<Border MouseLeftButtonDown="Border_MouseLeftButtonDown" Background="Transparent" />
<Grid><!-- My controls are in here --></Grid>
</Grid>
And the code-behind (this is within a Window class, and so DragMove() is available to call directly):
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
And that's it! For your solution, you would have to add more than one of these to achieve your non-rectangular draggable area.
simple way is
create stackpanel or every thing you want for your titlebar
XAML
<StackPanel Name="titleBar" Background="Gray" MouseLeftButtonDown="titleBar_MouseLeftButtonDown" Grid.ColumnSpan="2"></StackPanel>
code
private void titleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}