【问题标题】:Direct2D / GDI+ and slow Windows forms drawing - What can be done?Direct2D / GDI+ 和缓慢的 Windows 窗体绘图 - 可以做什么?
【发布时间】:2011-04-13 16:52:05
【问题描述】:

我经常使用 Visual Studio 2008、.NET C# 2.0-3.5 和 Windows 窗体,并且我注意到,像我之前的许多人一样,GDI+ 在绘制控件时非常慢。 注意,我不太会处理图片(JPG、GIF 等)。图像仅在某些地方作为图标。这实际上是绘制缓慢的控件/表单/等。

问题是您可以看到正在绘制的控件,并且可能需要几秒钟才能绘制一组看似简单的控件。即,它的滞后和可怕。

我已经进行了测试,我只是在一个表单上放置了一些标签 (40-50),按 F5 运行并且必须等待它们被绘制。再次,滞后,不是一个很好的体验。

那么,有 WPF 可以解决这个问题,但我/我们还没有准备好迁移到 WPF。因此,我正在四处寻找解决方法或修复程序,并且在阅读其他一些库时偶然发现了 Direct2D。

让我有点困惑,因此这些问题:

1) 首先,我想要的是一种相当简洁的方法,可以用更快的硬件加速方法替换 GDI+。 是否可以在不使用 WPF 且无需重写我所有的 Windows 窗体代码的情况下做到这一点?

每当我在 Direct2D 上阅读任何内容时,我都会看到一长串通常很糟糕的 C++ 代码,告诉我如何手动编写代码以进行绘图。我不想那样。

2) 在网上阅读时,我偶然发现了 SlimDX,但我不知道如何使用它(我承认,在写作时我还没有尝试过很多)。假设我已经有一个 GUI 应用程序(Windows 窗体,标准 C# 代码)——我可以以某种方式使用 SlimDX(或类似的东西)来“替换”GDI+ 而无需过多重写吗?

我的问题是我找不到任何示例或类似的东西告诉我是否可以在我已经创建的 Windows 窗体软件中使用 SlimDX、Direct2D 或其他类似的东西,如果可以的话 - 怎么做。

希望我不要太模糊 =)

==EDIT== 2010-09-22

我已经在我的真实应用中进行了一些测试,并将其中一件缓慢的事情隔离出来:

当我将文本添加到用户控件中的某些标签时,控件会自行调整大小以适应文本。例如,包含 GroupControl 会稍微适应刚刚添加到 Labels 的 .Text-property 的文本的大小。

大约有 10 个标签控件。第一次更新标签并因此更改大小时,整个过程大约需要 500 毫秒。第二次更新标签,no大小发生变化,大约需要0毫秒。

==编辑 2== 2010-09-22

发现其中一个减速。然后向 Text-property 添加一个字符串,如果添加的文本在字符串 length 中与更新前的文本不同,则会很慢。

我正在使用 DevExpress 库,并且可以将 LabelControls 设置为 AutoSizeMode。如果我将其设置为“无”,那么当添加长度与前一个文本不同的文本时,延迟就会消失。我猜这个问题对于普通的标签控件来说是一样的,因为它也有一个 AutoSize = true/false 设置。

但是,它是一种“解决方法”,但仍然证明了我的观点 - 调整大小时它真的很慢,这很蹩脚。

【问题讨论】:

  • Ted,你能发布一些简单的 Winforms 示例项目来展示你的性能问题吗?
  • 看不到如何在这里发布文件。但我尝试的是创建 1 个表单,在该表单上我放置 1 个 TabControl 和 3 页。我将使用标签、文本框和组合框的每一页(每个 tabPage 200 个控件)。然后我只是运行它,单击/在页面之间切换,你可以看到它是如何滞后的。在普通应用程序中也是如此,通常情况更糟 =(
  • 我正在使用 DevExpress 库 - 那么第一个尝试的测试是使用标准的内置 Windows 控件,看看它们是否更快。恕我直言,您使用第三方控件库的事实应该在您的第一段中! [我现在添加了适当的 devexpress 标签,希望能更明显。]

标签: c# gdi direct2d slimdx devexpress-windows-ui


【解决方案1】:

上面的许多海报都有优点。我自己在 GDI+ 中创建了一个 3D CAD 应用程序,并且发现如果正确实施它足够快。然而,使用控件立即让我觉得这是一种非常尴尬的做事方式。控件是一个相当大的对象,在这种情况下有很多理由自己制作。

我建议您查看retained mode drawing system。它很容易实施,并且在大多数情况下可以涵盖您的情况。你必须自己创建绘图逻辑,但这很有趣,并且会给你更多的灵活性:)

