How to click through a WinForm, without taking focus? - c#

I cant find any information for this...
Basically I have a TopMost WinForm in C# that is an overlay for another application.
The overlay has buttons that I need to be able to press without stealing focus from the other application.
Is this possible, as I can't find any relative information.

You could store the mouse position:
Point point = Cursor.Position;
and then use an area with no controls in it to change the focus back to the DirectX9 window,
moving the cursor back to the original position before clicking again? That might work.
The only issue is that the button would still be there so you would need some way of getting it to click to the window rather than the button.
e.g.
Point p = Cursor.Position;
Cursor.Position = new point(x,y);
mouse(leftClick);
Cursor.Position = p;
mouse(leftClick);
the mouse(leftclick) method is here.
The other way to do this would be to track cursor position separately and then on each click, check if the click is within any controls and if so then run that method;
(Please tell me in the comments if there is a way to do this more efficiently as it would actually be quite useful to know)

Here is a solution that is working for me:
It identifies the target program by its title or parts of it and after each Button click it sets the target to be the foreground window again.
I can type into notepad, click a Button and type on..
I start by making the Form click-through.
You may want to do a lot more styling, maybe maximize, remove the control/min/max boxes and the title or even grab the target's position and size and overlay just the target window. This way you can make completely unobtrusive adornments to the target.
(I did that once because the target didn't treat touch screens right; so I made an overlay with holes in its region where the non-touch-defunct controls were. Nobody can even notice that the overlay is there!)
I write a few status info into to the output console..
Instead of the MainWindowTitle you could also use the ProcessName to identify the target. You can also incorporate code to re-establish the process after an error, like the user closing and restarting the target app..
using System.Runtime.InteropServices;
using System.Diagnostics;
...
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
string targetTitle = "Notepad2";
Process theProcess = null;
public Form1()
{
InitializeComponent();
// make the form clickthrough..
TransparencyKey = Color.Fuchsia;
BackColor = TransparencyKey;
// find and keep a reference to the target process
theProcess = findProcess(targetTitle);
// our overlay should stay on top
TopMost = true;
}
Process findProcess(string processTitle)
{
Process[] processList = Process.GetProcesses();
return processList.FirstOrDefault(
pr => pr.MainWindowTitle.ToLower().Contains(targetTitle.ToLower()));
}
void setActive(Process theProcess)
{
if (theProcess == null)
{ Console.WriteLine( "Process " + targetTitle + " not found";) return; }
bool ok = SetForegroundWindow(theProcess.MainWindowHandle);
Console.Write("Process " + theProcess.ProcessName + " (" +
theProcess.MainWindowTitle + + ok ? " OK." : " not OK!" );
}
private void button1_Click(object sender, EventArgs e)
{
// do your stuff..
Console.WriteLine("Testing Button 1");
// done: bring the target process back:
setActive(theProcess);
}
}

Related

Functionality to treat floating tabs as separate from main window

Context: I launch my main window. Within that window I create three new tabs. I float two of the tabs and leave the other within the main window. There are two problems I am currently trying to tackle:
I want to be able to minimize the main window without hiding my other floating tabs as well.
When shifting focus to another program (e.g. Chrome) then clicking back on my floating tab, I don't want all my other floating tabs + the main window to be brought back to front, in effect hiding my other program.
I believe this should be possible since the Visual Studio UI is built using WPF and I can achieve this functionality using floating tabs in VS.
Yes you can do this. The reason why it is being minimized is because the default owner of the floating window is the main window. so you have to set the floatingWindow.Owner = null; then you will also be able to put the main window in front of the floating window. if you want to switch between floating window and main window you can set floatingWindow.ShowInTaskbar = true;.
In my code i put it in a selectionChanged event handler so when i pop out a document it fires the selectionChanged event.
Document creation
private void userItem_Click(object sender, RoutedEventArgs e)
{
LayoutDocument ld = new LayoutDocument();
ld.Title = "All Users";
ld.ToolTip = "Manage all users";
//selection changed event
ld.IsSelectedChanged += Ld_IsSelectedChanged;
ld.IsActiveChanged += Ld_IsSelectedChanged;
Users users = new Users(ld);
ld.Content = users;
LayoutDocumentPane pane = ((todaysPayments.FindParent<LayoutDocumentPane>() ?? (panal.Children?[0] as LayoutDocumentPane)) ?? new LayoutDocumentPane());
pane.Children.Add(ld);
if (panal.ChildrenCount == 0)
{
panal.Children.Add(pane);
}
ld.IsSelected = true;
}
And the event handler
public void Ld_IsSelectedChanged(object sender, EventArgs e)
{
//get the floating windows from the DockingManager
manager.FloatingWindows.ToList().ForEach(floatingWindow =>
{
floatingWindow.Owner = null;
floatingWindow.ShowInTaskbar = true;
var fw = floatingWindow.Model as LayoutDocumentFloatingWindow;
floatingWindow.Title = fw?.RootDocument?.Title ?? "";
});
}

