NotifyIcon Events not firing - c#

I'm new here and have a really mysterious problem to start off. I'm a software developer in the UK and have over 15 years of experience but have only been developing in .Net for 18 months. My NotifyIcon mouse events are not firing!
I am using C# to write an application that starts as a NotifyIcon ('main app icon') and displays a ContextMenu on mouse right-click. This works fine: ContextMenu, forms launching and mouse click events firing.
A tiny bit of background: the application is supposed to sense the insertion of a usb 'device' (it does), interrogate it and create another NotifyIcon ('device icon') to allow the user to interact with that device. The 'main app icon', mentioned in my previous paragraph, allows the user to interact with the database and to configure the software.
To encapsulate these device interaction functions, I have constructed a 'device class' that
contains the device NotifyIcon, ContextMenu, forms, mouse click events etc which allow interaction with the device.
The Problem
The problem starts when I instantiate my 'device class' from the ManagementEventWatcher EventArrived event. If I instantiate the 'device class' in my program Main then the events fire correctly when I click the notifyicon.
So, please, can someone help me?
Cheers, in advance
Matthew

IIRC, the event usage (rather than WaitForNextEvent) works async. I would be interested to know what thread the event is being raised on. I wonder if there is no message pump servicing messages for your icon.
Do you have a form anywhere? Or something else with a message loop? I would be tempted to call into the form (using Control.Invoke), and ask the form to show the icon - since then it should have an active message pump.
Sorry for the delay; reading your comments, it sounds like you've broadly got a workaround. The only gotcha is cross-threaded issues; ideally, you would ask the UI to make such a change on the UI thread; for example, if you have a Form kicking around (owning the icon) - add to your form class:
// not a property, as there is no need to add a complex x-thread "get"
public void SetIconVisible(bool isVisible) {
if(this.InvokeRequired) {
this.Invoke((MethodInvoker) delegate {
myIcon.Visible = isVisible;
});
} else {
myIcon.Visible = isVisible;
}
}
This does a thread-switch (if needed) to the UI thread. Any use?

So the answer is:
The events will only work if when you make the NotifyIcon visible, you do it in the main thread. So the code given by Marc Gravell is the solution.

Mark, just to let you know -
I worked out that I could create the class instances that have the NotifyIcon as members in the main thread and then make the NotifyIcon(s) visible when the USB device(s) are connected.
It needed a bit of adjustment though because the NotifyIcon is created when it's first made visible, so I had to make sure that (in the main thread) I set Visible to true then to false for each - giving rise to the need for limiting the number of instances.
The ManagementEventWatcher thread could then set the Visible property to true when the device gets connected.
A workaround.
(see replies to your comments)

Related

How to debug why a form can not obtain focus?

Using Visual Studio 2019, with an old C# Winforms .NET 4.6.2 application: I have a situation where my main form is acting as if it is being blocked by a modal:
Makes a beeping sound and flashes when clicking anywhere
Will not accept focus
Timer and serial communication Events are still triggering
No grey screen overlay, or message about the application ever becoming unresponsive
However, I can see no modal form, and using Application.OpenForms doesn't show any of the modals that I have created and have closed a few seconds previously.
I can successfully attach and debug remotely, that is how I know the timer ticks are still firing.
How / Where can I place a breakpoint to troubleshoot why a form will not receive focus / denies click events / thinks it is being blocked by a modal of some kind?
It is too difficult to create a minimal example because the backgroundWorker and showDialog code all seems to work properly in other locations, but the gist of what is happening looks like:
Application.Run (new FormA)
...
FormA.showDialog(new FormB)
...
FormB.showDialog(new FormC)
...
FormC.timer_tick() { ...still running... }
FormC.onReceive_data() { ...still running... }
FormC.button_click(){
...
showDialog()
...
showDialog()
...
important.showDialog() { backgroundWorker...database stuff }
...
showDialog()
...
}
(known completion : back to FormC event-loop)
It always gets to "known completion" just fine, important.showDialog() seems to work fine, pops up an animating dialog with no buttons, auto closes when backgroundWorker completes, closes itself and moves on to the next dialogs or exits button click fine.
But once back in FormC-Event-loop, the form will not receive any input, as if it is still blocked by some modal somewhere...
If I change to important.show() FormC will not have a problem, but now there is code in the click event running before database activity is finished...
I am more interested in debugging technique in this case as to how I can figure out what windows is doing as I try to click on FormC. What kind of debugging module symbols need to be loaded? Where can I breakpoint to see windows deciding whether a form can receive focus/input?
"something has gone wonky with the re-enabling mechanism" made me go through with a fine tooth comb and check how the dialog is being closed off, and I found the original problem:
FormC starts a backgroundWorker and then waits on important.showDialog()
Inside the RunWorkerCompleted() or a timer_tick : "important" was FormC.BeginInvoke() .closed() and .disposed() . But apparently the .dispose() can (but not always) happen on the wrong thread or at the wrong time and then the parent form can never be enabled again. .close() and nulling the formDialog reference allows the parent to get re-Enabled.
Out of interest, I am still wondering how you would go about debugging such a situation?

