Notifyicon.BalloonTip not showing properly - c#

I have a notifyIcon that I want to use in WinForms app, to show balloonTips to the user on certain events / issues.
It worked fine, and then I made changes to the application that I cannot see could have influenced the workings of the BalloonTip, but now the balloonTip does not pop up.
The NotifyIcon.Icon appears in the task bar and if you keep looking at the taskbar you briefly see the icon in there (like it flashes), but the actual BalloonTipText and title don't appear
here is the code I am using.
private void SetBalloonTip(String BalloonMessage, bool isError)
{
AppTips.Icon = SystemIcons.Exclamation;
AppTips.BalloonTipTitle = "Attention";
AppTips.BalloonTipText = BalloonMessage;
AppTips.Visible = true;
if (isError)
{
AppTips.BalloonTipIcon = ToolTipIcon.Error;
}
else
{
AppTips.BalloonTipIcon = ToolTipIcon.Info;
}
AppTips.ShowBalloonTip(300000);
}
whenever I want to show a BalloonTip Message I call the above method.
I have played around with the timeout as I read on other threads it could be a possible issue, but increasing nor decreasing it made any difference.
I also added Thread.Sleep before calling the SetBalloonTip method, thinking that maybe the method is called too quickly in succession, but that did not make a difference either.

There are limits on the length of BalloonTipText and BalloonTipTitle on some versions of Windows (but not on others, Windows 10 supports any length for example):
NotifyIcon.BalloonTipText max length of 255 characters
NotifyIcon.BalloonTipTitle max length of 63 characters
Make sure your text does not violate these limitations to have it display properly.

Related

Using TestStack.White to test a controls which fire off background work

I am trying to write UI test case for a WPF application. This consists of a search textbox. On providing input into the textbox, search for the input string is done in a background thread.
This is my basic code:
public void TestMethod1()
{
var applicationDirectory = #"C:\projects\dev\source\bin\Debug";
var applicationPath = Path.Combine(applicationDirectory, "Some.exe");
Application application = Application.Launch(applicationPath);
Window mainWindow = application.GetWindow("Window Title");
mainWindow.Get<TextBox>().Text = "testing things out";
Assert.IsTrue(true);
mainWindow.Dispose();
application.Dispose();
}
Now on the line where I set Text property, the background work will start, and the framework throws the exception:
TestStack.White.UIItems.UIActionException : Window didn't respond,
after waiting for 50000 ms
Basically the framework waits for a configured amount of time and then throws the exception as the background work did not finish in time.
I have checked the documentation and it mentions a workaround, but that would involve me changing my application code. Since this is a legacy application I do not want to change the code (we are in middle of migration, so want to keep code changes limited).
This seems to be a common issue, but have not been able to see any solution? Any ideas?
UPDATE
The following code does work, though still not close to finding the solution to original issue.
mainWindow.Get<TextBox>().BulkText = "testing things out";

Why would the hourglass (WaitCursor) stop spinning?

I've got code in a button click like so:
try
{
Cursor = Cursors.WaitCursor;
GenerateReports();
}
finally
{
Cursor = Cursors.Default;
GC.Collect();
GenPacketBtn.Enabled = true;
}
Nowhere else but in the finally block is the cursor set back to default, yet it does "get tired" and revert to its default state for some reason. Why would this be, and how can I assure that it will not stop "waiting" until the big daddy of all the processes (GenerateReports()) has completed?
Use instead Control.UseWaitCursor = true, this does not time out.
If an expensive operation is being executed then Windows will take over and it will change the Cursor.WaitCursor to whatever it deems necessary. So with Cursor.WaitCursor it will either due to a timeout (but not fully sure about this) or because of Windows simply claiming ownership of the cursor without regards to its previous state. We also had a similar situation with where the Cursor did not behave as expected when performing an expensive task that involved called 3rd party PDF converters but we did not investigate more on the nature of the issue as it was not a priority.
After a bit of reading, it turned out setting the Hourglass cursor is a bit more complicated than it seems:
.net WaitCursor: how hard can it be to show an hourglass?
Also as a side note: you should use Cursor.Current = Cursors.WaitCursor as this forces the cursor to change to busy immediately , more details at : https://stackoverflow.com/a/302865/1463733

