Event that triggers BEFORE my form loses focus - c#

I need to be alerted before my entire form loses focus. The Deactivate event only triggers after it loses focus. LostFocus and Leave are only for controls.
I have also tried overriding WndProc but this only triggers after the message has been processed.
overriding PreProcessMessage only can be used for keyboard stuff, not form deactivation.

Dodgy Method
Even though this is a quick and hacky way of doing it, changing Input Language is unnatural to start with..
private void Form1_Deactivate(object sender, EventArgs e)
{
((Form)sender).Activate();
System.Diagnostics.Debug.WriteLine(this.ActiveControl.Name);
//Change Input Language here..
//Alt TAB to set focus to the application selected 5 milliseconds ago
SendKeys.SendWait("%{TAB");
}
Correct and orthadox method
How to monitor focus changes? and C#: Detecting which application has focus
Its using the Automation framework, Add references to UIAutomationClient and UIAutomationTypes and use Automation.AddAutomationFocusChangedEventHandler, e.g.:
public class FocusMonitor
{
public FocusMonitor()
{
AutomationFocusChangedEventHandler focusHandler = OnFocusChanged;
Automation.AddAutomationFocusChangedEventHandler(focusHandler);
}
private void OnFocusChanged(object sender, AutomationFocusChangedEventArgs e)
{
AutomationElement focusedElement = sender as AutomationElement;
if (focusedElement != null)
{
int processId = focusedElement.Current.ProcessId;
using (Process process = Process.GetProcessById(processId))
{
Debug.WriteLine(process.ProcessName);
}
}
}
}

Got it, this hack works perfectly.
private void MyForm_Deactivate(object sender, EventArgs e)
{
Thread.Sleep(200); //delay to allow external tab time to open
Form f1 = new Form(); //create a new form that will take focus, switch input, then terminate itself
f1.Shown += new EventHandler((s, e1) => { f1.Activate(); InputLanguage.CurrentInputLanguage = InputLanguage.DefaultInputLanguage; f1.Close(); });
f1.Show();
}
EDIT: upon further testing I have found this to be equally unreliable. It doesn't seem like there is a good way to do this at all.
For now I am tracking the mouse and keyboard to detect when the user is about to deactivate it. Obviously a mouse and keyboard hook is a horrible solution but its the only reliable solution so far.

Related

Triggering onbeforeunload in WebView2

