2012-05-14 28 views
6

Tôi có hai vấn đề với một điều khiển người dùng riêng trong đó sử dụng bitmap:riêng WinForms kiểm soát thấp thoáng và Có Bad Performance

  1. ERP sẽ hiển thị nếu nó vẽ lại thông qua phương pháp 'Refresh' NET của.
  2. Hiệu suất kém.

Việc kiểm soát bao gồm ba bitmap:

  • Một hình nền tĩnh.
  • rôto quay.
  • Một hình ảnh khác tùy thuộc vào góc rotor.

Tất cả bitmap đã sử dụng có độ phân giải 500x500 pixel. Bộ điều khiển hoạt động như sau: https://www.dropbox.com/s/t92gucestwdkx8z/StatorAndRotor.gif (đó là hình động gif)

Điều khiển người dùng sẽ tự vẽ mỗi khi có góc rotor mới. Do đó, nó có một tài sản công cộng 'RotorAngle' mà trông như thế này:

public double RotorAngle 
{ 
    get { return mRotorAngle; } 
    set 
    { 
     mRotorAngle = value; 
     Refresh(); 
    } 
} 

Refresh làm tăng sự kiện Paint. Handler OnPaint kiện trông như thế này:

private void StatorAndRotor2_Paint(object sender, PaintEventArgs e) 
{ 
    // Draw the three bitmaps using a rotation matrix to rotate the rotor bitmap. 
    Draw((float)mRotorAngle); 
} 

Nhưng khi tôi sử dụng mã này - mà hoạt động tốt trong điều khiển người dùng riêng khác - người dùng điều khiển không được rút ra ở tất cả nếu điều khiển được đôi đệm qua SetStyle(ControlStyles.OptimizedDoubleBuffer, true). Nếu tôi không đặt cờ này thành true, điều khiển sẽ nhấp nháy khi được vẽ lại.

Trong constructor kiểm soát tôi đặt:

SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.ContainerControl, false); 
// User control is not drawn if "OptimizedDoubleBuffer" is true. 
// SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 
SetStyle(ControlStyles.ResizeRedraw, true); 
SetStyle(ControlStyles.SupportsTransparentBackColor, true); 

Trước tiên, tôi nghĩ rằng nó thấp thoáng vì nền sẽ bị xóa mọi sự kiểm soát được rút ra. Do đó, tôi đặt SetStyle(ControlStyles.AllPaintingInWmPaint, true). Nhưng nó không giúp được gì.

Vì vậy, tại sao nó nhấp nháy? Các điều khiển khác hoạt động rất tốt với thiết lập này. Và tại sao điều khiển không được vẽ nếu SetStyle(ControlStyles.OptimizedDoubleBuffer, true).

tôi phát hiện ra rằng sự kiểm soát không nhấp nháy nếu tôi gọi phương thức Draw tôi trực tiếp sau khi thay đổi thuộc tính RotorAngle:

public float RotorAngle 
{ 
    get { return mRotorAngle; } 
    set 
    { 
     mRotorAngle = value; 
     Draw(mRotorAngle); 
    } 
} 

Nhưng kết quả này trong một buổi biểu diễn rất xấu, đặc biệt là ở chế độ toàn màn hình. Không thể cập nhật điều khiển sau mỗi 20 mili giây. Bạn có thể tự mình thử. Tôi sẽ đính kèm giải pháp Visual Studio 2008 hoàn chỉnh bên dưới.

Vì vậy, tại sao hiệu suất kém như vậy? Không có vấn đề gì khi cập nhật các điều khiển khác (riêng) sau mỗi 20 mili giây. Có thực sự là do ảnh bitmap không?

Tôi tạo ra một giải pháp trực quan Visual Studio 2008 đơn giản để chứng minh hai vấn đề: https://www.dropbox.com/s/mckmgysjxm0o9e0/WinFormsControlsTest.zip (289,3 KB)