Fastest way to send keystrokes C#

I was wondering what the fastest way to send keystrokes using C# is. Currently I am using SendKeys.Send() and SendKeys.SendWait() with SendKeys.Flush().
I am using the following code to calculate the time of how long it takes for the both of them to work:
Stopwatch sw1 = new Stopwatch();
sw1.Start();
for (int a = 1; a <= 1000; a++)
{
SendKeys.Send("a");
SendKeys.Send("{ENTER}");
}
sw1.Stop();
And:
Stopwatch sw2 = new Stopwatch();
sw2.Start();
for (int b = 1; b <= 1000; b++)
{
SendKeys.SendWait("b");
SendKeys.SendWait("{ENTER}");
SendKeys.Flush();
}
sw2.Stop();
The results of the 2 are:
Result 1: 40119 milliseconds
Result 2: 41882 milliseconds
Now if we put the SendKeys.Flush() on the second test, outside of the loop we get:
Result 3: 46278 milliseconds
I was wondering why these changes in the code make the speed very different.
I was also wondering if there is a faster way of sending many keystrokes, as my application does it a lot. (These tests were done on a really slow netbook)
Thanks!
SendWait() is slower because it waits that the message has been processed by the target application. The Send() function instead doesn't wait and returns as soon as possible. If the application is somehow busy the difference can be even much more evident.
If you call Flush() you'll stop your application to process all events related to the keyboard that are queued in the message queue. It doesn't make too much sense if you sent them using SendWait() and you'll slow down a lot the application because it's inside the loop (imagine Flush() as a selective DoEvents() - yes with all its drawbacks - and it's called by SendWait() itself too).
If you're interested about its performance (but they'll always be limited to the speed at which your application can process the messages) please read this on MSDN. In sum, you can change the SendKeys class to use the SendInput function, rather than a journal hook. As quick reference, simply add this setting to your app.config file:
<appSettings>
<add key="SendKeys" value="SendInput"/>
</appSettings>
Anyway, the goal of the new implementation isn't the speed but consistent behavior across different versions of Windows and and options (the increased performance is kind of a side effect, I guess).
If you have a lot of text to push to a client, you may notice that SendKeys is really sluggish. You can vastly speed things up by using the clipboard. The idea is to put the text you wish to "type" into a target text box in the clipboard and then send a CTRL-V to the target application to paste that text. Here's an illustration:
Clipboard.Clear(); // Always clear the clipboard first
Clipboard.SetText(TextToSend);
SendKeys.SendWait("^v"); // Paste
I found this worked well for me with a cordless bar code scanner that talks via WiFi to a host app that sends long bar codes to a web app running in Google Chrome. It went from tediously pecking out 30 digits over about 4 seconds to instantly pasting all in under a second.
One obvious downside is that this can mess with your user's use of the clipboard. Another is that it doesn't help if you intend to send control codes like TAB or F5 instead of just plain old text.

How can I update a line in a C# Windows Console App while without disrupting user input?