【讨论】:

  • 似乎那个人在 2014 年离开了技术领域:bobpowelldotnet.blogspot.co.uk
  • 遗憾的是,鲍勃鲍威尔几年前去世了。他的网站几乎是 GDI+ 编程的最佳资源,但不幸的是,我认为任何地方都没有他的档案的完整副本。
  • 这在几个层面上都很难过。
  • 谢天谢地,互联网档案馆有一些他在被无灵魂的互联网吸血鬼接管之前的工作备份:web.archive.org/web/20120826121342/http://www.bobpowell.net/…
【解决方案2】:

关于你的第一个问题,我不得不使用 GDI 来做一些在 GDI+ 下需要很长时间的图像处理工作。这是 4 到 5 年前的事了,使用托管 C# 与 GDI 一起工作是一件痛苦的事——不知道它现在发生了多大的变化。有许多好的和快速的函数,例如 BitBlt,它们的绘制速度非常快,但是您需要非常小心地释放资源(句柄)和内存。我还有另一个问题,就是将结果保存为 JPEG,它在 GDI 中不存在,所以我不得不使用 CxImage 读取 HBitmap 然后保存它。

总而言之,GDI 非常快速且强大。如果 DirectX 中有更好的抽象,可能你最好使用它们。

【讨论】:

  • 谢谢。好吧,我不怎么处理图片。其绘制普通的 Windows 窗体控件,启动窗体等很慢。 GDI 快吗?我到处读到它不是?
【解决方案3】:

我正在研究一些相同的问题。我正在编写一个需要能够非常有效地渲染 2d 图形的应用程序,因为某些用户可以同时打开 10-50 个窗口。需要考虑的一件事是,这里没有其他人谈论过,direct2d 只能在带有 Service Pack 2 及更高版本的 Vista 计算机上使用。另外,根据这个链接:

http://www.tomshardware.com/news/msft-windows-xp-windows-7-market-share-win7,13876.html

截至 2011 年 11 月,所有 Windows 用户中有 38.5% 仍在使用 XP。因此,如果将应用程序出售给仍在运行 XP 的大量用户是一个问题(或者您的市场基础是主要使用 XP 的第三世界国家/地区) XP),那么您应该选择:

  1. Direct2d 适用于较新的操作系统,GDI+ 适用于 XP 系统。

  2. XNA - 与 XP 兼容,也可用于较新的操作系统。见此链接:http://msdn.microsoft.com/en-us/library/bb203925.aspx

  3. SlimDX - 在第一个答案中提到。支持 XP 以及更新的操作系统。看: http://slimdx.org/http://slimdx.org/features.php

  4. OpenTK 如果您关心 Windows、Linux、Max 等之间的兼容性。

您还应该知道,GDI+ 存在一个错误,导致它在最初发布时性能非常差。请参阅以下链接,为什么一些开发人员声称微软为使用 GDI+ 的应用破坏了 Windows7 的 gui:

http://www.windows7taskforce.com/view/3607

或使用以下字符串从您最喜欢的搜索引擎进行网络搜索:“gdi+ bug slow on windows 7”。

