I have a WinForms usercontrol (ZedGraphControl) hosted on a WindowsFormsHost WPF control, placed in a nested Grid. When I attempt to handle Ctrl+Left/Right in the WinForms control code the control looses focus, and focus moves to control in the grid cell to the Left/Right.
What may cause this, and how can the behavior be disabled?
Edit: The behavior is triggered if a DataGrid is in the grid cell to the left or right, with cells displayed that can take focus. Also if a TextBox is there, possibly any control that can take editable focus.
Sounds like something in your WPF universe is intercepting those key combinations and using them to move focus. At first, I thought it might be WPF's built-in directional keyboard navigation, but based on your edits, I think it might be something else.
Whatever is happening, you might be able to prevent it by overriding some keyboard processing behavior in WindowsFormsHost.
public class WindowsFormsHostEx : WindowsFormsHost
{
protected override bool TranslateAcceleratorCore(ref MSG msg, ModifierKeys modifiers)
{
const int vkLeft = 0x25;
const int vkRight = 0x27;
if (modifiers == ModifierKeys.Control &&
((int)msg.wParam == vkLeft || (int)msg.wParam == vkRight))
{
var m = Message.Create(msg.hwnd, msg.message, msg.wParam, msg.lParam);
this.Child?.WindowTarget?.OnMessage(ref m);
return true;
}
return base.TranslateAcceleratorCore(ref msg, modifiers);
}
}
Based on my tests, this causes the WindowsFormsHostEx to 'claim' all Ctrl+Left/Right keystrokes when the host has focus. It dispatches them to the hosted WinForms content, with WPF apparently carrying on as if the event never happened.
Related
I have a WinForms application that has a TextBox control (search box) at the top of it. This TextBox is constantly receiving focus during normal application use, and it is very distracting.
I would like the TextBox to only receive the focus if the user explicitly clicks on it.
I can think of a couple rather complicated ways to accomplish this:
Change an image of a text box into a text box when clicked
Keep track of mouse clicks and shift the focus away based on mouse state
Is there something simpler that I can do to accomplish this?
Edit to add better description of problem based on new understanding
Based on the answers that I have received, I now have a bit of a better understanding of what was causing this problem. As the user interacted with my application, various actions would cause controls to either be disabled or to completely disappear. If one of these controls happened to have the focus at the time, then the next control in the tab order would receive the focus.
I don't know what was the "next control" before I added the text box in question. The application has hundreds of controls on screen at any given time, and I'm pretty sure that tab order was never intentionally defined. Whatever it was before, it was innocuous. After adding the search text box, it seemed like that control would always end up with the focus.
Here is a very simple example that demonstrates what was happening:
public class Form1 : Form
{
public Form1()
{
var button = new Button
{
Location = new System.Drawing.Point(159, 67),
Size = new System.Drawing.Size(75, 23),
TabIndex = 0,
Text = #"Click me"
};
button.Click += (sender, args) => button.Enabled = false;
var textBox = new TextBox
{
Location = new System.Drawing.Point(159, 142),
Name = "textBox1",
Size = new System.Drawing.Size(174, 20),
TabIndex = 1
};
SuspendLayout();
ClientSize = new System.Drawing.Size(486, 392);
Controls.Add(textBox);
Controls.Add(_button);
ResumeLayout(false);
PerformLayout();
}
}
After starting the application, clicking on the button will force the text box to get the focus, since it is the next in the tab order. As mentioned by Handbag Crab in the accepted answer, this behavior can be avoided by setting TabStop = false on the text box.
textBox1.TabStop = false;
The above should stop it receiving focus from tabbing.
Subclass the TextBox and over WndProc function to capture the focus message and handle it. Maybe something like this:
if (m.Msg == WM_MOUSEACTIVATE) {
m.Result = (IntPtr)MA_NOACTIVATEANDEAT;
return;
}
base.WndProc(ref m);
I'm encountering strange behaviour while moving the mouse over selected text in a RichTextBox (C#, .NET 4.0, WinForms): as I move the mouse cursor, it flickers between Cursors.Arrow and Cursors.IBeam.
I found code that disables the flickering:
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == WM_SETCURSOR) //WM_SETCURSOR is set to 0x20
return;
}
but then the mouse cursor is stuck as Cursors.Arrow, even when I manually set it to something else, ex:
void RTFBox_MouseMove(object sender, MouseEventArgs e)
{
Cursor = Cursors.IBeam;
}
(I also had logic in the MouseMove function to set Cursor to other types of non-Arrow cursors, depending on what the mouse was over.)
I also tried:
public override Cursor Cursor
{
get
{
//(I have other logic here to determine the desired cursor type I want; in all cases it was a non-Arrow cursor)
return Cursors.Cross; //'Cross' instead of 'IBeam' just to prove whether this works
}
set
{
return;
}
}
which successfully made the cursor a cross (but only when I commented out the WndProc code), but the flickering remained when I moused over selected text (with the mouse cursor changing between Arrow and Cross).
In trying to find a solution, I came across this post, but calling
SendMessage(Handle, LVM_SETHOTCURSOR, IntPtr.Zero, Cursors.IBeam.Handle);
from a class inheriting from RichTextBox did not fix the flickering problem.
My problem seems identical to the one desribed in this post, but the problem was described to exist on .NET 3.0 and fixed in .NET 3.5.
When I created a new project and inserted a RichTextBox into the form, the flickering is still there.
Thus, my question is: How do I prevent this flickering? Or does anyone know if this problem is resolved in later versions of .NET/visual studio?
[Update: I downloaded Visual Studio 2013, but the "flicker" effect is still present. I downloaded .Net 4.5.1 installer and told it to repair, but the "flickering" remained. Under "Properties" > "References", it says that "System.Windows.Forms" is version 4.0.0.0; I suppose this means that updating past 4.0 was unnecessary?]
Flickering is mostly because of Graphics objects or surface redrawing. If you put codes to an event of a control that continuosly updates itself (f.i. MouseMove), and the code modificates somehow the surface or the contents of it, flickering occurs.
I used to use DoubleBuffering to fix flickering problems. Just add this code to your form's constructor (above or below the InitializeComponent() method):
DoubleBuffered = true;
If this doesn't fix the problem, the issue is probably not because of a graphical trouble.
Cursor could also flicker when two events codes want to change the cursor at the same time.
It could be your code or a default code as well (f.i. the IBeam automatically appears when you move cursor above the Control).
Check your code, whether it contains codes that modify the Cursor or the selected text during you use it. Then change the cursor from a nother event every time to the type you want. I mean:
//MouseMove is the best choice in this case
private void RichTextBox1_MouseMove(object sender, MouseEventArgs e)
{
Cursor = Cursors.Arrow;
}
But I use .Net 4.0, and I don't have any problems with it. I think it has been fixed in the latest version.
Hope it helps a bit. :)
The documentation for WndProc's WM_SETCURSOR is found here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms648382(v=vs.85).aspx
You can use this code to manually set the cursor to a specific type:
[DllImport("user32.dll")]
public static extern int SetCursor(IntPtr cursor);
private const int WM_SETCURSOR = 0x20;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == WM_SETCURSOR)
{
SetCursor(Cursors.IBeam.Handle);
m.Result = new IntPtr(1); //Signify that we dealt with the message. We should be returning "true", but I can't figure out how to do that.
return;
}
base.WndProc(ref m);
}
However, this code results in the text-caret flickering every time SetCursor is called (which happens every time the mouse is moved in the control).
I added onto chess123mate's answer to get it to show the arrow cursor over the vertical scrollbar:
[DllImport("user32.dll")]
public static extern int SetCursor(IntPtr cursor);
private const int WM_SETCURSOR = 0x20;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == WM_SETCURSOR)
{
var scrollbarWidth = System.Windows.Forms.SystemInformation.VerticalScrollBarWidth;
var x = PointToClient(Control.MousePosition).X;
var inScrollbar = x > this.Width - scrollbarWidth;
var cursor = inScrollbar ? Cursors.Arrow : Cursors.IBeam;
SetCursor(cursor.Handle);
m.Result = new IntPtr(1); //Signify that we dealt with the message. We should be returning "true", but I can't figure out how to do that.
return;
}
base.WndProc(ref m);
}
I can't really speak to the flickering issue... it sounds like something that you need to speak with the vendor about.
As to why your code "fixed" the issue, but doesn't let you change the cursor, it helps to understand more about how the windows message pump works.
Basically, at a low level, you are intercepting requests to change the cursor and then blocking them. If you look at the documentation for this function, you'll see this comment:
Notes to Inheritors
Inheriting controls should call the base class's WndProc method to process any messages that they do not handle.
You are overriding this function, essentially becoming an "inheritor", and then "handling" messages requesting the cursor to change by ignoring it.
I need to program a keyboard, with some configurations and customizations.
I need to configure it to be enabled if any text area is focused before the keyboard.
mainly I need to know what control is selected.
The Keyboard is a standalone application, so it cannot deal directly with the controls, just by accessing the OS.
for more explanation, I am gonna tell you the scenario:
the user runs the keyboard application, which its 'topmost' is set, and ShowInTaskBar is unset, and the keyboard is disabled.
the user clicks on any text control in the window, like the notepad or a TextBox in Microsoft Word or Paint, then after clicking the keyboard application is enabled.
the user clicks on the keyboard application and its keys to write text on the TextBox selected before in step 2, please note that the previous TextBox will loose focus but the keyboard will still enabled.
You can try with Focused property
foreach (var control in this.Controls)
{
if (control.Focused)
{
....
}
}
Link : http://msdn.microsoft.com/fr-fr/library/system.windows.forms.control.focused.aspx
You will need two of the windows API function
GetActiveWindow(void);
GetWindowClass()
First will give you the active windows (Focussed window) and the second one will let you know if it is a text window or not.
You will need further functions sendmessage(...) to get the current text of the window and also to set the text.
It is more about windows API.
It is easy by searching for the caret position, since it should be larger than 0
GUITHREADINFO lpgui = new GUITHREADINFO();
IntPtr fore = GetForegroundWindow();
uint tpid = GetWindowThreadProcessId(fore, IntPtr.Zero);
lpgui.cbSize = Marshal.SizeOf(lpgui.GetType());
bool flag = GetGUIThreadInfo(tpid, out lpgui);
WINDOWINFO pwi = new WINDOWINFO();
pwi.cbSize = (uint)Marshal.SizeOf(pwi.GetType());
GetWindowInfo((IntPtr)lpgui.hwndCaret, ref pwi);
if (flag)
{
if (!(lpgui.rcCaret.Location.X == 0 && lpgui.rcCaret.Location.Y == 0))
{
//TODO
}
}
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 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).