トップ回答者
Windows8.1で、RichTextBox上で表示されるカーソルをデフォルト設定から変更するとカーソルが点滅します

質問
-
Visual Studio 2010を使ったプログラムを組んでいます。
Windows8.1を最近使うようになり、フォームにRichTextBoxを貼り付けてCursorプロパティを変更した状態で動作確認しました。
するとRichTextBox上でマウスカーソルを移動させている間、カーソルがデフォルト状態のアイコンと変更した後のアイコン、交互に高速で切り替わります。
過去のWindowsでも時々デフォルトのアイコンに一瞬戻る事は気づいていましたが、マウスを動かすたびに点滅するように切り替わるというのはWindows8.1で初めて確認しました。
この現象に対する修正プログラムや、Windows設定環境の変更による対策は何か見つかっているでしょうか?
回答
-
Mouseを動かすたびにWM_SETCURSORが飛んできてるからかな?
using System.Linq; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { ComboBox comboBox1 = new ComboBox(); this.Controls.Add(comboBox1); RichTextBox r = new RichTextBoxEx(); r.Top = comboBox1.Height + 5; r.Height = this.ClientSize.Height - r.Top; r.Width = this.ClientSize.Width; this.Controls.Add(r); comboBox1.Items.AddRange(typeof(Cursors).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).Select(pi => pi.GetValue(null, null)).OfType<Cursor>().ToArray()); comboBox1.SelectedIndexChanged += (s, e) => { r.Cursor = (Cursor)comboBox1.SelectedItem; }; comboBox1.SelectedItem = Cursors.Default; } class RichTextBoxEx : RichTextBox { public override Cursor Cursor { get { return base.Cursor; } set { isCursorChanging = true; base.Cursor = value; isCursorChanging = false; } } private bool isCursorChanging; const int WM_SETCURSOR = 0x20; protected override void WndProc(ref Message m) { if (m.Msg == WM_SETCURSOR && !isCursorChanging) { m.Result = System.IntPtr.Zero; return; } base.WndProc(ref m); } } } }
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク nekko-iron 2016年2月26日 8:57
-
英語ですが以下のようなサイトがありました。
Mouse cursor flickers over selected text - how to prevent this?
Window 10のVS2013でも確かに再現したので(そんなには目立たないのですが...)
上記のサイトを参考にコードを検証したところちらつきはなくなりました。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } } class RichBox : RichTextBox { [DllImport("user32.dll")] public static extern int SetCursor(IntPtr cursor); private const int WM_SETCURSOR = 0x20; protected override void WndProc(ref Message m) { if (m.Msg == WM_SETCURSOR) { SetCursor(Cursors.Hand.Handle); m.Result = new IntPtr(1); return; } base.WndProc(ref m); } } }
- 回答としてマーク nekko-iron 2016年2月26日 8:57
すべての返信
-
Mouseを動かすたびにWM_SETCURSORが飛んできてるからかな?
using System.Linq; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { ComboBox comboBox1 = new ComboBox(); this.Controls.Add(comboBox1); RichTextBox r = new RichTextBoxEx(); r.Top = comboBox1.Height + 5; r.Height = this.ClientSize.Height - r.Top; r.Width = this.ClientSize.Width; this.Controls.Add(r); comboBox1.Items.AddRange(typeof(Cursors).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).Select(pi => pi.GetValue(null, null)).OfType<Cursor>().ToArray()); comboBox1.SelectedIndexChanged += (s, e) => { r.Cursor = (Cursor)comboBox1.SelectedItem; }; comboBox1.SelectedItem = Cursors.Default; } class RichTextBoxEx : RichTextBox { public override Cursor Cursor { get { return base.Cursor; } set { isCursorChanging = true; base.Cursor = value; isCursorChanging = false; } } private bool isCursorChanging; const int WM_SETCURSOR = 0x20; protected override void WndProc(ref Message m) { if (m.Msg == WM_SETCURSOR && !isCursorChanging) { m.Result = System.IntPtr.Zero; return; } base.WndProc(ref m); } } } }
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク nekko-iron 2016年2月26日 8:57
-
英語ですが以下のようなサイトがありました。
Mouse cursor flickers over selected text - how to prevent this?
Window 10のVS2013でも確かに再現したので(そんなには目立たないのですが...)
上記のサイトを参考にコードを検証したところちらつきはなくなりました。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } } class RichBox : RichTextBox { [DllImport("user32.dll")] public static extern int SetCursor(IntPtr cursor); private const int WM_SETCURSOR = 0x20; protected override void WndProc(ref Message m) { if (m.Msg == WM_SETCURSOR) { SetCursor(Cursors.Hand.Handle); m.Result = new IntPtr(1); return; } base.WndProc(ref m); } } }
- 回答としてマーク nekko-iron 2016年2月26日 8:57
-
gekkaさん、ありがとうございます。
教えていただいたプログラムを確認した所、目的のアイコンとは異なるカーソルでRichTextBox内に入った場合、
(例としてすぐ横のRichTextBoxはデフォルトのカーソルを表示させる設定で、そこからカーソルを変更したRichTextBoxにマウスを移動させた時)
カーソルが前のコントロールに従ってしまうという問題点がありますので、このような処理を追加する事にしました。
private Cursor mCursor; public Cursor SetCursor { set { mCursor = value; } get { return mCursor; } } protected override void OnMouseEnter(EventArgs e) { this.Cursor = mCursor; base.OnMouseEnter(e); }
アイコンを設定する時は「SetCursor」にアイコンを指定する事で対応します。
動作には問題ありませんでしたので、このような形で使用させていただきます。
kenjinoteさんの回答もそのまま引用して問題ないものでしたが、APIを廃止するというFrameworkの方針を考えて今回はgekkaさんの方法を使用させていただきます。- 編集済み nekko-iron 2016年2月26日 9:06