【讨论】:

    【解决方案4】:

    您可以尝试托管 directx,但他们不再支持它(移至 XNA)。老实说,除非你有一台糟糕的电脑或大量的控件,否则我不知道为什么它会滞后这么糟糕。如果您在主线程上执行一些 CPU 密集型工作,请将其移至单独的线程。这是我能想到会导致这种滞后的唯一其他原因。

    【讨论】:

    • 我有一个 Intel 920 2.6 Ghz 和 6 GB 内存,运行 om Win7 64 位,可用内存和无聊的 CPU。不,它不是计算机 =) 只是在 TabControl 中添加一堆标签、文本框的测试项目显示了问题。切换选项卡时它非常明显地滞后(并且 tabPages 包含数百个标签或文本框或其他)....
    【解决方案5】:

    我们在 C# 应用程序中使用 SlimDX....但我们实际上是在做 3D。我们编写了自己的 2D 库来进行简单的 2D 绘图。 SlimDX 只是 DirectX 的轻量级包装器。因此,您将了解 DirectX 的所有优点和缺点。像这样,如果它不存在,模拟视频卡是你的问题。

    如果您想要绘制到屏幕外位图的东西,我会选择 WPF,因为它与 C# 很好地集成,几乎可以在任何地方工作,并且在有可用硬件时加速。您可以将输出复制到位图并在常规 GDI/Winforms 中使用。但是如果你做相当复杂的事情(大量的过滤器,混合纹理等......),它只会比 GDI+ 更快。

    编辑:

    为了响应 cmets,我构建了一个小样本表单。第一次切换时有几秒钟的等待时间,但之后它会响应。比我预期的要慢,但无论如何都可以使用。如果这是关于他在应用程序中看到的性能,希望 Ted 发表评论。

    public class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
    
            // *** EDIT ***
            this.tabPage1.SuspendLayout();
            this.tabPage2.SuspendLayout();
            this.tabControl1.SuspendLayout();
            this.SuspendLayout();
    
            FillTab(tabPage1, Color.White);
            FillTab(tabPage2, Color.Yellow);
    
            // *** EDIT ***
            this.tabPage1.ResumeLayout(false);
            this.tabPage2.ResumeLayout(false);
            this.tabControl1.ResumeLayout(false);
            this.ResumeLayout(false);
        }
    
        private static void FillTab(TabPage tabPage, Color color)
        {
            for (int i = 0; i < 200; ++ i)
            {
                int left = (i % 5) * 320;
                int topOffset = (i / 5) * 23;
                tabPage.Controls.Add(new Label { Left = left, Top = topOffset, Width = 100, Text = "Label" });
                tabPage.Controls.Add(new TextBox() { BackColor = color, Left = left + 100, Top = topOffset, Width = 100, Text = tabPage.Text });
                tabPage.Controls.Add(new ComboBox { BackColor = color, Left = left + 200, Top = topOffset, Width = 100 });
            }
    
        }
    
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
    
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
    
        #region Windows Form Designer generated code
    
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.tabControl1 = new System.Windows.Forms.TabControl();
            this.tabPage1 = new System.Windows.Forms.TabPage();
            this.tabPage2 = new System.Windows.Forms.TabPage();
            this.tabControl1.SuspendLayout();
            this.SuspendLayout();
            // 
            // tabControl1
            // 
            this.tabControl1.Controls.Add(this.tabPage1);
            this.tabControl1.Controls.Add(this.tabPage2);
            this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tabControl1.Location = new System.Drawing.Point(0, 0);
            this.tabControl1.Name = "tabControl1";
            this.tabControl1.SelectedIndex = 0;
            this.tabControl1.Size = new System.Drawing.Size(292, 266);
            this.tabControl1.TabIndex = 0;
            // 
            // tabPage1
            // 
            this.tabPage1.Location = new System.Drawing.Point(4, 22);
            this.tabPage1.Name = "tabPage1";
            this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage1.Size = new System.Drawing.Size(284, 240);
            this.tabPage1.TabIndex = 0;
            this.tabPage1.Text = "tabPage1";
            this.tabPage1.UseVisualStyleBackColor = true;
            // 
            // tabPage2
            // 
            this.tabPage2.Location = new System.Drawing.Point(4, 22);
            this.tabPage2.Name = "tabPage2";
            this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage2.Size = new System.Drawing.Size(245, 217);
            this.tabPage2.TabIndex = 1;
            this.tabPage2.Text = "tabPage2";
            this.tabPage2.UseVisualStyleBackColor = true;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Controls.Add(this.tabControl1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.tabControl1.ResumeLayout(false);
            this.ResumeLayout(false);
    
        }
    
        #endregion
    
        private System.Windows.Forms.TabControl tabControl1;
        private System.Windows.Forms.TabPage tabPage1;
        private System.Windows.Forms.TabPage tabPage2;
    }
    

    【讨论】:

    • 我不认为我在关注。离屏位图?嗯,我只是希望我的用户控件/表单上的控件能够快速绘制并且没有滞后。你是说 GDI+ 的速度一样快吗?因为它真的很慢。我刚刚尝试添加大约 160 个 TextBoxes otno 一个空表单,并且您可以在重绘该区域时延迟...
    • @Ted:在这种情况下,WPF 可能不合适。你能发布一些代码来展示你对这些控件做了什么吗? 160 个空文本框,即 160 个矩形,在任何技术中都应该很快。
    • 嘿,正如我所说,WPF 不是一个选项,因为它需要大量重写,而这不是一个选项。代码很简单,一个新的 Windows 窗体项目,1 个 TabControl,其中包含大约 160-180 个文本框。在下一个 TabPage 上大约有 200-300 个 LabelControl,在下一个 TabPage 上大约有 200 个 ComboBox。如果你再改变 TabPage 你可以清楚地看到效果。谢谢。
    • 200 个文本框/组合框?!您确定使用数据网格类型的控件或其他东西不是更好吗?因为我质疑在一个表单上拥有这么多控件的价值。
    • @Alex 这不是重点。它只是一个显示它有多慢的例子。那只是一个测试项目。在我的普通应用程序中,我们使用了很多不同的控件,而且速度非常慢,让我想哭。
    猜你喜欢
    • 2014-11-28
    • 2012-10-28
    • 2011-05-15
    • 2015-01-25
    • 2019-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-22
    相关资源
    最近更新 更多