MessageBox modal to a single form

I want to display a MessageBox that is modal to just a single form, and allows other top level windows on the thread to remain active. WinForms disables all windows on the current thread, whether displaying a custom modal form or using the MessageBox class.
I've tried a number of different approaches to solving the problem, including re-enabling any other top level windows (using the Win32 EnableWindow function) and p/invoking the native MessageBox function. Both of these approaches work, but leave keyboard shortcuts inoperable, including tab to move between controls, escape to cancel an open menu and most importantly menu shortcut keys.
I can make menu shortcut keys work by installing a keyboard hook when I display the MessageBox and then directly calling ProcessCmdKey on the active form, but other keyboard shortcuts still don't work. I guess I could press on and add more hacks to make these other cases work, but I was wondering if there was a better way.
Note, this is not about being non-blocking, but about not disabling all windows on the current thread. It happens that the solution may be similar, however.
The basic problem you are battling here is that MessageBox.Show() pumps its own message loop to make itself modal. This message loop is built into Windows and is thoroughly unaware of what the Winforms message loop looks like. So anything special that Winforms does in its message loop just won't happen when you use MessageBox. Which is keyboard handling: detecting mnemonics, implementing navigation and calling methods like ProcessCmdKey() so that a form can implement its own shortcut keystrokes. Not normally a problem since it is supposed to be modal and ignore user input.
The only practical way to revive this is to display the message box on its own thread. This is formally allowed in the winapi, the owner of a window can be a window owned by another thread. But that's a rule that Microsoft did not implement when they added the code to .NET 2.0 that detects threading mistakes. Working around that code requires an IWin32Window as the message box owner that is not also a Control.
Add a new class to your project and paste this code:
using System;
using System.Threading;
using System.Windows.Forms;
public class NonModalMessageBox : IWin32Window {
public NonModalMessageBox(Form owner, Action<IWin32Window> display) {
this.handle = owner.Handle;
var t = new Thread(new ThreadStart(() => display(this)));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
public IntPtr Handle {
get { return handle; }
}
private IntPtr handle;
}
And use it like this:
new NonModalMessageBox(this, (owner) => {
MessageBox.Show(owner, "Testing", "Non-modal");
});
Where this the Form object that should be disabled while the message box is displayed. There's little I can do to make you feel better about this hack, if the FUD is too overpowering then you really do need to re-implement MessageBox with your own Form class so you can use Show() instead of ShowDialog(). It has been done.
You could pass a reference to the Form, set Enabled property to false when you open the dialog, and set it back to true when the dialog closes.

Force a form to stay on top

I want a form to be shown modal every time it is opend. Since I can not change the way it is created and opend. I wondered if it is possible to make the form stay on top from within the forms class.
One opportunity is the TopMost property. This works in general, but if I display the form while the main thread is waiting for it to close, the form will stay on top even if I change the application(to a browser for example). So no matter where I am, the form is still displayed.
Another issue which I came across is that in some cases it is adopted by the parent form which then might block other windows or popup messages.
I was thinking about a hook to the OnLostFocus event to get it on top again, once the focus is lost, but I'm not sure if that is a good idea ...
Any helpful thoughts about it?
Edit
Due to the comments I will extend my description, Here is the real use-case
We are using the Devexpress's SplashScreenManager which is able to show a certain form as a WaitForm. Since the WaitForm is not intended to be shown modal(see on the Support Center), we are looking for a way to do so.
We can not change the way the form is shown, because this is done through the SplashScreenManager. The WaitForm is shown both from the main thread, as well as from certain backgroundworker.
So this is only about an own form of ourselfs, displaying it within our own application.
Use:
TopLevel = true;
This will do exactly what you want; be topmost as long as the main form is shown and hide if the mainform is hidden by another window.
You can set the owner of your splash form to your main form explicitly without using .Show(owner).
splashForm.Owner=mainForm;
splashManager.Show(splashForm);
We did not want the TopMost property since it works on windows level and covers other windows too (for example the browser).
In the end I hooked up on the focus event of the window to make sure the window is always on top.

TouchDown event is delayed

My application uses the UiElement.TouchDown event in various places, one of them is to let the user stop a spinning wheel. In this situation, one can easily notice a short delay of about 1/3s between the actual touching of the screen and the TouchDown event.
I have set Stylus.IsPressAndHoldEnabled to false.
In order to troubleshoot this problem, I've written a test tool that reports WPF events and native window messages, and I noticed that as soon as I touched the screen, messages with id 0x02CC, 0x011B and 0x011A are generated, about 100 to 300 ms before the TouchDown event. This leads me to believe that the drivers report the touch quickly, and the delay is introduced somewhere later in the WPF translations.
Is there a way to make the touch interaction more responsive? Please ask for any information you need!
Unless Flicks are needed set both in XAML or by manually setting in code behind: (I assume you meant to say IsPressAndHoldEnabled not IsTouchAndHoldEnabled)
Stylus.IsPressAndHoldEnabled="False" Stylus.IsFlicksEnabled="False"
Furthermore consider handling the TouchDown Event and also capturing it to the relevant UIElement (don't forget to release it in TouchUp).
uielement.CaptureTouch(e.TouchDevice);
e.Handled = true;

AxAcroPDF swallowing keys, how to get it to stop?

The AxAcroPDF swallows all key-related events as soon as it gets focus, including shortcuts, key presses, etc. I added a message filter, and it doesn't get any key-related messages either. It's a COM component, could that be relevant?
Is there any way to catch these before the control starts swallowing them?
Hans is correct, the Acrobat Reader spawns two child AcroRd32 processes which you have no direct access to from within your managed code.
I have experimented with this and you have three viable options:
You can create a global system hook, and then look for and filter out / respond to WM_SETFOCUS messages sent to your child AcroRd32 windows. You can accomplish some of this from within C# by using a wrapper library, such as the one here: http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx
You also need to identify the correct processes as there may be more than one instance of your application, or other instances of AcroRd32. This is the most deterministic solution, but because your application will now be filtering messages sent to every single window in existence, I generally don't recommend this approach because then your program could negatively affect system stability.
Find an alternate PDF viewing control. See this answer for a few commercial components: .net PDF Viewer control , or roll your own: http://www.codeproject.com/KB/applications/PDFViewerControl.aspx
Find an acceptable hack. Depending on how robust your application needs to be, code such as the following may be suitable (it was suitable for my case):
DateTime _lastRenav = DateTime.MinValue;
public Form1()
{
InitializeComponent();
listBox1.LostFocus += new EventHandler(listBox1_LostFocus);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
axAcroPDF1.src = "sample.pdf"; //this will cause adobe to take away the focus
_lastRenav = DateTime.Now;
}
void listBox1_LostFocus(object sender, EventArgs e)
{
//restores focus if it were the result of a listbox navigation
if ((DateTime.Now - _lastRenav).TotalSeconds < 1)
listBox1.Focus();
}
I might finally have a ridiculously simple answer. So far in testing this is working.
Having suffered from this problem for quite some time and having built a complex system of each custom control recording which of them last had focus and using a timer to flip focus back (when acropdf grabbed it) I revisited this problem and read a great number of answers (looking for recent solutions). The information gleaned helped me with the idea.
The idea is to disable the (acropdf) control whilst it is loading as in the following example (code reduced for clarity)
AxAcroPDF_this.Enabled = False
AxAcroPDF_this.src = m_src
Then on a timer, after say 1 second.
AxAcroPDF_this.Enabled = True
Basically the idea is to tell Windows not to let users use the acropdf control until allowed, so asking Windows to prevent it from getting focus (because users are not allowed in there).
So far this is holding up, I will edit this if anything changes. If it doesn't work completely for you then maybe the idea points into a useful direction.
It is an out-of-process COM component, that's the problem. Completely in violation of Windows SDK requirements as laid out in SetParent(). Once its window gets the focus, the message loop in the acroread.exe process gets all the messages, your message filter cannot see any messages anymore.
Technically it is fixable by using SetWindowsHookEx() to inject a DLL into the process and monitor messages with WH_GETMESSAGE. But you can't write such a DLL in the C# language.
Major suck, I know. There never seems to be any lack of it with that program.
For some reason Tim's answer, disabling the AxAcroPDF control directly, didn't work in my case. The Leave event on the previously-selected Textbox would never fire, either.
What is working is nesting the AxAcroPDF control inside of a disabled GroupBox. Since the users of my application need to only see the PDF, not interact with it, the GroupBox's Enabled property is set to False in the designer.

Categories

Resources