2010-04-10 37 views
87

Trong ứng dụng của tôi, tôi liên tục di chuyển từ điều khiển này sang điều khiển khác. Tôi đã tạo ra không. điều khiển người dùng, nhưng trong khi điều hướng, các điều khiển của tôi bị nhấp nháy. phải mất 1 hoặc 2 giây để cập nhật. Tôi đã cố gắng đặt điều nàyCách sửa lỗi nhấp nháy trong Điều khiển người dùng

SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 
or 
SetStyle(ControlStyles.UserPaint, true); 
SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.DoubleBuffer, true); 

nhưng không giúp được ... Mỗi điều khiển có cùng hình nền với các điều khiển khác nhau. Vì vậy, giải pháp cho nó là gì ..
Cảm ơn.

+0

Đâu là các báo cáo này? Lý tưởng nhất, đặt chúng vào hàm tạo. Bạn có gọi là 'UpdateStyles' sau khi thiết lập những cái này không? Đó là tài liệu xấu, nhưng đôi khi có thể là cần thiết. – Thomas

Trả lời

260

Nó không phải là loại nhấp nháy mà bộ đệm đôi có thể giải quyết. Cũng không phải BeginUpdate hoặc SuspendLayout. Bạn có quá nhiều điều khiển, BackgroundImage có thể làm cho điều này trở nên tồi tệ hơn lot.

Bắt đầu khi UserControl tự sơn. Nó vẽ BackgroundImage, để lại các lỗ nơi cửa sổ điều khiển con đi. Mỗi đứa trẻ kiểm soát sau đó nhận được một tin nhắn để vẽ chính nó, họ sẽ điền vào các lỗ với nội dung cửa sổ của họ. Khi bạn có rất nhiều điều khiển, các lỗ đó sẽ hiển thị cho người dùng trong một thời gian. Chúng thường có màu trắng, tương phản với BackgroundImage khi trời tối. Hoặc chúng có thể là màu đen nếu biểu mẫu có thuộc tính Opacity hoặc TransparencyKey, tương phản với bất kỳ thứ gì.

Đây là một hạn chế khá cơ bản của Windows Forms, nó bị mắc kẹt với cách Windows hiển thị cửa sổ. Được sửa bởi WPF btw, nó không sử dụng các cửa sổ cho các điều khiển con. Những gì bạn muốn là tăng gấp đôi đệm toàn bộ biểu mẫu, bao gồm cả các điều khiển con. Có thể, hãy kiểm tra mã của tôi theo số this thread để biết giải pháp. Nó có tác dụng phụ mặc dù, và không thực sự tăng tốc độ vẽ. Mã này rất đơn giản, dán này trong mẫu của bạn (không phải là kiểm soát người sử dụng):

protected override CreateParams CreateParams { 
    get { 
    CreateParams cp = base.CreateParams; 
    cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED 
    return cp; 
    } 
} 

Có rất nhiều điều bạn có thể làm để cải thiện tốc độ hội họa, đến nỗi các nhấp nháy là không đáng chú ý nữa. Bắt đầu bằng cách giải quyết BackgroundImage. Chúng có thể là thực sự là tốn kém khi hình ảnh nguồn lớn và cần được thu nhỏ để vừa với điều khiển. Thay đổi thuộc tính BackgroundImageLayout thành "Ngói". Nếu điều đó cho tốc độ đáng chú ý, hãy quay lại chương trình vẽ và thay đổi kích thước hình ảnh để phù hợp hơn với kích thước điều khiển điển hình.Hoặc viết mã trong phương thức OnResize() của UC để tạo ra một bản sao có kích thước đúng của hình ảnh sao cho nó không phải được thay đổi kích cỡ mỗi khi điều khiển được sơn lại. Sử dụng định dạng pixel Format32bppPArgb cho bản sao đó, nó hiển thị nhanh hơn khoảng 10 lần so với bất kỳ định dạng pixel nào khác.

Điều tiếp theo bạn có thể làm là ngăn không cho các lỗ này trở nên dễ thấy và tương phản xấu với hình ảnh. Bạn có thể turn off cờ kiểu WS_CLIPCHILDREN cho UC, cờ ngăn không cho UC vẽ tranh trong khu vực nơi điều khiển con đi. Dán mã này vào mã của UserControl:

protected override CreateParams CreateParams { 
    get { 
    var parms = base.CreateParams; 
    parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN 
    return parms; 
    } 
} 

Giờ đây, điều khiển con sẽ tự vẽ trên nền của hình nền. Bạn có thể vẫn thấy chúng tự vẽ từng cái một, nhưng lỗ đen hoặc trắng trung gian xấu xí sẽ không thể nhìn thấy được.

