我有一个定期更新的列表视图(每60秒)。对我来说很烦人,每当它过时都会闪烁。使用的方法是清除所有项目,然后重新创建它们。我决定不使用新文本清除直接写入单元格的项目。这是一种更好的方法,还是有人有更好的解决方案。
答案 0 :(得分:88)
ListView控件有一个闪烁问题。问题似乎是控件的Update重载不正确地实现,使其像Refresh一样运行。更新应该使控件仅重绘其无效区域,而刷新重绘控件的整个客户区域。因此,如果您要更改列表中某个项目的背景颜色,则只需要重新绘制该特定项目。不幸的是,ListView控件似乎有不同的意见,并且想要在你弄乱单个项目时重新绘制整个表面......即使当前没有显示该项目。所以,无论如何,您可以通过滚动自己来轻松抑制闪烁,如下所示:
class ListViewNF : System.Windows.Forms.ListView
{
public ListViewNF()
{
//Activate double buffering
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
this.SetStyle(ControlStyles.EnableNotifyMessage, true);
}
protected override void OnNotifyMessage(Message m)
{
//Filter out the WM_ERASEBKGND message
if(m.Msg != 0x14)
{
base.OnNotifyMessage(m);
}
}
}
答案 1 :(得分:22)
除了其他回复之外,许多控件都有一个[Begin|End]Update()
方法,可用于在编辑内容时减少闪烁 - 例如:
listView.BeginUpdate();
try {
// listView.Items... (lots of editing)
} finally {
listView.EndUpdate();
}
答案 2 :(得分:5)
是的,让它双倍缓冲。它会减少闪烁;)http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.doublebuffered.aspx
答案 3 :(得分:4)
很好的问题和Stormenent的回答很明显。这是他的代码的C ++端口,可供其他任何可能正在处理C ++ / CLI实现的人使用。
#pragma once
#include "Windows.h" // For WM_ERASEBKGND
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
public ref class FlickerFreeListView : public ListView
{
public:
FlickerFreeListView()
{
//Activate double buffering
SetStyle(ControlStyles::OptimizedDoubleBuffer | ControlStyles::AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
SetStyle(ControlStyles::EnableNotifyMessage, true);
}
protected:
virtual void OnNotifyMessage(Message m) override
{
//Filter out the WM_ERASEBKGND message
if(m.Msg != WM_ERASEBKGND)
{
ListView::OnNotifyMessage(m);
}
}
};
答案 4 :(得分:4)
如果这可以提供帮助,则以下组件解决了我使用.NET 3.5的ListView闪烁问题
[ToolboxItem(true)]
[ToolboxBitmap(typeof(ListView))]
public class ListViewDoubleBuffered : ListView
{
public ListViewDoubleBuffered()
{
this.DoubleBuffered = true;
}
}
我将它与.BeginUpdate()和.EndUpdate()方法结合使用,我在那里进行ListView.Items操作。
我不明白为什么这个属性是受保护的...即使在.NET 4.5中(可能是安全问题)
答案 5 :(得分:4)
这是我对C#实现的快速修复,它不需要对列表视图进行子类化等。
使用反射将DoubleBuffered属性设置为在表单构造函数中尝试。
lvMessages
.GetType()
.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.SetValue(lvMessages, true, null);
答案 6 :(得分:3)
最简单的解决方案可能是使用
listView.Items.AddRange(listViewItems.ToArray());
而不是
foreach (ListViewItem listViewItem in listViewItems)
{
listView.Items.Add(listViewItem);
}
这种方式更好。
答案 7 :(得分:2)
您可以使用以下扩展程序类将DoubleBuffered
属性设置为true
:
using System.Reflection;
public static class ListViewExtensions
{
public static void SetDoubleBuffered(this ListView listView, bool value)
{
listView.GetType()
.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(listView, value);
}
}
答案 8 :(得分:1)
简单解决方案
yourlistview.BeginUpdate()
//Do your update of adding and removing item from the list
yourlistview.EndUpdate()
答案 9 :(得分:0)
尝试将double buffered属性设置为true。
你也可以使用:
this.SuspendLayout();
//update control
this.ResumeLayout(False);
this.PerformLayout();
答案 10 :(得分:0)
我知道这是一个非常的问题和答案。然而,这是搜索“C ++ / cli listview flicker”时的最佳结果 - 尽管事实上这甚至都不是在讨论C ++。所以这是C ++版本:
我把它放在主表单的头文件中,你可以选择将它放在其他地方......
static void DoubleBuffer(Control^ control, bool enable) {
System::Reflection::PropertyInfo^ info = control->GetType()->
GetProperty("DoubleBuffered", System::Reflection::BindingFlags::Instance
| System::Reflection::BindingFlags::NonPublic);
info->SetValue(control, enable, nullptr);
}
如果你碰巧在这里寻找托管C ++的类似答案,那对我有用。 :)
答案 11 :(得分:0)
在Winrt Windows Phone 8.1中,您可以设置以下代码来解决此问题。
<ListView.ItemContainerTransitions>
<TransitionCollection/>
</ListView.ItemContainerTransitions>
答案 12 :(得分:0)
对于它的价值,在我的情况下,我只需要添加一个对
的调用Application.EnableVisualStyles()
在运行应用程序之前,如下所示:
private static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
否则,双缓冲是不够的。也许这是一个非常古老的项目,默认情况下新的设置......
答案 13 :(得分:0)
这对我来说效果最好。
由于您是直接编辑单元格,因此,最好的解决方案是简单地刷新/重新加载该特定单元格/行,而不是整个表。
您可以使用RedrawItems(...)
方法,该方法基本上只重新绘制列表视图中指定范围的项目/行。
public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly);
Reference
对我来说,这完全摆脱了整个列表视图闪烁。
在更新时,只有相关的项目/记录闪烁。
干杯!
答案 14 :(得分:0)
如果仍然有人会对此做出回答,我使用了一个计时器稍作延迟,它很好地解决了问题。我想突出显示鼠标移动事件时整行的颜色(更改颜色),但是我认为它可以用于项目替换等。对我来说,listView.BeginUpdate()和listView.EndUpdate()无效,DoubleBuffered属性也无效没用,我在Google上搜索了很多,但没用。
private int currentViewItemIndex;
private int lastViewItemIndex;
private void listView_MouseMove(object sender, MouseEventArgs e)
{
ListViewItem lvi = listView.GetItemAt(e.X, e.Y);
if (lvi != null && lastViewItemIndex == -1)
{
listView.Items[lvi.Index].BackColor = Color.Green;
lastViewItemIndex = lvi.Index;
}
if (lvi != null && lastViewItemIndex != -1)
{
currentViewItemIndex = lvi.Index;
listViewTimer.Start();
}
}
private void listViewTimer_Tick(object sender, EventArgs e)
{
listView.BeginUpdate();
listView.Items[lastViewItemIndex].BackColor = Colour.Transparent;
listView.Items[currentViewItemIndex].BackColor = Colour.Green;
listView.EndUpdate();
lastViewItemIndex = currentViewItemIndex;
listViewTimer.Stop();
}