What is the correct way to prompt the mouse cursor to reappear if it was hidden because the user was typing?

I have a control in a WPF application that contains a text box and a submit button. The submit button is set as the "default" so that if the user presses Enter while the cursor is in the text box, the click handler for the button is run. The process kicked off by the click handler is lengthy, so I use a wait cursor coded like the following:
public class WaitCursor: IDisposable
{
private readonly System.Windows.Input.Cursor _oldCursor = null;
public WaitCursor()
{
_oldCursor = System.Windows.Input.Mouse.OverrideCursor;
System.Windows.Input.Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
// *** 1
}
~WaitCursor()
=> Dispose(false);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool unused)
{
System.Windows.Input.Mouse.OverrideCursor = _oldCursor;
// *** 2
}
}
If I type in the text box, the mouse cursor disappears. This is standard behaviour on Windows that I have observed in many applications. However, if I type in the text box and then press Enter without moving the mouse, then the mouse cursor is not shown while the application is busy - even if the user is moving it around. Effectively, the mouse cursor becomes invisible whenever it's over my application's windows, and remains that way until the application ceases being busy. This is undesirable.
I tried adding System.Windows.Forms.Cursor.Show() at the position marked with // *** 1 in my code above. This solved the problem of the cursor not being shown. But it introduced a new problem, in that the cursor no longer gets automatically hidden when the user types into text boxes in the application thereafter (for the lifetime of the application). The documentation page on Cursor.Show() says that calls to the Show() and Hide() methods should be paired, so I tried adding System.Windows.Forms.Cursor.Hide() at the position marked with // *** 2. This fixed all observed issues.
But I am not comfortable with this solution, for 2 reasons:
I am using a combination of the facilities provided by System.Windows.Forms.Cursor and System.Windows.Input.Cursor. This feels like it must be incorrect.
I am not comfortable instructing the application to "hide" the cursor when I do not in fact want the cursor to be hidden, even though the observed behaviour is that I merely undo the effect of the earlier call to Show(). It seems like something that isn't the intent of the framework designers (it really looks like what they had in mind was that you would Hide() the cursor and later Show() it) and might therefore break unpredictably.
What's the correct/proper way of solving this problem? If there is an officially sanctioned way to do this then I want to do that.
What about moving (simulated from code behind) the mouse before you start your process?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
KeyDown += _OnKeyDown;
}
[DllImport("User32.dll")]
private static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
private void _OnKeyDown(object sender, KeyEventArgs keyEventArgs)
{
if (keyEventArgs.Key == Key.Enter)
{
Point pos = GetMousePosition();
SetCursorPos((int)pos.X + 1, (int)pos.Y); //move 1 pixel
SetCursorPos((int)pos.X - 1, (int)pos.Y); //move back to original position
//start your process afterwards ..
}
}

Timer doesn't seem to tick ( until I minimize window )

