Mouse cursor flickers over selected text - how to prevent this? - c#

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.

Related

Ignoring VM_ERASEBKGND from paint event does not work every time on winform off-screen why?

I have a windows form application that draws image and geometry form in front of it. I have a problem that occurs when I drag the windows form off-screen and bring it back on-screen, makes the part that went off-screen all cleared.
I read that this might occurs because of windows message sending to my application the VM_ERASEBKGND and clear the part that went off-screen. (Am I right ? )
So first, I have created a test application that only displays an image and I overrides the WndProc method that does work in that case, here how it is implemented :
protected override void WndProc(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
//0x0014 reprensts VM_ERASEBKGND message
case 0x0014:
//ignore this message else pass it to base
break;
default:
base.WndProc(ref m);
break;
}
}
Basically, I'm am just ignoring VM_ERASEBKGND message, and it does work in that case. The application is a Form that contains a PictureBox.
Now, I wanted to integrate this to another project that is quite the same, a PictureBox into a Form, but has some more Control such has ScrollBar , Axis, GridPanel.
When overwriting the WndProc method the same way I did in another project, it doesn't work even though it goes into the break point and does not process base.WndProc(ref m). However, it seems to doesn't care that I am not processing the message, it still does clear my Form.
My question is : is it possible that other control like axis and scrollbar makes the form cleared when moving off-screen even though I've overwritten WndProc like in the example above and ignored ERASEBKGND.
This is a really weird behavior from windows since it does work in one application, but not in another which is almost the same.
My easy trick to resolve this problem, which is not the best solutions in your case, but still works, is to override WndProc of your form and catch your VM_ERASEBKG. And instead of passing to base, Present back your swapchain which redraws your swapchain.
GPU and CPU from windows 32 handles often gives bad behavior when using DirectX with winforms.
Protected Overrides Sub WndProc(ByRef m As Message)
Select Case m.Msg
Case VM_ERASEBKGND
swapchain.Present(1,PresentFlags.None)
Exit Select
Case Else
MyBase.WndProc(m)
Exit Select
End Select
End Sub

