MrC*_*rCC 5 .net c# listview rendering winforms
更新1:我同时编写了MFC-C ++实现和老式的Win32应用程序,并录制了一段视频,演示了问题的严重程度:
https://www.youtube.com/watch?v=f0CQhQ3GgAM
由于老式的Win32应用程序没有出现此问题,因此使我相信C#和MFC都使用必须导致此问题的同一呈现API(基本上是怀疑我的问题可能出在OS /图形驱动程序级别)。
原始帖子:
当不得不在ListView中显示一些REST数据时,我遇到了一个非常特殊的问题:
对于某些输入,在水平滚动时,ListView渲染在字面上会缓慢地进行爬网。
在我的系统上,使用典型的带有“ OptimizedDoubleBuffer”的子类ListView,在ListView中只有6个项目时,在滚动到我可以看到标题“游动”的点时,渲染速度会变慢,即在编译过程中项目和标题的渲染滚动不匹配。
对于具有10个项目的常规非子类ListView,我可以从字面上看到滚动时分别绘制每个项目(重新绘制大约需要1-2s)。
这是示例代码(是的,我知道它们看起来像熊和蝴蝶的表情;毕竟,从用户提供的数据中发现了这个问题):
using System;
using System.Windows.Forms;
namespace SlowLVRendering
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new System.EventHandler(this.Form1_Load);
}
private void Form1_Load(object sender, EventArgs e)
{
const string slow = "?( ´??°)? ?????~ ? ( ?( ´??°)? ? ?´º?º ?? ) (????)??( ´??°)? ?????~ ? ( ?( ´??°)? ? ?´º?º ?? ) (????)?";
ListView lv = new ListView();
lv.Dock = DockStyle.Fill;
lv.View= View.Details;
for (int i = 0; i < 2; i++) lv.Columns.Add("Title "+i, 500);
for (int i = 0; i < 10; i++)
{
var lvi = lv.Items.Add(slow);
lvi.SubItems.Add(slow);
}
Controls.Add(lv);
}
}
}
Run Code Online (Sandbox Code Playgroud)
有人可以解释这个问题是什么以及如何解决?
我相信我已经将问题缩小到Visual Styles。注释掉Application.EnableVisualStyles();
会static void Main
在滚动过程中带来巨大的性能提升,尽管与我在更新 1 中提到的视频中所示的 Win32 应用程序的性能相差甚远。
当然,这样做的缺点是应用程序中的所有控件都会看起来“旧”。因此,我尝试通过以下方式选择性地禁用/启用视觉样式
[DllImport("uxtheme", ExactSpelling = true, CharSet = CharSet.Unicode)]
public extern static Int32 SetWindowTheme(IntPtr hWnd, String textSubAppName, String textSubIdList);
Run Code Online (Sandbox Code Playgroud)
并Win32.SetWindowTheme(lv.Handle, " ", " ");
按照 MSDN 文档中的描述使用。最合乎逻辑的事情是让大多数控件的视觉样式保持活动状态,并在性能关键的控件中关闭它。然而,ListView的一部分似乎故意忽略视觉样式是禁用还是启用,即报表模式下列表视图的列标题:
(注意列标题与滚动条相比的外观)
因此,除非有人知道如何在列表视图列标题上强制关闭视觉样式,否则这是一种“选择你的毒药”的情况:要么注释掉 Application.EnableVisualStyles(); 并且有一个丑陋的用户界面,或者保留它并冒着不可预测的渲染器速度减慢的风险。
如果你选择第一个选择,你可以通过子类化 ListView 并短路 WM_REFLECT_NOTIFY 消息来获得另一个巨大的性能提升(感谢 SteveFerg 的原创):
public class ListViewWithoutReflectNotify : ListView
{
[StructLayout(LayoutKind.Sequential)]
private struct NMHDR
{
public IntPtr hwndFrom;
public uint idFrom;
public uint code;
}
private const uint NM_CUSTOMDRAW = unchecked((uint) -12);
public ListViewWithoutReflectNotify()
{
}
protected override void WndProc(ref Message m)
{
// WM_REFLECT_NOTIFY
if (m.Msg == 0x204E)
{
m.Result = (IntPtr)0;
return;
//the if below never was true on my system so i 'shorted' it
//delete the 2 lines above if you want to test this yourself
NMHDR hdr = (NMHDR) m.GetLParam(typeof (NMHDR));
if (hdr.code == NM_CUSTOMDRAW)
{
Debug.WriteLine("Hit");
m.Result = (IntPtr) 0;
return;
}
}
base.WndProc(ref m);
}
}
Run Code Online (Sandbox Code Playgroud)
禁用视觉样式和子类化可以使渲染速度几乎与 Win32 C 应用程序相当。但是,我不完全理解缩短 WM_REFLECT_NOTIFY 的潜在后果,因此请谨慎使用。
我还检查了 Win32 应用程序,并确认您可以通过添加清单来真正降低应用程序的渲染性能,例如如下所示:
// enable Visual Styles
#pragma comment( linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
681 次 |
最近记录: |