Có một thực thi trong thư mục bin\Debug.

Cảm ơn sự giúp đỡ của bạn.

+0

tôi không thể đi vào tất cả các chi tiết bây giờ, nhưng bạn cần phải nhìn vào đôi đệm. May mắn thay nó rất đơn giản trong năm 2008 + Cách bạn đang làm việc làm mới trong tài sản là xấu quá. – Ian

+0

Vui lòng không đệm đôi nếu ứng dụng của bạn có thể được sử dụng trên máy tính từ xa - xem [liên kết này] (http://blogs.msdn.com/b/oldnewthing/archive/2006/01/03/508694.aspx) . Bạn có thể kiểm tra xem phiên hiện tại là máy tính từ xa với: 'if (SystemInformation.TerminalServerSession) ...' – Bridge

Trả lời

4

Thứ nhất, mỗi câu trả lời LarsTech, bạn nên sử dụng Graphics bối cảnh cung cấp trong PaintEventArgs. Bằng cách gọi CreateGraphics() bên trong trình xử lý Paint, bạn ngăn không cho OptimizedDoubleBuffer hoạt động chính xác.

Thứ hai, trong khối setStyle của bạn, thêm:

SetStyle(ControlStyles.Opaque, true); 

... để ngăn chặn sự kiểm soát lớp cơ sở từ điền vào màu nền trước khi gọi handler Sơn của bạn.

Tôi đã thử nghiệm điều này trong dự án ví dụ của bạn, nó dường như loại bỏ nhấp nháy.

2

Vẽ trên màn hình trên sự kiện Paint là thao tác nặng. Làm sơn trên bộ nhớ đệm tương đối rất nhanh.

Double buffering sẽ cải thiện hiệu suất. Thay vì vẽ trên đồ họa sơn, tạo ra một đồ họa mới, và làm tất cả các bản vẽ trên nó.

Khi vẽ bitmap được thực hiện, sao chép toàn bộ bitmap để e.Graphics nhận được từ PaintEventArgs

Flicker free drawing using GDI+ and C#

4

Không sử dụng CreateGraphics, nhưng sử dụng các đối tượng đồ họa đã được thông qua với bạn từ sự kiện sơn:

tôi đã thay đổi nó như vậy và thêm một rõ ràng kể từ khi thay đổi kích thước sẽ hiển thị hình ảnh ma quái:

private void Draw(float rotorAngle, Graphics graphics) 
{ 
    graphics.Clear(SystemColors.Control); 
    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 

    // yada-yada-yada 

    // do not dispose since you did not create it: 
    // graphics.Dispose(); 
} 

gọi từ:

private void StatorAndRotor2_Paint(object sender, PaintEventArgs e) 
{ 
    Draw((float)mRotorAngle, e.Graphics); 
} 

Trong constructor, bật đôi đệm, nhưng tôi nghĩ rằng tính minh bạch là không cần thiết:

SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.ContainerControl, false); 
SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 
SetStyle(ControlStyles.ResizeRedraw, true); 
//SetStyle(ControlStyles.SupportsTransparentBackColor, true); 
0

Đối với câu trả lời của tôi, tôi lấy cảm hứng từ https://stackoverflow.com/a/2608945/455904 và tôi đã ghi lại nội dung từ LarsTechs câu trả lời ở trên.

Để tránh phải tạo lại hình ảnh hoàn chỉnh trên tất cả các OnPaint, bạn có thể sử dụng biến để giữ hình ảnh được tạo.

private Bitmap mtexture; 

Sử dụng Vẽ() để tạo ra các kết cấu