How to always show underline character? (C# Windows Form)

I'm making a dialog that look like Notepad's Find Dialog. I notice that the underline character of Notepad's Find dialog always show all the time (I have to press ALT key to see this with my dialog). How to always show underline character like that?
I try to use SendKeys.Send("%") on Form_Load event but nothing happens.
There is another problem, when I press ALT key on child Form, it show underline charater of parent Form too. How to avoid that?
This is sreenshot of Notepad's find dialog:
I pretty sure this is not about Ease of Acess Center, because the main Form of Notepad doesn't always show this.
Seeing the n in "Find" underlined in the Notepad dialog is an intentional bug. The dialog isn't actually part of Notepad, it built into Windows. Underlying winapi call is FindText(). The feature is in general a pile 'o bugs, one core problem is that creating a new window after the UI is put in the "show underlines" state doesn't work correctly, that new window isn't also in that state. Presumably the intentional bug was based on the assumption that the user would be somewhat likely to use the Alt key to get the dialog displayed. Yuck if he pressed Ctrl+F.
The Windows dialog probably does it by simply drawing the "Find" string with DrawText() with the DT_NOPREFIX option omitted. You could do the same with TextRenderer.DrawText(), omit the TextFormatFlags.HidePrefix option.
Not exactly WinFormsy, you'd favor a Label control instead of code. It is hackable, you'd have to intentionally send the message that puts the UI in the "show underlines" state for your own dialog. Do so in an override for the OnHandleCreated() method:
protected override void OnHandleCreated(EventArgs e) {
const int WM_UPDATEUISTATE = 0x0128;
base.OnHandleCreated(e);
SendMessage(this.label1.Handle, WM_UPDATEUISTATE, new IntPtr(0x30002), IntPtr.Zero);
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
Where "label1" is the control you want to show underlines. Repeat for other controls, if any. It is supposed to work by sending the message to the form, that this doesn't work is part of the pile 'o bugs. Yuck.
Fwiw: do not fix this by changing the system option as recommended in the duplicate. That's very unreasonable.
You can use RichTextBox control and extension method for that:
public static class FontHelper
{
public static void Underline(this RichTextBox txtBox, int underlineStart, int length)
{
if (underlineStart > 0)
{
txtBox.SelectionStart = underlineStart;
txtBox.SelectionLength = length;
txtBox.SelectionFont = new Font(txtBox.SelectionFont, FontStyle.Underline);
txtBox.SelectionLength = 0;
}
}
}
richTextBox1.Text = "Search for";
richTextBox1.Underline(7, 1); // index and length of underlying text

Intercept mouse click from other program

I’m trying to intercept mouse clicks from another program. I’m making a plugin for the program, that overlays a transparent form on the program and displays additional information. When I click on the transparent part of the form I can click on things in the main program. I don’t want this to happen (at least not every time - there are some parts where you are allowed to click and some parts where you aren’t but this isn’t the problem).
The way I’m doing this now is by using WH_MOUSE_LL, this is working fine and I can keep the mouse click from getting to the program by returning a non zero value (http://msdn.microsoft.com/en-gb/library/windows/desktop/ms644988(v=vs.85).aspx).
The problem is, this makes my main program lag, I don’t need to get notifications for all mouse movements, I only want to get a notification if the user actually clicked something. Is there any way I can limit the WH_MOUSE_LL so it only fires on mouse clicks? (The lag isn’t because of calculations in the MouseHookProc method - it’s currently doing nothing except for calling: CallNextHookEx(hHook, nCode, wParam, lParam).)
I’ve tried to fix this by using a global hook (http://www.codeproject.com/Articles/18638/Using-Window-Messages-to-Implement-Global-System-H) that hooks the WM_MOUSEACTIVATE message. The idea was to only hook up the WH_MOUSE_LL when I received a WM_MOUSEACTIVATE notification. Unfortunately WH_MOUSE_LL click notification gets sent before WM_MOUSEACTIVATE so this doesn't work.
EDIT:
#Nanda here’s the proc code:
public int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
return WindowUtility.CallNextHookEx(hHook, nCode, wParam, lParam);
}
As you can see I’m not doing very much with it atm, but it already lags...
#Cody Gray I’ve made a very small test for the Form handling the messages:
public class Form1 : Form
{
private TrackBar m_Trackbar;
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public Form1()
{
m_Trackbar = new System.Windows.Forms.TrackBar();
m_Trackbar.LargeChange = 1;
m_Trackbar.Location = new System.Drawing.Point(5, 10);
m_Trackbar.Maximum = 100;
m_Trackbar.Size = new System.Drawing.Size(280, 40);
m_Trackbar.Value = 100;
this.Controls.Add(m_Trackbar);
m_Trackbar.Scroll += new System.EventHandler(this.m_TrackbarScroll);
}
private void m_TrackbarScroll(object sender, System.EventArgs e)
{
this.Opacity = ((Convert.ToDouble(m_Trackbar.Value)) / 100);
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x201: //WM_LBUTTONDOWN
Console.WriteLine("MouseButton Down!");
//I could copy the Message over to the MainProgram with this right?
//SendMessage(MainProgramHwnd, m.Msg, m.WParam, m.LParam);
//This will also only work on an opacity higher than 0.
break;
}
base.WndProc(ref m);
}
}
When you said: “return that it's transparent and let it be routed to the window underneath it?” Can I do this by using SendMessage and basically copying the message I receive in my WndProc method?
To make things more complicated I’m also using this form http://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C. To my understanding this enables me to draw bitmaps on the form who are Anti Aliasing against the background. With this form there seems to be no way to set the opacity, as it’s just transparent all the time. Is there a better way to draw bitmaps on a Form?
You may want to look into Easyhook (http://easyhook.codeplex.com/) which will allow you to hook Windows API calls from a single process rather than for all processes.

Drag-able WinForm Problem

I have a windows form that can be moved around by clicking and dragging on any portion of the form. I used the method of overriding WndProc, and setting the result of the NCHITTEST function to be HTCAPTION, in order to fool the form into thinking I clicked the caption - so it enables dragging.
The code for this works great, and is below:
protected override void WndProc(ref Message msg)
{
if (msg.Msg == (int)WinAPI.NCHITTEST)
{
DefWndProc(ref msg);
if ((int)msg.Result == (int)MousePositionCodes.HTCLIENT)
{
msg.Result = (IntPtr)MousePositionCodes.HTCAPTION;
return;
}
}
}
base.WndProc(ref msg);
}
The problem occurs when I dock a ToolStripPanel into the form (this is acting as a draggable toolbar). I need any portion of the ToolStripPanel that is not covered by a ToolStrip to pass up the messages necessary to cause the whole form to enter drag mode.
I have created my own ToolStripContainer class to override the WndProc function and have tried using the same function as above, but it causes the ToolStripContainer to enter drag mode within the form, which is not the desired functionality.
I have also tried passing up NCHITTEST messages to the parent, as well as constructing a new message with the current mouse coordinates and sending it to the parent using the WinAPI and the parent's window handle.
I have to be missing something simple here... Anyone have any ideas?
Try in WndProc of your own ToolStripContainer where you have testing for WM_NCHITTEST returning HTTRANSPARENT (-1) for the area where you want drag to happen. This will cause the message to go up in chain to your form where you handle it and return HTCAPTION so drag happens.
Hope this helps.

Disabling redraw in WinForms app

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).

Categories