C# 更新单个 ListViewItem 的文本时如何防止在 ListView 中闪烁?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/87795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to prevent flickering in ListView when updating a single ListViewItem's text?
提问by Jonas
All I want is to update an ListViewItem's text whithout seeing any flickering.
我想要的只是更新 ListViewItem 的文本而不会看到任何闪烁。
This is my code for updating (called several times):
这是我的更新代码(多次调用):
listView.BeginUpdate();
listViewItem.SubItems[0].Text = state.ToString(); // update the state
listViewItem.SubItems[1].Text = progress.ToString(); // update the progress
listView.EndUpdate();
I've seen some solutions that involve overriding the component's WndProc():
我见过一些涉及覆盖组件的解决方案 WndProc():
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WM.WM_ERASEBKGND)
{
m.Msg = (int)IntPtr.Zero;
}
base.WndProc(ref m);
}
They say it solves the problem, but in my case It didn't. I believe this is because I'm using icons on every item.
他们说它解决了问题,但在我的情况下它没有。我相信这是因为我在每个项目上都使用了图标。
采纳答案by Jonas
To end this question, here is a helper class that should be called when the form is loading for each ListView or any other ListView's derived control in your form. Thanks to "Brian Gillespie" for giving the solution.
为了结束这个问题,这里有一个助手类,当为表单中的每个 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);
}
}
回答by ctacke
If you only want to update the text, simply set the changed SubItem's text directly rather than updating the entire ListViewItem (you've not said how you're doing your updates).
如果您只想更新文本,只需直接设置更改后的 SubItem 的文本,而不是更新整个 ListViewItem(您还没有说明您是如何进行更新的)。
The override you show is equivalent to simply overriding OnPaintBackground, which would be a "more correct" managed way to do that task, and it's not going to help for a single item.
您显示的覆盖相当于简单地覆盖 OnPaintBackground,这将是执行该任务的“更正确”的管理方式,并且对单个项目没有帮助。
If you still have problems, we'll need clarification on what you've actually tried.
如果您仍然有问题,我们需要澄清您实际尝试过的内容。
回答by Ed S.
This is a shot in the dark, but you could try double buffering the control.
这是在黑暗中拍摄,但您可以尝试对控件进行双缓冲。
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer, true)
回答by Brian Gillespie
The ListView in CommonControls 6 (XP or newer) supports double buffering. Fortunately, .NET wraps the newest CommonControls on the system. To enable double buffering, send the appropriate Windows message to the ListView control.
CommonControls 6(XP 或更新版本)中的 ListView 支持双缓冲。幸运的是,.NET 在系统上封装了最新的 CommonControl。要启用双缓冲,请将适当的 Windows 消息发送到 ListView 控件。
Here are the details: http://www.codeproject.com/KB/list/listviewxp.aspx
回答by Rolf Kristensen
In .NET Winforms 2.0 there exist a protected property called DoubleBuffered.
在 .NET Winforms 2.0 中,存在一个名为 DoubleBuffered 的受保护属性。
By inheriting from ListView, then one can set this protected property to true. This will enable double buffering without needing to call SendMessage.
通过从 ListView 继承,可以将此受保护的属性设置为 true。这将启用双缓冲而无需调用 SendMessage。
Setting the DoubleBuffered property is the same as setting the following style:
设置 DoubleBuffered 属性与设置以下样式相同:
listview.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94096
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94096
回答by Bjoern
this will help:
这将有助于:
class DoubleBufferedListView : System.Windows.Forms.ListView
{
public DoubleBufferedListView()
:base()
{
this.DoubleBuffered = true;
}
}
回答by Mike
Call the BeginUpdate() method on the ListView before setting any of the list view items and then only call EndUpdate() after all of the items have been added.
在设置任何列表视图项之前调用 ListView 上的 BeginUpdate() 方法,然后仅在添加所有项后才调用 EndUpdate()。
That will stop the flicker.
这将停止闪烁。
回答by Oliver
The accepted answer works, but is quite lengthy, and deriving from the control (like mentioned in the other answers) just to enable double buffering is also a bit overdone. But fortunately we have reflection and can also call internal methods if we like to (but be sure what you do!).
接受的答案有效,但相当冗长,并且从控件(如其他答案中提到的那样)仅用于启用双缓冲也有点过头了。但幸运的是,我们有反射,如果我们愿意,也可以调用内部方法(但要确保你做什么!)。
Be encapsulating this approach into an extension method, we'll get a quite short class:
将这种方法封装到扩展方法中,我们将得到一个非常短的类:
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 });
}
}
Which can easily be called within our code:
可以在我们的代码中轻松调用:
InitializeComponent();
myListView.DoubleBuffering(true); //after the InitializeComponent();
And all flickering is gone.
所有闪烁都消失了。
Update
更新
I stumbled on this questionand due to this fact, the extension method should (maybe) better be:
我偶然发现了这个问题,由于这个事实,扩展方法应该(也许)更好:
public static void DoubleBuffered(this Control control, bool enable)
{
var doubleBufferPropertyInfo = control.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
doubleBufferPropertyInfo.SetValue(control, enable, null);
}
回答by jaiveeru
Simple solution is this:
简单的解决方案是这样的:
yourlistview.BeginUpdate()
yourlistview.BeginUpdate()
//Do your update of adding and removing item from the list
//更新列表中的添加和删除项目
yourlistview.EndUpdate()
yourlistview.EndUpdate()
回答by T4cC0re
I know this question is quite old, but because this is one of the first search results on Google I wanted to share my fix.
我知道这个问题已经很老了,但是因为这是 Google 上的第一个搜索结果之一,所以我想分享我的解决方案。
The only way i could remove flickering 100% was to combine the answer from Oliver (extension class with double-buffering) and using the BeignUpdate()
and EndUpdate()
methods.
我可以 100% 消除闪烁的唯一方法是结合 Oliver(具有双缓冲的扩展类)的答案并使用BeignUpdate()
和EndUpdate()
方法。
Neither of those on their own could fix flickering for me. Granted, I use a very complex list, that I need to push into the list and also need to update it almost every sec.
他们自己都无法为我解决闪烁问题。当然,我使用了一个非常复杂的列表,我需要推送到列表中,并且几乎每秒钟都需要更新它。