private void Draw(float rotorAngle) 
{ 
    using (var bufferedGraphics = Graphics.FromImage(mtexture)) 
    { 
     Rectangle imagePosition = new Rectangle(0, 0, Width, Height); 
     bufferedGraphics.DrawImage(mStator, imagePosition); 
     bufferedGraphics.DrawImage(RotateImage(mRotor, mRotorAngle), imagePosition); 

     float normedAngle = mRotorAngle % cDegreePerFullRevolution; 

     if (normedAngle < 0) 
      normedAngle += cDegreePerFullRevolution; 

     if (normedAngle >= 330 || normedAngle <= 30) 
      bufferedGraphics.DrawImage(mLED101, imagePosition); 
     if (normedAngle > 30 && normedAngle < 90) 
      bufferedGraphics.DrawImage(mLED001, imagePosition); 
     if (normedAngle >= 90 && normedAngle <= 150) 
      bufferedGraphics.DrawImage(mLED011, imagePosition); 
     if (normedAngle > 150 && normedAngle < 210) 
      bufferedGraphics.DrawImage(mLED010, imagePosition); 
     if (normedAngle >= 210 && normedAngle <= 270) 
      bufferedGraphics.DrawImage(mLED110, imagePosition); 
     if (normedAngle > 270 && normedAngle < 330) 
      bufferedGraphics.DrawImage(mLED100, imagePosition); 
    } 
} 

Có ghi đè OnPaint vẽ kết cấu về kiểm soát của bạn

protected override void OnPaint(PaintEventArgs e) 
{ 
    base.OnPaint(e); 
    Rectangle imagePosition = new Rectangle(0, 0, Width, Height); 
    e.Graphics.DrawImage(mtexture, imagePosition); 
} 

Override OnInvalidated() để vẽ() kết cấu khi cần

protected override void OnInvalidated(InvalidateEventArgs e) 
{ 
    base.OnInvalidated(e); 

    if (mtexture != null) 
    { 
     mtexture.Dispose(); 
     mtexture = null; 
    } 

    mtexture = new Bitmap(Width, Height); 
    Draw(mRotorAngle); 
} 

Thay vì gọi Vẽ làm mất hiệu lực hình ảnh. Điều này sẽ làm cho nó tái sử dụng OnInvalidated và OnPaint.

public float RotorAngle 
{ 
    get { return mRotorAngle; } 
    set 
    { 
     mRotorAngle = value; 
     Invalidate(); 
    } 
} 

Tôi hy vọng tôi có tất cả của nó trong đó :)

0

Cảm ơn bạn rất nhiều vì sự giúp đỡ của bạn.

Nhấp nháy được giải quyết. :)

Bây giờ, tôi theo dõi LarsTech's đề xuất và sử dụng đối tượng Graphics từ PaintEventArgs.

Cảm ơn lnmx vì gợi ý rằng CreateGraphics() bên trong bộ xử lý Paint ngăn chức năng chính xác của OptimizedDoubleBuffer. Điều này giải thích vấn đề nhấp nháy ngay cả khi OptimizedDoubleBuffer được bật. Tôi không biết điều đó và tôi cũng không tìm thấy điều này tại MSDN Library. Trong các điều khiển trước đây của tôi, tôi cũng đã sử dụng đối tượng Graphics từ PaintEventArgs.

Cảm ơn Sallow vì những nỗ lực của bạn. Tôi sẽ kiểm tra mã của bạn hôm nay và tôi sẽ đưa ra phản hồi. Tôi hy vọng điều này sẽ cải thiện hiệu suất bởi vì vẫn còn một vấn đề hiệu suất - mặc dù đệm đôi chính xác.

Vẫn còn một vấn đề hiệu suất khác trong mã ban đầu của tôi.

Changed

graphics.DrawImage(mStator, imagePosition); 
graphics.DrawImage(RotateImage(mRotor, rotorAngle), imagePosition); 

để

graphics.DrawImage(mStator, imagePosition); 
Bitmap rotatedImage = RotateImage(mRotor, rotorAngle); 
graphics.DrawImage(rotatedImage, imagePosition); 
rotatedImage.Dispose(); // Important, otherwise the RAM will be flushed with bitmaps. 
Các vấn đề liên quan