This is the image of the design window:
Here is the MainForm.Designer.cs file:
namespace SamsCSharp24
{
partial class ImeObrasca
{
// irrelavent code is omitted, only event subscriptions are left
private void InitializeComponent()
{
// irrelavent code is omitted for brewity
//
// SelectPicture
//
this.SelectPicture.Paint += new System.Windows.Forms.PaintEventHandler(this.SelectPicture_Paint);
this.SelectPicture.Click += new System.EventHandler(this.SelectPicture_Click);
//
// Quit
//
this.Quit.Click += new System.EventHandler(this.Quit_Click);
//
// PictureBox
//
this.PictureBox.MouseLeave += new System.EventHandler(this.PictureBox_MouseLeave);
this.PictureBox.MouseEnter += new System.EventHandler(this.PictureBox_MouseEnter);
//
// btnOptions
//
this.btnOptions.Click += new System.EventHandler(this.btnOptions_Click);
//
// timerClock
//
this.timerClock.Tick += new System.EventHandler(this.timerClock_Tick);
}
#endregion
}
}
Here is the MainForm.cs file:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace SamsCSharp24
{
public partial class ImeObrasca : Form
{
public ImeObrasca()
{
InitializeComponent();
// when uncommenting below line, window is not seen in taskbar
// this.ShowInTaskbar = false;
}
// other code is omitted for brewity
private void SelectPicture_Paint(object sender, PaintEventArgs e)
{
// just for fun, change color of a button to light blue
SelectPicture.BackColor = Color.Azure;
}
private void timerClock_Tick(object sender, EventArgs e)
{
// when timer ticks, change label's text into current time of day
staticClock.Text = "Current time of day: " +
DateTime.Now.Hour.ToString() + " : " +
DateTime.Now.Minute.ToString() + " : " +
DateTime.Now.Second.ToString();
}
}
}
Timer control has following properties set via designer:
Enabled = true;
Interval = 1000
Name = timerClick
Tick (event) = timerClock_Tick
As for label, here are the properties also set with designer:
BorderStyle = FixedSingle
Name = staticClock
Autosize = false
Text =
Other properties are default or irrelevant ( like Location or Size )
PROBLEM:
When I run the application ( in Debug mode ), window appears with properly placed controls and with proper look. Every other part of the code works successfully ( picture opening / drawing etc ) but the label remains empty, as initially set in the designer.
After I minimize / maximize the window, the label text is set correct. I have tried to move the part of the window with label "out" of the screen and get it back to see what happens. The text in the label was changed sometimes -> it didn't update correct.
MY EFFORTS TO SOLVE THE PROBLEM:
This is my first time trying out C# and WinForms so I have tried to find some online documentation on timers.
After examining .Designer.cs file I have found out that the timer from toolbox belongs to System.Windows.Forms.Timer class. I found nothing there to help me, since in Remarks section is stated that setting property Enabled to true starts the timer, and setting it to false stops it.
I have tried to put simple message box, and it started popping properly when the window was minimized. When window is in normal state nothing showed, but other parts of the program worked well ( picture opening / drawing / etc ).
After trying to Google for solution / searching here on SO, I have found no concrete solution ( although some suggestions were made, but as I said, they weren't helpful to me ).
QUESTION:
How to modify timer's tick handler, so label's text can be modified every second?
What am I doing wrong?
As Hans Passant stated in one of the comments, "Paint event handlers should only paint, they should never change properties that cause the Paint event to be fired again. Such shenanigans cause the UI thread to burn 100% core, never getting to dispatch the low-priority synthesized messages. Like WM_TIMER. Minimizing the window stops that, temporarily."

C# ActiveX--how to keep the pops-up dialog(windows.Forms.Form) always on top of browser(IE8)

I have write the ActiveX using C# to communicate with the other browser-based system b, it need pops up an dialog in an other thread because of the existing architecture. the current behavior is that the dialog can be hidden behind if i click the browser title. Is it possible to keep the pops-up dialog always on the top of browser(IE8)? Thanks in advance.
public int operation()
{
....
MyMsgBox myMsgBox = new MyMsgBox(message,title);
evt = System.Threading.AutoResetEvent(false);
Thread showDialogThread = new Thread(ShowMsgDialog);
ShowDislogThread.SetApartmentState(System.Threading.ApartmentState.STA);
showDialogThread.Start(myMsgBox);
System.Threading.WaitHanle.WaitAll(new System.Threading.WaitHandle[] {evt});
....
}
public void ShowMsgDialog(object requestObj)
{
MyMsgBox msgBox = (MyMsgbox)requestObj;
msgBox.showDialog();
evt.Set();
}
Class MyMsgBox:Form
{
public MyMsgBox(string message, string title)
{
//do initialization....
}
}
I have tried to set the TopMost of Form to 'true', then it will be always on the top of all applications. it's not meet the requirement as the pops-up dialog need be only always on the top of browser. Thanks.
I don't think that what you want will be possible.
However, you can make a div stretched across all page and set and event on mouse move to call BringToFront on your ActiveX object. That should do the trick.

App Bar Window pops away from docking postion, then moves into the docking position

I have recently added a window to my WPF application which can be docked to an edge of the desktop as an "app bar". The code I'm using to do the docking came from this stackoverflow post.
The program has three user settings defined related to this window. One is the edge where the window is docked, the other two are the values of the Left & Top properties. The idea is that when the window is closed, or the program is shut down, the window will open back in the same state and location when the program restarts.
The problem I'm having is that when the program opens up, the window is first displayed at a random location on the screen (probably the coordinates assigned to it by Windows when the window is created) and then it moves into the docked position. Other programs I've seen that have the app bar functionality, like Trillian, are drawn in the docked position from the beginning. It's a little disconcerting to see the window move like that.
Here is some code from the window:
private void AppBarWindow_Activated( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellActivated( this );
}
}
private void AppBarWindow_Closing( object sender, CancelEventArgs e ) {
Settings.Default.AppBarWindowLeft = Left;
Settings.Default.AppBarWindowTop = Top;
Settings.Default.Save();
AppBarFunctions.SetAppBar( this, ABEdge.None );
// Other, app specific code . . .
}
private void AppBarWindow_LocationChanged( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellWindowPosChanged( this );
}
}
private void AppBarWindow_SourceInitialized( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
SizeWindow( Settings.Default.AppBarWindowEdge == ABEdge.None ? ABEdge.Left : ABEdge.None );
}
}
private void AppBarWindow_SizeChanged( object sender, SizeChangedEventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellWindowPosChanged( this );
}
}
private void SizeWindow( ABEdge originalEdge ) {
// App specific code to compute the window's size . . .
if ( originalEdge != Settings.Default.AppBarWindowEdge ) {
AppBarFunctions.SetAppBar( this, Settings.Default.AppBarWindowEdge );
}
Settings.Default.AppBarWindowLeft = Left;
Settings.Default.AppBarWindowTop = Top;
Settings.Default.Save();
}
I have added functions to call SHAppBarrMessage when the window is activated, or when its position and size change, as I read in this acrticle. The calls don't seem to have any effect on the behavior, so I might remove them.
I know that the SourceInitialized and Loading events are called before the window is displayed but after the window handle and the layout & measure passes have been completed. It appears, though, that the window is rendered before the call to AppBarFunctions.SetAppBar is made, which is why I see it appear and then move into place.
I've also tried to move the window into the docked position by setting the Left and Top properties to the values saved in the settings in the window's constructor. That didn't work, either. In fact, it was worse, as the window was first drawn in the docked position, then apparently was moved away from that desktop edge to make room for it, and then moved back into the docked location.
How do I get this window to appear in the docked position upon start up and not move afterward?
Edit:
I think I have found the cause of the problem. There is a comment in the AppBarFunctions class code, in the ABSetPos method, just before it schedules a call to the DoResize method on the window's Dispatcher (UI thread). The comment reads:
// This is done async, because WPF will send a resize after a new appbar is added.
// if we size right away, WPFs resize comes last and overrides us.
So apparently WPF or Windows is moving the window out of the space being reserved for the window, and I then move it back in. I added a lot of trace points in my code & I can see that the window isn't rendered until after that move is made (the one mentioned in the comment in the code). After the window is rendered, it is moved into the docked position by my code.
The AppBarFunctions class already adds a window procedure hook for wathcing for messages from the shell. If I add a check for WM_WINDOWPOSCHANGED, could I somehow stop the message from being processed? Or maybe I can change the values for the Left and Top properties for the move done by Windows / WPF so the window ends up where I want it to be?
I've found a way to keep the window from moving from the docked area. Basically, the code I'm using already uses a Window Procedure Hook method to watch for ABN_* notification messages. I added code in this method to watch for WM_WINDOWPOSCHANGING messages.
Here's the code I wrote:
public IntPtr WindowProcedureHook( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled ) {
if ( msg == (int) WinMessages.WM_WINDOWPOSCHANGING ) {
if ( IsDocked && !IsDragging ) {
WindowPos pos = (WindowPos) Marshal.PtrToStructure( lParam, typeof( WindowPos ) );
// Keep this window in its docked position.
pos.x = (int) DockedPosition.X;
pos.y = (int) DockedPosition.Y;
pos.cx = (int) DockedSize.Width;
pos.cy = (int) DockedSize.Height;
Marshal.StructureToPtr( pos, lParam, false );
handled = true;
}
} else if ( msg == CallbackId ) {
if ( wParam.ToInt32() == (int) ABNotify.ABN_WINDOWPOSCHANGED ) {
SetDockedPosition( Window, this, true );
handled = true;
}
}
return IntPtr.Zero;
}
When the window is docked to an edge & registered with the shell, it remembers the docking rectangle returned from the call to SHAppBarMessage / ABM_SETPOS. When the method receives a WM_WINDOWPOSCHANGED message, it checks to see if the window is docked along an edge and is not being dragged. If it is, it marshals the WINDOWPOS structure from unmanaged memory into a managed object, sets the position & size of the window back to the docked position & size, and marshals it back to unmanaged memory. It then sets handled to true & exits.
This works perfectly & keeps the window from bouncing away from its docked position & back in. And there is no need to schedule a move into the docked position on the window's Dispatcher thread.

Categories

Resources