Cuối cùng nhưng không kém phần quan trọng, giảm số lượng điều khiển con luôn là cách tiếp cận tốt để giải quyết các vấn đề về sơn chậm. Ghi đè lên sự kiện OnPaint() của UC và vẽ những gì được hiển thị trong một đứa trẻ. Nhãn đặc biệt và PictureBox là rất lãng phí. Thuận tiện cho điểm và nhấp chuột nhưng thay thế trọng lượng nhẹ của chúng (vẽ một chuỗi hoặc một hình ảnh) chỉ mất một dòng mã trong phương thức OnPaint() của bạn.

3

Nếu bạn đang làm bất kỳ bức tranh tùy chỉnh trong việc kiểm soát (ví dụ: trọng OnPaint), bạn có thể thử các bộ đệm cho mình đôi.

Image image; 
protected override OnPaint(...) { 
    if (image == null || needRepaint) { 
     image = new Bitmap(Width, Height); 
     using (Graphics g = Graphics.FromImage(image)) { 
      // do any painting in image instead of control 
     } 
     needRepaint = false; 
    } 
    e.Graphics.DrawImage(image, 0, 0); 
} 

Và làm mất hiệu lực kiểm soát của bạn với một tài sản NeedRepaint

Nếu câu trả lời ở trên với SuspendLayout và ResumeLayout có lẽ là những gì bạn muốn.

+0

Đây là một phương pháp sáng tạo để mô phỏng doublebuffer !. Bạn có thể thêm 'if (image! = Null) image.Dispose();' before 'image = new Bitmap ...' –

-2

Bạn đã thử Control.DoubleBuffered Thuộc tính?

Nhận hoặc đặt giá trị cho biết liệu điều khiển này có nên vẽ lại bề mặt của nó bằng bộ đệm thứ cấp để giảm hoặc ngăn nhấp nháy.

Đồng thời thisthis có thể hữu ích.

-9

Không cần của bất kỳ đệm giường đôi và tất cả những gì chàng trai thứ ...

Một giải pháp đơn giản ...

Nếu bạn đang sử dụng MDI Interface, chỉ cần dán mã dưới đây vào hình thức chính. Nó sẽ loại bỏ tất cả nhấp nháy từ các trang. Tuy nhiên, một số trang yêu cầu thêm thời gian tải sẽ hiển thị sau 1 hoặc 2 giây. Nhưng điều này là tốt hơn so với hiển thị một trang nhấp nháy trong đó mỗi mục đến từng người một.

Đây là giải pháp tốt nhất cho toàn bộ ứng dụng. Xem mã để đưa vào hình thức chính:

protected override CreateParams CreateParams { 
    get { 
    CreateParams cp = base.CreateParams; 
    cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED 
    return cp; 
    } 
} 
+11

Vì vậy, những gì bạn đang nói là câu trả lời mà Hans cung cấp cách đây hơn hai năm, trên thực tế , chính xác? Cảm ơn, Kshitiz. Điều đó rất hữu ích! – Fernando

0

Chỉ cần để thêm vào câu trả lời Hans đã:

(TLDR phiên bản: Tính minh bạch nặng hơn bạn nghĩ, sử dụng màu sắc chỉ ở mức giỏi ở khắp mọi nơi)

Nếu WS_EX_COMPOSITED, DoubleBuffered và WS_CLIPCHILDREN không giải quyết nhấp nháy của bạn (cho tôi WS_CLIPCHILDREN làm cho nó tồi tệ hơn), hãy thử điều này: đi qua TẤT CẢ các điều khiển của bạn và tất cả mã của bạn, và bất cứ nơi nào bạn có bất kỳ minh bạch hoặc bán trong suốt cho BackColor, ForeColor, hoặc bất kỳ màu nào khác, chỉ cần loại bỏ nó, chỉ sử dụng màu sắc rắn. Trong hầu hết các trường hợp mà bạn cho rằng bạn chỉ cần để sử dụng tính minh bạch, bạn thì không. Thiết kế lại mã và điều khiển của bạn và sử dụng các màu đồng nhất. Tôi đã có khủng khiếp, khủng khiếp nhấp nháy và chương trình đang chạy chậm chạp. Khi tôi loại bỏ độ trong suốt, nó tăng tốc đáng kể và có 0 nhấp nháy.