This answer shows how to trigger the onbeforeunload event in a WebBrowser control in the following way:
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (!this.formsWebBrowser.IsDisposed)
{
//// Generate SHDocVw.dll: Visual Studio Developer Command Prompt "tlbimp.exe ieframe.dll /out: C:\temp\SHDocVw.dll",
var activeX = this.formsWebBrowser.ActiveXInstance;
var input = Type.Missing;
object leavePage = true;
((SHDocVw.WebBrowser)activeX).ExecWB(
SHDocVw.OLECMDID.OLECMDID_ONUNLOAD,
SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT,
ref input,
ref leavePage);
if (!(bool)leavePage)
{
e.Cancel = true;
return;
}
}
base.OnFormClosing(e);
}
But now trying to move away from IE11 (as used by WebBrowser) to Edge Chromium with the WebView2 control, I can't figure out how to do the same thing in WebView2.
The dialog shows correctly when navigating the WebView2 to another page.
The problem comes when the user closes the application or the window containing the WebView2.
It then just closes without showing any dialog.
That's what the code above does for the WebBrowser control, when closing the application the (on)beforeonload event is triggered in the IE11 browser and a bool is returned. True if the user pressed "Leave" or there isn't an beforeonload event active and false if the user pressed "Stay on the page".
Short of calling ExecuteScriptAsync("onbeforeunload();") (which doesn't work when setting the event with window.addEventListener("beforeunload", function(event) { ... });) how can the same be done in WebView2?
Edit:
The problem is that I don't want to show the dialog always when closing, unless I really have to.
I only want to show it if the page has unsaved changes (and it communicates that in the beforeunload event in JavaScript).
The only way I know how handle that in the C#-code is by triggering the built in onunload event showing the beforeunload dialog in the browser.
That is exactly what the ActiveXInstance.ExecWB(OLECMDID_ONUNLOAD) does for the WebBrowser control and IE11.
It may simply not be possible to trigger that event in WebView2/Chromium in the same way? That's really what I'm asking.
I've tried calling JavaScripts in the FormClosing event, but the application just closes w/o waiting for the response.
I guess the only other option is to remove the x-close button and use a custom close button that can do the needed checks and then close the application.
This is working for me. You can combine it with a form closing or something. Don't forget to properly detach events / dispose where proper. This is just a sample.
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
DoWork();
}
private async Task DoWork()
{
await webView21.EnsureCoreWebView2Async();
webView21.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
webView21.CoreWebView2.ScriptDialogOpening += CoreWebView2_ScriptDialogOpening;
await webView21.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('beforeunload', function(e) { return event.returnValue = 'prompt';});");
webView21.Source = new Uri("https://www.google.com");
}
private void CoreWebView2_ScriptDialogOpening(object sender, Microsoft.Web.WebView2.Core.CoreWebView2ScriptDialogOpeningEventArgs e)
{
if (MessageBox.Show("do you want to leave", "Leave?", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
e.Accept();
}
else
{
e.GetDeferral();
}
}

Using the Keyboard.IsKeyDown in a loop C# form app

I am trying to make a platformer game in windows c# forms, in my main game loop I've got some pieces of code but I can't seem to get the user input to work correctly, any help would be appreciated!
this is my code:
while (true)// this is still in testing so it should go on forver
if (Keyboard.IsKeyDown(Key.Insert) == true)
{
btn1.Left = btn1.Left + 1;// btn is a button
Update();
}
System.Threading.Thread.Sleep(50);
}
whenever I run this the program becomes unresponding and eventually crashes
and when I press insert or any other key I used it does not work
Assuming this code is running in a Form you should subscribe to the Form's KeyDown event:
public partial class YourForm : Form
{
public YourForm()
{
InitializeComponent();
KeyDown += KeyDownHandler; // subscribe to event
KeyPreview = true; // set to true so key events of child controls are caught too
}
private void KeyDownHandler(object sender, KeyEventArgs e)
{
if (e.KeyCode != Keys.Insert) return;
btn1.Left = btn1.Left + 1;// btn is a button
e.Handled = true; // indicate that the key was handled by you
//Update(); // this is not necessary, after this method is finished, the UI will be updated
}
}
So the KeyDownHandler gets called if the user presses the key. There is no need to pull the keyboard state in a loop that blocks your UI thread.
The subscription to the event and the KeyPreview value can be set in the designer window too if you prefer that to writing in your own code.
And btw: the Keyboard class is part of WPF. You should not mix that with Windows Forms.

How do I get characters from KeyEventArgs and not KeyPressEventArgs?

I know this has been asked hundred of times, but I haven't been able to find a solution that helps me. I'm using a barcode scanner and I want to be able to get the keys that are typed using just the keydown events. For some reason, I can't use both keydown and keypress events (my keypress events won't run).
I need to be able to get the characters, including hyphens, uppercase letters and dots and also need to detect the enter key.
These are my listeners:
form.KeyDown += new KeyEventHandler(Input_KeyDown);
form.KeyPress += new KeyPressEventHandler(Input_KeyPress);
And these are my methods:
private void TimedOut(object sender, EventArgs e)
{
_barcode = "";
}
private void Input_KeyDown(object sender, KeyEventArgs e)
{
_timer.Stop();
_timer.Start();
if (e.KeyData == Keys.Enter)
{
if (!_barcode.Equals(""))
{
this.BarcodeScanned(_barcode, new EventArgs());
}
}
else
{
}
}
private void Input_KeyPress(object sender, KeyPressEventArgs e)
{
_timer.Stop();
_timer.Start();
_barcode += e.KeyChar;
}
Your code above works...on a blank form. However there are several things that can interfere with the key events, especially when there are other controls on the page. Make sure that
The AcceptButton property isn't set on the form (this will trap the Enter key)
That there are no controls on the form with TabStop set to true (might not be viable but give it a go)
That the form has focus when you're typing (unlikely given the description but check anyway)
That focus is not otherwise on any control in the form when typing, e.g., a TextBox
That no other controls are trying to process the KeyPress or KeyDown events and that no other custom events are configured/set anywhere else in your code
One thing I notice is that you are registering the events like so;
form.KeyDown += new KeyEventHandler(Input_KeyDown);
This implies that you are instantiating this form from another place and trying to get it to send its key events to the calling code. Are you sure that the form instance is persisted/saved to a private class level variable or some such?

