単一の ListViewItem のテキストを更新するときに ListView のちらつきを防ぐにはどうすればよいですか?
質問
私が望むのは、ちらつきを確認せずに ListViewItem のテキストを更新することだけです。
これは更新用のコードです(数回呼び出されています)。
listView.BeginUpdate();
listViewItem.SubItems[0].Text = state.ToString(); // update the state
listViewItem.SubItems[1].Text = progress.ToString(); // update the progress
listView.EndUpdate();
コンポーネントのオーバーライドを含むいくつかの解決策を見てきました。 WndProc():
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WM.WM_ERASEBKGND)
{
m.Msg = (int)IntPtr.Zero;
}
base.WndProc(ref m);
}
問題は解決すると言われていますが、私の場合は解決しませんでした. 。これはすべての項目にアイコンを使用しているためだと思います。
解決 2
この質問を終わらせるために、フォーム内の各 ListView または他の ListView の派生コントロールに対してフォームが読み込まれるときに呼び出されるヘルパー クラスを次に示します。解決策を提供してくれた「Brian Gillespie」に感謝します。
public enum ListViewExtendedStyles
{
/// <summary>
/// LVS_EX_GRIDLINES
/// </summary>
GridLines = 0x00000001,
/// <summary>
/// LVS_EX_SUBITEMIMAGES
/// </summary>
SubItemImages = 0x00000002,
/// <summary>
/// LVS_EX_CHECKBOXES
/// </summary>
CheckBoxes = 0x00000004,
/// <summary>
/// LVS_EX_TRACKSELECT
/// </summary>
TrackSelect = 0x00000008,
/// <summary>
/// LVS_EX_HEADERDRAGDROP
/// </summary>
HeaderDragDrop = 0x00000010,
/// <summary>
/// LVS_EX_FULLROWSELECT
/// </summary>
FullRowSelect = 0x00000020,
/// <summary>
/// LVS_EX_ONECLICKACTIVATE
/// </summary>
OneClickActivate = 0x00000040,
/// <summary>
/// LVS_EX_TWOCLICKACTIVATE
/// </summary>
TwoClickActivate = 0x00000080,
/// <summary>
/// LVS_EX_FLATSB
/// </summary>
FlatsB = 0x00000100,
/// <summary>
/// LVS_EX_REGIONAL
/// </summary>
Regional = 0x00000200,
/// <summary>
/// LVS_EX_INFOTIP
/// </summary>
InfoTip = 0x00000400,
/// <summary>
/// LVS_EX_UNDERLINEHOT
/// </summary>
UnderlineHot = 0x00000800,
/// <summary>
/// LVS_EX_UNDERLINECOLD
/// </summary>
UnderlineCold = 0x00001000,
/// <summary>
/// LVS_EX_MULTIWORKAREAS
/// </summary>
MultilWorkAreas = 0x00002000,
/// <summary>
/// LVS_EX_LABELTIP
/// </summary>
LabelTip = 0x00004000,
/// <summary>
/// LVS_EX_BORDERSELECT
/// </summary>
BorderSelect = 0x00008000,
/// <summary>
/// LVS_EX_DOUBLEBUFFER
/// </summary>
DoubleBuffer = 0x00010000,
/// <summary>
/// LVS_EX_HIDELABELS
/// </summary>
HideLabels = 0x00020000,
/// <summary>
/// LVS_EX_SINGLEROW
/// </summary>
SingleRow = 0x00040000,
/// <summary>
/// LVS_EX_SNAPTOGRID
/// </summary>
SnapToGrid = 0x00080000,
/// <summary>
/// LVS_EX_SIMPLESELECT
/// </summary>
SimpleSelect = 0x00100000
}
public enum ListViewMessages
{
First = 0x1000,
SetExtendedStyle = (First + 54),
GetExtendedStyle = (First + 55),
}
/// <summary>
/// Contains helper methods to change extended styles on ListView, including enabling double buffering.
/// Based on Giovanni Montrone's article on <see cref="http://www.codeproject.com/KB/list/listviewxp.aspx"/>
/// </summary>
public class ListViewHelper
{
private ListViewHelper()
{
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr handle, int messg, int wparam, int lparam);
public static void SetExtendedStyle(Control control, ListViewExtendedStyles exStyle)
{
ListViewExtendedStyles styles;
styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
styles |= exStyle;
SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
}
public static void EnableDoubleBuffer(Control control)
{
ListViewExtendedStyles styles;
// read current style
styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
// enable double buffer and border select
styles |= ListViewExtendedStyles.DoubleBuffer | ListViewExtendedStyles.BorderSelect;
// write new style
SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
}
public static void DisableDoubleBuffer(Control control)
{
ListViewExtendedStyles styles;
// read current style
styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
// disable double buffer and border select
styles -= styles & ListViewExtendedStyles.DoubleBuffer;
styles -= styles & ListViewExtendedStyles.BorderSelect;
// write new style
SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
}
}
他のヒント
受け入れられた回答は機能しますが、非常に長く、ダブルバッファリングを有効にするためだけにコントロールから(他の回答で述べたように)派生するのも少しやりすぎです。しかし幸いなことに、私たちはリフレクションを持っており、必要に応じて内部メソッドを呼び出すこともできます (ただし、何を行うかは必ず確認してください)。
このアプローチを拡張メソッドにカプセル化すると、非常に短いクラスが得られます。
public static class ControlExtensions
{
public static void DoubleBuffering(this Control control, bool enable)
{
var method = typeof(Control).GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic);
method.Invoke(control, new object[] { ControlStyles.OptimizedDoubleBuffer, enable });
}
}
これはコード内で簡単に呼び出すことができます。
InitializeComponent();
myListView.DoubleBuffering(true); //after the InitializeComponent();
そしてちらつきはすべて消えました。
アップデート
つまずいた この質問 この事実により、拡張メソッドは (おそらく) 次のようにする方がよいでしょう。
public static void DoubleBuffered(this Control control, bool enable)
{
var doubleBufferPropertyInfo = control.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
doubleBufferPropertyInfo.SetValue(control, enable, null);
}
CommonControls 6 (XP 以降) の ListView はダブル バッファリングをサポートします。幸いなことに、.NET はシステム上の最新の CommonControls をラップします。ダブルバッファリングを有効にするには、適切な Windows メッセージを ListView コントロールに送信します。
詳細は次のとおりです。http://www.codeproject.com/KB/list/listviewxp.aspx
.NET Winforms 2.0 には、DoubleBuffered と呼ばれる保護されたプロパティが存在します。
ListView から継承することにより、この protected プロパティを true に設定できます。これにより、SendMessage を呼び出す必要がなく、二重バッファリングが有効になります。
DoubleBuffered プロパティの設定は、次のスタイルの設定と同じです。
listview.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
http://connect.microsoft.com/VisualStudio/フィードバック/ViewFeedback.aspx?FeedbackID=94096
この質問がかなり古い質問であることは承知していますが、これは Google で最初に検索された結果の 1 つであるため、私の修正を共有したいと思いました。
ちらつきを100%除去できる唯一の方法は、オリバーからの回答(ダブルバッファリングを備えた拡張クラス)を組み合わせて、 BeignUpdate()
そして EndUpdate()
メソッド。
どちらも単独ではちらつきを修正できませんでした。確かに、私は非常に複雑なリストを使用しているため、リストにプッシュする必要があり、ほぼ毎秒更新する必要もあります。
これは役に立ちます:
class DoubleBufferedListView : System.Windows.Forms.ListView
{
public DoubleBufferedListView()
:base()
{
this.DoubleBuffered = true;
}
}
簡単な解決策は次のとおりです。
yourlistview.BeginUpdate()
// リストへの項目の追加と削除の更新を行います
yourlistview.EndUpdate()
テキストのみを更新したい場合は、ListViewItem 全体を更新するのではなく、変更された SubItem のテキストを直接設定します (更新方法については述べていません)。
あなたが示すオーバーライドは、単に OnPaintBackground をオーバーライドすることと同等であり、これはそのタスクを実行するための「より正確な」管理方法であり、単一の項目には役に立ちません。
それでも問題が解決しない場合は、実際に試したことについて説明する必要があります。
これは暗闇の中でのショットですが、コントロールをダブルバッファリングしてみることもできます。
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer, true)
リスト ビュー項目を設定する前に ListView で BeginUpdate() メソッドを呼び出し、すべての項目が追加された後でのみ EndUpdate() メソッドを呼び出します。
そうすればちらつきは止まります。