When building a Windows Console App in C#, is it possible to update lines in the console while the user is entering text?
My current code is:
public static void Scope()
{
bool stop = false;
ASCOM.Utilities.Chooser chooser = new ASCOM.Utilities.Chooser {DeviceType = "Telescope"};
ASCOM.Interface.ITelescope scope = new ASCOM.DriverAccess.Telescope(chooser.Choose());
scope.Connected = true;
ThreadPool.QueueUserWorkItem(
cb =>
{
do
{
Console.WriteLine("RA: " + scope.RightAscension);
Console.WriteLine("Dec: " + scope.Declination);
Console.WriteLine("Status: " + scope.Slewing);
Console.SetCursorPosition(0, Console.CursorTop - 3);
Thread.Sleep(1000);
} while (!stop);
}
);
Console.Read();
stop = true;
}
I would recommend changing this from a Console application to a Windows application, and putting the reporting into a standard GUI.
This will let you have user input areas of your screen separate from your reporting, and provide a much nicer, more usable interface overall.
That being said, you could potentially position the cursor explicitly, and avoid "interrupting" the user (for longer than your refresh method call). This would require a lot of effort, though.
The keys would be to read the console input key by key (instead of using ReadLine), as you'd need to be able to "retype" the user's previous input.
When it's time to refresh the display, you could get the current cursor position and content for the current line, clear the console, write your new text, write in any text that would have been displaying already (the user's current input), and use Console.SetCursorPosition to explicitly set the cursor location.
If you're going to do that, it might be nice to, up front, set the cursor position to the last line of the console (in effect making a console-mode gui). This would allow you to at least give the effect of an old-school console application, and make this less surprising to the user.

Process.CloseMainWindow() not working

I start the Windows On-Screen-Keyboard like that:
s_onScreenKeyboard = new Process();
s_onScreenKeyboard.StartInfo = new ProcessStartInfo("osk.exe");
s_onScreenKeyboard.EnableRaisingEvents = true;
s_onScreenKeyboard.Exited += new EventHandler(s_onScreenKeyboard_Exited);
s_onScreenKeyboard.Start();
This works fine, but when I try to stop it using the following code, it does not work, i.e. the OSK keeps running and the method returns false:
s_onScreenKeyboard.CloseMainWindow();
if (!s_onScreenKeyboard.HasExited)
{
if (!s_onScreenKeyboard.WaitForExit(1000))
{
s_onScreenKeyboard.Close();
//s_onScreenKeyboard.Kill();
}
}
When uncommenting s_onScreenKeyboard.Kill(); it is closed, but the problem is that osk.exe obviously uses another process called "msswchx.exe" which is not closed if I simply kill the OSK process. This way, I would end up with hundreds of these processes which is not what I want.
Another strange thing is that the CloseMainWindow() call worked at some time, but then it suddenly did not work anymore, and I do not remember what has changed.
Any ideas?
EDIT: I have found a solution myself. Please see my answer for details.
Background:
I am implementing an On-Screen-Keyboard for my application because it should work with a touchscreen. It is important that the keyboard layout matches the layout which is configured in Windows since the application will be shipped to many different countries. Therefore, instead of implementing a custom keyboard control with approx. 537 keyboard layouts (exaggerating a little here...), I wanted to utilize the Windows built-in On-Screen-Keyboard which adapts to the selected keyboard layout automatically, saving a lot of work for me.
I have found the/a solution myself:
When I successfully retrieve the MainWindowHandle after the process has been started, the call to CloseMainWindow() is also successful later on. I do not understand the reason for this, but the important thing is: it works!
BTW, for others having the same problem: The MainWindowHandle is not available immediately after starting the process. Obviously, it takes some milliseconds until the MainWindow is started which is why I use the following code to retrieve the handle:
DateTime start = DateTime.Now;
IntPtr handle = IntPtr.Zero;
while (handle == IntPtr.Zero && DateTime.Now - start <= TimeSpan.FromSeconds(2))
{
try
{
// sleep a while to allow the MainWindow to open...
System.Threading.Thread.Sleep(50);
handle = s_onScreenKeyboard.MainWindowHandle;
}
catch (Exception) { }
}
In this code I continuously get the MainWindowHandle every ~50ms as long as it is still equal to IntPtr.Zero. If the handle could not be retrieved after 2 seconds, I quit the loop to avoid an endless loop in case of error.
You need to wait untill the process finishes initialization, run
Process.WaitForInputIdle Method in order to do that.

Categories