EDIT: Để thêm hơn nữa, tôi vừa phát hiện ra rằng WS_EX_COMPOSITED không phải là cửa sổ, nó chỉ có thể được áp dụng cho các điều khiển cụ thể! Điều này đã cứu tôi rất nhiều rắc rối. Chỉ cần tạo điều khiển tùy chỉnh được kế thừa từ bất kỳ điều khiển nào bạn cần và dán ghi đè đã đăng cho WS_EX_COMPOSITED. Bằng cách này, bạn chỉ nhận được bộ đệm đôi ở mức thấp trên điều khiển này, tránh các tác dụng phụ khó chịu trong phần còn lại của ứng dụng!

7

Đây là vấn đề thực sự và câu trả lời mà Hans Passant đưa ra là rất tốt để lưu nhấp nháy. Tuy nhiên, có những tác dụng phụ như ông đã đề cập, và họ có thể xấu xí (UI xấu xí). Như đã nói, "Bạn có thể tắt cờ kiểu WS_CLIPCHILDREN cho UC", nhưng điều đó chỉ tắt nó cho một UC.Các thành phần trên biểu mẫu chính vẫn có vấn đề.

Ví dụ, thanh cuộn bảng điều khiển không vẽ, bởi vì nó là kỹ thuật trong khu vực con. Tuy nhiên thành phần con không vẽ thanh cuộn, vì vậy nó không được vẽ cho đến khi chuột qua (hoặc một sự kiện khác kích hoạt nó).

Ngoài ra, các biểu tượng hoạt ảnh (thay đổi biểu tượng trong vòng lặp chờ) không hoạt động. Loại bỏ các biểu tượng trên tabPage.ImageKey không thay đổi kích thước/sơn lại các tabPages khác một cách thích hợp.

Vì vậy, tôi đang tìm cách tắt WS_CLIPCHILDREN trên bức tranh ban đầu để Biểu mẫu của tôi sẽ được tải đẹp mắt, hoặc tốt hơn nhưng chỉ bật lên khi thay đổi kích thước biểu mẫu của tôi với nhiều thành phần.

Bí quyết là để ứng dụng gọi CreateParams theo kiểu WS_EX_COMPOSITED/WS_CLIPCHILDREN mong muốn? Tôi tìm thấy một hack ở đây (http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) và nó hoạt động tuyệt vời. Cảm ơn AngryHacker!

Tôi đặt lệnh gọi TurnOnFormLevelDoubleBuffering() trong biểu mẫu ResizeBegin. TurnOffFormLevelDoubleBuffering() gọi trong trường hợp hình thức ResizeEnd (hoặc chỉ cần để nó WS_CLIPCHILDREN sau khi nó được sơn ban đầu đúng cách.)

int originalExStyle = -1; 
    bool enableFormLevelDoubleBuffering = true; 

    protected override CreateParams CreateParams 
    { 
     get 
     { 
      if (originalExStyle == -1) 
       originalExStyle = base.CreateParams.ExStyle; 

      CreateParams cp = base.CreateParams; 
      if (enableFormLevelDoubleBuffering) 
       cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED 
      else 
       cp.ExStyle = originalExStyle; 

      return cp; 
     } 
    } 

    public void TurnOffFormLevelDoubleBuffering() 
    { 
     enableFormLevelDoubleBuffering = false; 
     this.MaximizeBox = true; 
    } 
+0

Mã của bạn không bao gồm phương thức TurnOnFormLevelDoubleBuffering() ... –

+0

