I'm working on a C#.Net application which has a somewhat annoying bug in it. The main window has a number of tabs, each of which has a grid on it. When switching from one tab to another, or selecting a different row in a grid, it does some background processing, and during this the menu flickers as it's redrawn (File, Help, etc menu items as well as window icon and title).
I tried disabling the redraw on the window while switching tabs/rows (WM_SETREDRAW message) at first. In one case, it works perfectly. In the other, it solves the immediate bug (title/menu flicker), but between disabling the redraw and enabling it again, the window is "transparent" to mouse clicks - there's a small window (<1 sec) in which I can click and it will, say, highlight an icon on my desktop, as if the app wasn't there at all. If I have something else running in the background (Firefox, say) it will actually get focus when clicked (and draw part of the browser, say the address bar.)
Here's code I added.
m = new Message();
m.HWnd = System.Windows.Forms.Application.OpenForms[0].Handle; //top level
m.WParam = (IntPtr)0; //disable redraw
m.LParam = (IntPtr)0; //unused
m.Msg = 11; //wm_setredraw
WndProc(ref m);
<snip> - Application ignores clicks while in this section (in one case)
m = new Message();
m.HWnd = System.Windows.Forms.Application.OpenForms[0].Handle; //top level
m.WParam = (IntPtr)1; //enable
m.LParam = (IntPtr)0; //unused
m.Msg = 11; //wm_setredraw
WndProc(ref m);
System.Windows.Forms.Application.OpenForms[0].Refresh();
Does anyone know if a) there's a way to fix the transparent-application problem here, or b) if I'm doing it wrong in the first place and this should be fixed some other way?
There are calls on classes derived from Control for this purpose. They are SuspendLayout and PerformLayout. As they are on Control and Form is derived from Control, your Form has them too.
These calls suffice for most updates but in other circumstances, just hiding the control using Visible = false can be enough. To stop the flicker during this hiding and then reshowing of the control, I usually draw the control to a bitmap which I show in a PictureBox during the update. This is useful when updating trees, tab controls, or lists (as can turning off sorting during the update in that last example).
The behavior you're describing is not normal for a .NET winforms application. The fact that you're using WndProc and sending messages in your example suggests that there is a lot of other unusual stuff going on with this form (I'm guessing there's more than one thread involved). Another possibility that is common in tabbed interfaces is that your form is simply overloaded with controls; sometimes this can cause strange behavior.
I have never witnessed or heard of anything remotely like what you describe.
You can try override the Paint method on your control that you do not want rendered and control it by some global boolean (=ignore all painting while some bool is true.)
If your control is a 3rd party, subclass it and override it there.
Then when you are satisified, set the bool to false and let the control be painted again (might have to force a paint when you turn it on again with .Refresh?)
If this is a custom control, you can try some of the control style flags: I think DoubleBuffered or AllPaintingInWmPaint might help. You can change the style bits using Control.SetStyle (which is protected, which is why you need to do it in your own custom Control class).
Related
A Quick Note
I've tried to be as thorough as possible with my question but you may still need additional clarification; if this happens to be the case, feel free to comment your concerns and I will update the post to answer them as best as I can.
I came across a rather odd issue yesterday when toggling controls on my form. I have a period of loading on FormShown and all controls but the loading display should be hidden. The toggle works to turn visibility off for everything but the loading display, but when loading completes only some of the controls are visible.
I stepped through the code that turns everything back to visible and ensured that everything is indeed being set to Visible = true. I think it may have something to do with the Dock property on the controls, or maybe the TabIndex or something similar but I'm having trouble tracking the underlying issue down.
This is the code I use to toggle the control visibility:
private void ToggleAllControlVisibility() {
foreach (Control c in Controls)
ToggleControlVisibility(c);
}
private void ToggleControlVisibility(Control c) {
if (c.Name == "loadingContainer")
return;
if (!(c is SplitContainer || c is SplitterPanel))
c.Visible = !c.Visible;
foreach (Control child in c.Controls)
ToggleControlVisibility(child);
}
It is a recursive toggle in which all child controls are also toggled. The ToggleAllControlVisibility method is called before loading begins, and again after loading completes.
A More Detailed Look
Now that you know the problem, there are certain controls in particular that I know do not show (at least the way they should). Take the following control tree for example:
pTimePanel (Panel)
timeSlider (TrackBar)
lblStartTime (Label)
lblStopTime (Label)
Out of the controls above, only the pTimePanel actually displays in the foreground. I believe its child controls may be displaying behind it somehow because as I step through the code and it reaches the lblStartTime control, I can briefly see the outline of the label (no content) and then when it moves to the next control it's gone. The timeSlider control doesn't seem to show the same behavior but it does get back to Visible = true.
The full tree from form to the above controls (with docking properties) is as below:
splitContainer : Dock-Fill
panel1 (SplitterPanel)
loadingPanel (Panel) : Dock-Fill
pTimePanel (Panel) : Dock-Bottom
pTimeLabels (Panel) : Dock-Bottom
lblStartTime (Label) : Dock-Left
lblStopTime (Label) : Dock-Right
timeSlider (TrackBar) : Dock-Fill
Notes
Some of these may be less helpful than others, but as I think of things that may help clarify what's going on, I'll add them here.
When visibility is toggled in either direction, the Resize event on the loadingPanel is raised.
Question
What could possibly be occurring to make this odd behavior possible?
It turns out that this was related to the order in which the controls were added to the form. Due to some copying and pasting controls a while back things ended up in an improper order. I had to rebuild the form from scratch to get any toggling to actually work. Once I rebuilt the form, all of the available options for toggling began working the way I expected them to.
I'm trying to put an icon in the system tray and then give it a global keyboard shortcut to carry out a function.
I'm using RegisterHotKey to set the global keyboard shortcut, and it works if the main form associated with the icon is visible. But if the form is invisible then the WndProc method is never invoked.
Any ideas?
Edit:
What I mean by "hidden" is that the following is added to the main form:
protected override void OnLoad(EventArgs e)
{
hotKey = new GlobalHotkey(GlobalHotkey.WIN, Keys.T, this);
bool registered = hotKey.Register();
Visible = false;
ShowInTaskbar = false;
base.OnLoad(e);
}
"registered" is showing as "true", and the shortcut key works fine if I leave out the "Visible = false;" and the "ShowInTaskbar = false;".
The problem is that setting ShowInTaskbar to false changes the window handle, which means that the hwnd passed to RegisterHotkey is no longer valid.
Registering the hotkey after setting ShowInTaskBar works fine.
Winforms works around a pretty draconian restriction in the winapi. Some properties of a window can only be specified when a window is created and can't be changed later. Or in other words, they are specified in the native CreateWindowEx() call.
It works around it by calling CreateWindowEx() again. Or in other words, destroy the existing window and create it again. That's a nifty trick but it does have some side effects. You can see a wee bit of flicker for example when the new window paints itself. Some bigger side effects are visible on for example a TreeView. All the nodes collapse when it gets recreated. Hard to avoid, there is just too much state associated with the original window. For a Form, the ShowInTaskbar property is one such property. But also RightToLeft, FormBorderStyle, ControlBox, etcetera.
The most relevant side-effect is the one you are running into. Recreating the window always changes the Handle property, inevitably. And that goes wrong when you use RegisterHotKey(), or a library that uses it, that winapi call uses the window handle. So when Winforms destroys that window there will never again be a callback.
It is easy to fix, you are just using the wrong event handler. Make the call in an override for the OnHandleCreated method instead. It re-runs when the window gets re-created. Yet another easy fix, but not nearly as reliable, is to only set properties like ShowInTaskbar in the constructor.
I have lots of old Windows Forms applications that will eventually be ported to WPF (it is a large application so it can't be done in one sprint), and I have started the process by creating a main menu in WPF. The Windows Forms applications are separate windows opened from this menu.
The Windows Forms applications are opening and working without any problems except the issues I am having with the shortcut and Tab keys. The tab key is not moving focus to the next control, and the Alt key to trigger the &Search button no longer works.
What am I doing wrong?
A partial solution I discovered is to call this from your WPF constructor:
System.Windows.Forms.Integration.WindowsFormsHost.EnableWindowsFormsInterop();
(You need to reference the dll WindowsFormsIntegration.dll)
I say partial because not all key strokes function as expected. Eg, seems to work okay for simple forms.
See this:
http://msdn.microsoft.com/en-us/library/system.windows.forms.integration.windowsformshost.enablewindowsformsinterop(v=vs.100).aspx
I finally managed to fix the issue by hosting the winform inside a WindowsFormsHost control inside a WPF form.
public partial class MyWindow : Window
{
public MyWindow()
{
InitializeComponent();
Form winform = new Form();
// to embed a winform using windowsFormsHost, you need to explicitly
// tell the form it is not the top level control or you will get
// a runtime error.
winform.TopLevel = false;
// hide border because it will already have the WPF window border
winform.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.windowsFormsHost.Child = winform;
}
}
Please note that you may also need to hook up the winform close event if you have a button to close the form.
This is by design. Shortcut keys are handled at the message loop level, detected before the Windows message gets dispatched to the window with the focus. That's the reason those keys can work regardless of the focus.
Problem is, you don't have the Winforms message loop pumping the messages. Application.Run() is implemented by WPF in your program, not Winforms. So any of the code in Winforms that processes keyboard messages to implement shortcut keystrokes won't run.
There's no good solution for this, it is pretty fundamentally the "can't get a little pregnant" problem. This code in Winforms is locked up heavily since it would allow CAS bypass. The only workaround is to display a Form derived class that contain Winforms controls with its ShowDialog() method. That method pumps a modal message loop, the Winforms one, good enough to revive the shortcut keystroke handling code. Restructure your approach by converting the main windows first, dialogs last.
Another solution I found to handle focus on the Tab key is to override OnKeyDown like this:
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab)
{
HandleFocus(this, ActiveControl);
}
else
{
base.OnKeyDown(e);
}
}
internal static void HandleFocus(Control parent, Control current)
{
Keyboard keyboard = new Keyboard();
// Move to the first control that can receive focus, taking into account
// the possibility that the user pressed <Shift>+<Tab>, in which case we
// need to start at the end and work backwards.
System.Windows.Forms.Control ctl = parent.GetNextControl(current, !keyboard.ShiftKeyDown);
while (null != ctl)
{
if (ctl.Enabled && ctl.CanSelect)
{
ctl.Focus();
break;
}
else
{
ctl = parent.GetNextControl(ctl, !keyboard.ShiftKeyDown);
}
}
}
The advantage of this solution is that it doesn't require neither a WindowsFormsHost nor a message pump which can be a hassle to implement. But I don't know if it is possible to handle shortcuts keys like this because I didn't need it.
Check if IsTabStop="True" and TabIndex is assigned. For Alt + Key shortcut, try using the underscore (_) character instead of the ampersand (&).
I'm using webbrowser control in my winforms app (c#). And when it is doing automation things, I'm losing focus control from the window I was working with. Webbrowsers' form doesn't show up also, I just lose focus from the contol. I now writing this message I have to click into textbox again and again...
How to disable such behaviour in webbrowser?
I create invisible webbrowser like that:
var br = new WebBrowser();
br.Visible = false;
br.ScriptErrorsSuppressed = true;
Please advise.
I had the same problem:
The Webbrowser Control stole focus from the application once the URL is loaded.
This worked for me:
Before Webbrowser.Navigate() method call, set the parent control of the Webbrowser to Enabled = false.
At the DocumentCompleted event of the Webbrowser, reset parent control of the Webbrowser to Enabled = true.
You can't do it directly on Webbrowser because WebBrowserBase.Enabled is not supported.
Let me know if it works for you.
You could try disabling it globally via the SystemParametersInfo api. Use SPI_SETFOREGROUNDLOCKTIMEOUT. Setting foreground lockout is a global settings, so you will want to clear this setting when you're done. A more permanent solution is to change HKCU\Control Panel\Desktop\ForegroundLockTimeout registry key. See also this discussion on social.msdn (specifically, billb08's answer).
I guess WebBrowser acquires the focus after a page is loaded by calling Navigate (or the Click method of an HtmlElement, which causes navigation). The focus could be given back to the control on the window (the TextBox) in the DocumentComplete event handler of the WebBrowser, but this is very difficult:
When would you determine which control owned the focus
originally? Before calling Navigate? This is not enough, because the
user can move to another control after calling Navigate, but before
handling DocumentComplete.
AFAIK setting the focus to a TextBox will select its whole
content, so you will have to put the cursor back to its original
position. But when would you store the original position? Same problem.
There can be more than one DocumentComplete event after a single
Navigate (or Click).
A possible solution would be to create a separate application for your hidden WebBrowser. This second application would be invisible, and could communicate with the original GUI application using some InterProcess Communication (IPC) technique. Because the WebBrowser in this case would run in a different process, you would have a better chance not to lose lose the focus and bother the user.
it's a very complex problem to fix, and should be revised by microsoft, an app just stealing the focus is not logical, it does depend on what the website is doing though. I had to resort to a CBT filter, see http://msdn.microsoft.com/en-us/magazine/cc188966.aspx, and filter out unwanted HCBT_ACTIVATE and HCBT_SETFOCUS (return 1;). You can use GetWindowClass(wParam) to see what's going on.
Even above didn't entirely work, the app window would still pop to the front temporarily so worked around that using SetWindowPos HWND_TOPMOST and HWND_NOTOPMOST on the window currently in foreground. The HCBT_SETFOCUS gets hit 2 or 3 times so on 1st set HWND_TOPMOST and last set HWND_NOTOPMOST. Count how many classname == "Internet Explorer_Server" which should be 2 (or possibly depends on website?), the other is "Shell Embedding" but doesn't always occur. Hope it helps.
I was looking at all the other answers to this question and they weren't working for me, but i saw the one about settings Browser.Parent.Enabled = false; i tried so and got an error, so i tried this instead it just came to mind.
Browser.Parent = new Control();
Browser.Parent.Enabled = false;
And now the problem is completely gone, it does not take away focus anymore.
I am using the web browser class as a variable, it is not on my form.
well this worked for me try it, this seemed to be a 100% solution.
Most of the methods won't work for me on more than one web browser. This method is work with any amount of web browsers;
1. Put web browser into a panel and set panel enabled to false, then navigate;
webBrowser.Parent = panelBottom;
panelWebBrowser.Enabled = false;
webBrowser.Navigate("http://www.google.com");
2. Define a navigated event to web browser and delay panels enabling for a second;
private void webBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
System.Threading.Timer timer = null;
timer = new System.Threading.Timer((obj) =>
{
panelWebBrowser.Enabled = true;
timer.Dispose();
},null, 1000, Timeout.Infinite);
}
My solution for sending the focus back to a form:
Private Sub Web_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles Web.DocumentCompleted
If Me.Visible = False Then
For Each f As Form In My.Application.OpenForms
If TypeOf f Is frmLogin Then
Dim fl As frmLogin = DirectCast(f, frmLogin)
If fl.Visible = True Then
fl.Focus()
Exit For
End If
End If
Next
End If
End Sub
How can I create a Popup balloon like you would see from Windows Messenger or AVG or Norton or whomever?
I want it to show the information, and then slide away after a few seconds.
Edit: It needs to be blocking like Form.ShowDialog() because the program exits after displaying the notification
You can use the notifyIcon control that's part of .NET 2.0 System.Windows.Forms. That allows you to place an icon for your application in the System Tray. Then, you can call the ShowBalloonTip(int timeOut) method on that. Be sure however to first set the text, and icon properties on the notifyIcon for it to work. Small code sample:
private void button1_Click(object sender, EventArgs e)
{
this.notifyIcon1.BalloonTipText = "Whatever";
this.notifyIcon1.BalloonTipTitle = "Title";
this.notifyIcon1.Icon = new Icon("icon.ico");
this.notifyIcon1.Visible = true;
this.notifyIcon1.ShowBalloonTip(3);
}
EDIT: Ok, so notifyIcon won't work for you. My second suggestion would then be to create your own control for this. Actually, I would use a form. A simple form, with no borders, and no control box and just have a timer running so you can set the Opacity for fade in/out. Then, you can easily get the bottom right of the screen using the Rectangle Screen.PrimaryScreen.WorkingArea. Then just show your form at that position.
Don't create a modal (blocking) balloon. Please. A big part of the design of these UIs is that they are not dialogs: they're transient, potentially non-interactive elements, intended to provide incidental information to a user without necessarily interrupting their workflow. A balloon that steals focus and blocks user input would be irritating at best - if you need a dialog, then use a dialog.
The .NET 1.1 Visual Basic Power Pack had a toaster control.