The problem I have is that when one switches from one child form to another a strange thing happens: the form to be shown appears in a strange way as if it has been minimized, restored and then maximized, causing an effect of like several drawing events at the same time.
The problem does not appear (ie, everything works) in these situations:
when one switches between forms using CTRL+TAB or CTRL+SHIFT+TAB
when child forms are not maximized and one simply changes the order/position of the form
Possibly related; there are a tonne of methods one can called for showing forms, including (and related):
child.Focus()
child.Show()
child.Activate()
child.Select()
child.BringToFront()
My question is, what exactly should I be calling?
Edit:
In my case, I have the following code that works, but still causes the weird effect I described above:
private void tabForms_MouseClick(object sender, MouseEventArgs e)
{
// handle middle-mouse-button click (close)
if (e.Button == System.Windows.Forms.MouseButtons.Middle)
{
// See: http://stackoverflow.com/a/745361
TabPage tab = tabForms.TabPages.Cast<TabPage>().Where((t, i) => tabForms.GetTabRect(i).Contains(e.Location)).FirstOrDefault();
if (tab != null && tab.Tag != null) (tab.Tag as Form).Close();
}
// handle left-mouse-button click (show)
if ((tabForms.SelectedTab != null) && (tabForms.SelectedTab.Tag != null) && (ActiveMdiChild != tabForms.SelectedTab.Tag))
{
(tabForms.SelectedTab.Tag as Form).Select();
(tabForms.SelectedTab.Tag as Form).Show();
}
}
PS: Without the .Select() it doesn't work. Although it seems that it still works if I replace .Select() and .Show() with .Focus().
This might be old now, but for the benefit of anyone else with this issue there's a simple solution to this. You can call the Win32 method LockWindowUpdate, providing the window handle to your form, create and open / maximise your form, then call LockWindowUpdate again and provide an IntPtr.Zero value (as shown below).
try
{
LockWindowUpdate(this.Handle);
// Open your form, maximise it etc
}
catch (Exception ex)
{
// Handle any errors
}
finally
{
LockWindowUpdate(IntPtr.Zero);
}
LockWindowUpdate disables drawing for whichever window handle you provide it, calling it a second time with a zero IntPtr resumes drawing the window. Only one window can be locked at a time.
You will need two other things; a DLL import, and a using reference for System.Runtime.InteropServices. Here's the DLL import:
[DllImport("user32.dll", EntryPoint = "LockWindowUpdate", SetLastError = true,
ExactSpelling = true, CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern long LockWindowUpdate(IntPtr hWndLock);
Related
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 ..
}
}
I have a winforms application where I need to obtain the current keyboard layout of the user. To do that I'm using System.Windows.Forms.InputLanguage.CurrentInputLanguage.LayoutName.
This works fine as long as the user has the form as his active window, once he focuses something else and changes the language the former property wont return a proper value, it will return the last used language while the form was still the active window.
Is there a way I can get the users keyboard layout's name even if he is not focusing the form, there are no restrictions to what can be used.
As you might already know that the System.Windows.Forms.InputLanguage.CurrentInputLanguage.LayoutName property returns the keyboard layout for the current thread and no matter what layout you choose it would remain the same for the executing thread unless you select that window and change the keyboard input layout for that window.
That said, what you are essentially trying to do is check the current keyboard layout culture and be able to know when it changes. I had a similar requirement a while ago and I came up with the following code which served me well:
public delegate void KeyboardLayoutChanged(int oldCultureInfo, int newCultureInfo);
class KeyboardLayoutWatcher : IDisposable
{
private readonly Timer _timer;
private int _currentLayout = 1033;
public KeyboardLayoutChanged KeyboardLayoutChanged;
public KeyboardLayoutWatcher()
{
_timer = new Timer(new TimerCallback(CheckKeyboardLayout), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
}
[DllImport("user32.dll")] static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hwnd, IntPtr proccess);
[DllImport("user32.dll")] static extern IntPtr GetKeyboardLayout(uint thread);
public int GetCurrentKeyboardLayout()
{
try
{
IntPtr foregroundWindow = GetForegroundWindow();
uint foregroundProcess = GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero);
int keyboardLayout = GetKeyboardLayout(foregroundProcess).ToInt32() & 0xFFFF;
if (keyboardLayout == 0)
{
// something has gone wrong - just assume English
keyboardLayout = 1033;
}
return keyboardLayout;
}
catch (Exception ex)
{
// if something goes wrong - just assume English
return 1033;
}
}
private void CheckKeyboardLayout(object sender)
{
var layout = GetCurrentKeyboardLayout();
if (_currentLayout != layout && KeyboardLayoutChanged != null)
{
KeyboardLayoutChanged(_currentLayout, layout);
_currentLayout = layout;
}
}
private void ReleaseUnmanagedResources()
{
_timer.Dispose();
}
public void Dispose()
{
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
~KeyboardLayoutWatcher()
{
ReleaseUnmanagedResources();
}
}
And use it like:
new KeyboardLayoutWatcher().KeyboardLayoutChanged += (o, n) =>
{
this.CurrentLayoutLabel.Text = $"{o} -> {n}"; // old and new KB layout
};
Couple of things to note here. Keyboard selection in Windows is done on a per-thread basis. This allows the user to select a different keyboard locale for any given application, which Windows will respect, while leaving other applications alone.
The user does this by enabling the language bar (for installed keyboards) in the Windows Taskbar. If they select a different keyboard while another application has the focus, then the selection only applies to that application. And besides, if your application doesn't have the focus, it can't do anything about it anyway. By using the language bar in this way, the user is plainly stating their intention to have the selected keyboard apply to the active application only. There is no way for your application to find out about it, since from Windows' point of view, it's none of your application's business.
Now, if you want to know if the user changes the keyboard for the entire system (using the control panel applet), that's feasible. If your application doesn't have the focus, there's no way to trap the notification message. Your form will still consider the current language to be the one it started with. However, the system-wide change does change the InputLanguage.DefaultInputLanguage. to the newly-selected keyboard. So, override the OnActivated handler and check the value of the default language, not the current one (which is per-thread).
I want to disable the screensaver and monitor power off. At this stage there's no windows form, which I could youse. Thus I wan't to use NativeWindow.
Here's my code
sealed class ObserverWindow : NativeWindow, IDisposable
{
internal ObserverWindow()
{
this.CreateHandle(new CreateParams()
{
Parent= IntPtr.Zero
});
}
public void Dispose()
{
DestroyHandle();
}
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND &&
((((long)msg.WParam & 0xFFF0) == SC_SCREENSAVE) ||
((long)msg.WParam & 0xFFF0) == SC_MONITORPOWER))
{
msg.Msg = 0;
msg.HWnd = IntPtr.Zero;
}
base.WndProc(ref msg);
}
}
The Problem is, that the WndProc is not called with WM_SYSCOMMAND. Actualy the WndProc is called 4 times. At the last call there's msg.Msg == WM_CREATE.
I think I'm missing some create parameter. Does anyone have advise?
Regards Michael
UPDATE
I was running the code in a non STA thread. Thus the window did not reveive any messages exept the initial ones. Now I'm receiving WM_SYSCOMMAND messages. But when the screensaver is activated, there's no message.
I also tried to overwrite a Form's WndProc with the same result. But this used to work in Windows XP. Is there a change in Windows 7?
OS: Windows 7 64bit.
SOLUTION
As a comment in this Question states, only the foreground window can cancel the screensaver. Thus the above code can't work. The NativeWindow is great for receiving messages, but not for canceling a screensaver. For latter I recommend the answer to this question.
The proper way to do this is by telling Windows that your thread needs to have the display active. Commonly used by video players. P/Invoke the SetThreadExecutionState() API function, pass ES_DISPLAY_REQUIRED. And ES_SYSTEM_REQUIRED to keep the machine from shutting down automatically. Visit pinvoke.net for the required declarations.
Disabling the screen saver is much easier, according to this KB article:
This can be done easily using:
SystemParametersInfo( SPI_SETSCREENSAVEACTIVE,
FALSE,
0,
SPIF_SENDWININICHANGE
);
[...]
If you need the screen saver to start up again, you'll need to reinitialize the time-out period. Do this by [c]alling SystemParametersInfo (SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE).
You could try overriding DefWndProc instead.
public override void DefWndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND &&
((((long)msg.WParam & 0xFFF0) == SC_SCREENSAVE) ||
((long)msg.WParam & 0xFFF0) == SC_MONITORPOWER))
{
msg.Msg = 0;
msg.HWnd = IntPtr.Zero;
}
base.DefWndProc(ref msg);
}
I'm not on a Windows box right now, so I cannot test this. Let me know if it works.
I want to disable the screensaver and monitor power off. At this stage there's no windows form, which I could youse. Thus I wan't to use NativeWindow.
Here's my code
sealed class ObserverWindow : NativeWindow, IDisposable
{
internal ObserverWindow()
{
this.CreateHandle(new CreateParams()
{
Parent= IntPtr.Zero
});
}
public void Dispose()
{
DestroyHandle();
}
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND &&
((((long)msg.WParam & 0xFFF0) == SC_SCREENSAVE) ||
((long)msg.WParam & 0xFFF0) == SC_MONITORPOWER))
{
msg.Msg = 0;
msg.HWnd = IntPtr.Zero;
}
base.WndProc(ref msg);
}
}
The Problem is, that the WndProc is not called with WM_SYSCOMMAND. Actualy the WndProc is called 4 times. At the last call there's msg.Msg == WM_CREATE.
I think I'm missing some create parameter. Does anyone have advise?
Regards Michael
UPDATE
I was running the code in a non STA thread. Thus the window did not reveive any messages exept the initial ones. Now I'm receiving WM_SYSCOMMAND messages. But when the screensaver is activated, there's no message.
I also tried to overwrite a Form's WndProc with the same result. But this used to work in Windows XP. Is there a change in Windows 7?
OS: Windows 7 64bit.
SOLUTION
As a comment in this Question states, only the foreground window can cancel the screensaver. Thus the above code can't work. The NativeWindow is great for receiving messages, but not for canceling a screensaver. For latter I recommend the answer to this question.
The proper way to do this is by telling Windows that your thread needs to have the display active. Commonly used by video players. P/Invoke the SetThreadExecutionState() API function, pass ES_DISPLAY_REQUIRED. And ES_SYSTEM_REQUIRED to keep the machine from shutting down automatically. Visit pinvoke.net for the required declarations.
Disabling the screen saver is much easier, according to this KB article:
This can be done easily using:
SystemParametersInfo( SPI_SETSCREENSAVEACTIVE,
FALSE,
0,
SPIF_SENDWININICHANGE
);
[...]
If you need the screen saver to start up again, you'll need to reinitialize the time-out period. Do this by [c]alling SystemParametersInfo (SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE).
You could try overriding DefWndProc instead.
public override void DefWndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND &&
((((long)msg.WParam & 0xFFF0) == SC_SCREENSAVE) ||
((long)msg.WParam & 0xFFF0) == SC_MONITORPOWER))
{
msg.Msg = 0;
msg.HWnd = IntPtr.Zero;
}
base.DefWndProc(ref msg);
}
I'm not on a Windows box right now, so I cannot test this. Let me know if it works.
I've written a IE Toolbar in C# and everything is working fine except that when I open a child Windows Form from my toolbar, the tab key doesn't work on the child form to allow me to move from field to field.
The interesting part is that when I open my child form using form.showDialog() instead of form.show() the tabs work like normal.
The toolbar I've created is based on this article and this article
I've implemented TranslateAcceleratorIO as mentioned in several articles, but still no luck.
Here are my implmentations of TranslateAcceleratorIO() and HasFocusIO() (implemented in my toolband class)
[DllImport("user32.dll")]
public static extern int TranslateMessage(ref MSG lpMsg);
[DllImport("user32", EntryPoint = "DispatchMessage")]
static extern bool DispatchMessage(ref MSG msg);
public int HasFocusIO()
{
return this.ContainsFocus ? 0 : 1; //S_OK : S_FALSE;
}
public int TranslateAcceleratorIO(ref MSG msg)
{
if (msg.message == 0x100)//WM_KEYDOWN
if (msg.wParam == (uint)Keys.Tab || msg.wParam ==(uint)Keys.F6)
{
if (SelectNextControl(
ActiveControl,
ModifierKeys == Keys.Shift ? false : true,
true,
true,
false)
)
{
return 0;//S_OK
}
}
else
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
return 0;//S_OK
}
return 1;//S_FALSE
}
I've also tried having TranslateAccelerator like this with no luck:
public int TranslateAcceleratorIO(ref MSG msg)
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
return 0;//S_OK
}
Has anybody else run into this issue?
Are you also implementing HasFocusIO? I believe your main toolbar class must also implement HasFocusIO and return true.
These types of problems with IE toolbars were the bane of my existence for a while. I think what I eventually ended up doing was creating separate UI threads and making my dialogs modal in those threads, which eliminated a bunch of weird issues. But I think implementing HasFocusIO and TranslateAcceleratorIO should work for this particular one.
Where are you implementing these? It's hard to tell from what you have there, are you implementing them in your Form or are you implementing them in your deskband class?
You need to implement them in your DeskBand implementation, and HasFocusIO needs to return true whenever one of your windows has focus (not just when the toolbar has focus). Then the messages for Tab, Delete, arrow keys, etc should be dispatched to the TranslateAcceleratorIO, also in your deskband, and from there you'll have to pass them along to your form.
The IE plugin framework is incredibly hacky that way.