@DanW Hãy xem URL được đăng trong câu trả lời này (http://www.angryhacker.com/blog/archive/2010/07/21/how -to-get-rid-of-flicker-on-windows-forms-applications.aspx) – ChrisB

1

Về hình thức chính hoặc điều khiển người dùng nơi hình nền sống thiết lập BackgroundImageLayout tài sản để Center hoặc Stretch . Bạn sẽ nhận thấy sự khác biệt lớn khi điều khiển người dùng hiển thị.

0

Tôi biết câu hỏi này là rất cũ, nhưng muốn cung cấp cho kinh nghiệm của tôi về nó.

Tôi gặp nhiều sự cố với Tabcontrol nhấp nháy trong biểu mẫu có ghi đè OnPaint và/hoặc OnPaintBackGround trong Windows 8 bằng .NET 4.0.

Các chỉ nghĩ rằng làm việc đã được KHÔNG SỬ DỤNG phương pháp Graphics.DrawImage trong OnPaint ghi đè, hay nói cách khác, khi bốc thăm đã được thực hiện trực tiếp với đồ họa được cung cấp bởi các PaintEventArgs, thậm chí vẽ tất cả các hình chữ nhật, nhấp nháy biến mất. Nhưng nếu gọi phương thức DrawImage, thậm chí vẽ một Bitmap bị cắt bớt, (được tạo cho đệm kép) nhấp nháy sẽ xuất hiện.

Hy vọng điều đó sẽ hữu ích!

0

tôi kết hợp this flicker fixthis font fix, sau đó tôi đã phải thêm một chút mã của riêng tôi để bắt đầu một giờ trên sơn để làm mất hiệu lực TabControl khi nó đi offscreen và lưng, vv ..

Cả ba làm cho điều này:

using System; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 
public class TabControlEx:TabControl 
{ 
    [DllImport("user32.dll")] 
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); 
    private const int WM_PAINT = 0x0f; 
    private const int WM_SETFONT = 0x30; 
    private const int WM_FONTCHANGE = 0x1d; 
    private System.Drawing.Bitmap buffer; 
    private Timer timer = new Timer(); 
    public TabControlEx() 
    { 
     timer.Interval = 1; 
     timer.Tick += timer_Tick; 
     this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); 
    } 
    void timer_Tick(object sender, EventArgs e) 
    { 
     this.Invalidate(); 
     this.Update(); 
     timer.Stop(); 
    } 
    protected override void WndProc(ref Message m) 
    { 
     if (m.Msg == WM_PAINT) timer.Start(); 
     base.WndProc(ref m); 
    } 
    protected override void OnPaint(PaintEventArgs pevent) 
    { 
     this.SetStyle(ControlStyles.UserPaint, false); 
     base.OnPaint(pevent); 
     System.Drawing.Rectangle o = pevent.ClipRectangle; 
     System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control); 
     if (o.Width > 0 && o.Height > 0) 
     DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height)); 
     pevent.Graphics.DrawImageUnscaled(buffer, 0, 0); 
     this.SetStyle(ControlStyles.UserPaint, true); 
    } 

    protected override void OnResize(EventArgs e) 
    { 
     base.OnResize(e); 
     buffer = new System.Drawing.Bitmap(Width, Height); 
    } 
    protected override void OnCreateControl() 
    { 
     base.OnCreateControl(); 
     this.OnFontChanged(EventArgs.Empty); 
    } 
    protected override void OnFontChanged(EventArgs e) 
    { 
     base.OnFontChanged(e); 
     IntPtr hFont = this.Font.ToHfont(); 
     SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1)); 
     SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero); 
     this.UpdateStyles(); 
    } 
} 

Tôi không phải là người tạo nhưng từ những gì tôi hiểu bitmap thực hiện tất cả các lỗi bỏ qua.

Đây là điều duy nhất giải quyết dứt khoát TabControl (có biểu tượng) nhấp nháy cho tôi.

chênh lệch kết quả video: vani TabControl vs tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

ps.bạn sẽ cần phải đặt HotTrack = true, bởi vì điều này khắc phục lỗi đó quá

1

Tôi đã cố gắng thêm nhận xét này làm nhận xét nhưng tôi không có đủ điểm. Đây là điều duy nhất từng giúp các vấn đề nhấp nháy của tôi rất nhiều nhờ Hans cho bài viết của mình. Đối với bất kỳ ai đang sử dụng trình tạo C++ giống như tôi, thì đây là bản dịch

Thêm khai báo CreateParams vào biểu mẫu chính của ứng dụng .h tệp e.g.

class TYourMainFrom : public TForm 
{ 
protected: 
    virtual void __fastcall CreateParams(TCreateParams &Params); 
} 

và thêm video này vào tập tin cpp của bạn

void __fastcall TYourMainForm::CreateParams(TCreateParams &Params) 
{ 
    Params.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED 
    TForm::CreateParams(Params); 
} 
1

Reflection hiện các trick! Ném điều này trong constructor của bạn hoặc sự kiện OnLoad và nếu bạn sử dụng một số UserControl tùy chỉnh có các điều khiển con, bạn sẽ cần phải chắc chắn rằng những điều khiển tùy chỉnh với các điều khiển phụ cũng có điều này.

GHI CHÚ: Không sử dụng điều này trong một số sự kiện như OnPaint đang thực hiện hàng nghìn tỷ lần mỗi giây vì phản ánh rất tốn kém để sử dụng.

Giải thích: Kiểm soát/Biểu mẫu gốc sẽ lặp qua tất cả các Điều khiển trong biểu mẫu và đặt thuộc tính của chúng thành DoubleBuffered.

Code:

foreach (Control control in Controls) // reflection to sort flickering. 
    { 
     typeof(Control).InvokeMember("DoubleBuffered", 
      BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, 
      null, control, new object[] { true }); 
    } 
Các vấn đề liên quan