Hiding form when are other controls focus

This question is related to this my question. Now I have form in my class and when user click on button I show (or hide) form. That´s ok. But I want to hide form when I move with origin form or when I click somewhere in origin form. The new form is behind that origin form. I was trying events like lostfocus and others but It didn´t help. So I think I need some trick that check from my control if there was click in parrent form (origin form) or some other hack. I know the best would be that I put code but I have many lines so I think that best way will be if you help me in general way and then I try to applicate to my app.
You can do it with a global mouse and keyboard hook. In fact, its been wrapped up into well documented, well structured .NET API over at CodePlex
Go over there and download it. Then, set up a global mouse hook:
_mouseListener = new MouseHookListener(new GlobalHooker());
_mouseListener.MouseMove += HandleGlobalHookMouseMove;
_mouseListener.Start();
The key here is that you will receive the MouseMove event ANY time the mouse moves ANYWHERE on the desktop, not just within the bounds of your window.
private void HandleAppHookMouseMove(object sender, MouseEventArgs e)
{
if (this.Bounds.Contains(e.Location))
{
HandleEnter();
}
else
{
HandleLeave();
}
}
You can also setup one for MouseClick. The combination of the two will enable you to determine any time the mouse moves over your origin form, or the mouse is clicked when its over it. Unlike the LostFocus and other events you tried, focus is irrelevant.
Does below help?
public partial class Form1 : Form
{
Form f2 = new Form2();
public Form1()
{
InitializeComponent();
f2.Show();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (this.ClientRectangle.Contains(e.Location) && f2.Visible) { f2.Hide(); }
}
private void button1_Click(object sender, EventArgs e)
{
f2.Visible = !f2.Visible ? true : false;
}
}

Winforms: Keyboard shortcuts for Winforms app hosting other applications in its panels

I have a Winforms application, which hosts two PDF Viewers (Unmanaged C++) in its panels.
I want to have application wide hotkeys for project handling (Open,Save,Close,...) which would work even when users focus is on the hosted PDF Viewer.
I managed to achieve that via Winapi and RegisterHotKey, but client doesnt like that those are system wide (e.g. they disable the same MS Word hotkeys)
I did try to disable and enable global hotkeys on Form's Activate/Deactivate events, however those events appear even when i dont leave my own app, for example on top-menu entry or on a dialog-box.
Are there other solutions for application-wide hotkeys, that would work even if the focus is on the hosted application?
SOLUTION:
In the main Form, I handle Activated and Deactivate events like this :
public void PDFPicker_Activated(object sender, EventArgs e)
{
var foregroundHwnd = GUI.winapiwrap.GetForegroundWindow();
if (foregroundHwnd==this.Handle || this.OwnedForms.Any(form => form.Handle==foregroundHwnd))
{
if (!this.hotkeys.Any())
{
registerHotkeys();
this.Text = "Hotkeys just registered";
}
}
}
public void PDFPicker_Deactivate(object sender, EventArgs e)
{
var foregroundHwnd = GUI.winapiwrap.GetForegroundWindow();
if (foregroundHwnd == this.Handle || this.OwnedForms.Any(form => form.Handle == foregroundHwnd))
{
}
else
{
unregisterHotkeys();
this.Text = "Hotkeys off";
}
}
Then in the dialog, which is in the OwnedForms array, i call just this:
private void QuestionPostingPanel_Activated(object sender, EventArgs e)
{
parent.PDFPicker_Activated(sender, e);
}
private void QuestionPostingPanel_Deactivate(object sender, EventArgs e)
{
parent.PDFPicker_Deactivate(sender, e);
}
In case of more such dialogs, I would inherit AbstractDialog : Form, add those 2 event functions and a reference to parent, and then inherit all of them from this AbstractDialog.
While registering/unregistering GlobalHotkeys, it was a problem for me when I tried doing that in parallel - global hot keys were bound to a thread.
So then check on Form Activate/Deactivate events whether the form is a foreground window or not (::GetForegroundWindow()) and then disable/enable hotkeys appropriately.

Categories