사용자 컨트롤에서 깜박임을 수정하는 방법


107

내 응용 프로그램에서 나는 한 컨트롤에서 다른 컨트롤로 끊임없이 이동하고 있습니다. 나는 아니오를 만들었습니다. 하지만 탐색 중에 내 컨트롤이 깜박입니다. 업데이트하는 데 1 ~ 2 초가 걸립니다. 나는 이것을 설정하려고했다

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

하지만 도움이되지 않았습니다 ... 각 컨트롤에는 다른 컨트롤이있는 동일한 배경 이미지가 있습니다. 그래서 그것에 대한 해결책은 무엇입니까 ..
감사합니다.


이 진술은 어디에 있습니까? 이상적으로는 생성자에 넣으십시오. UpdateStyles이것들을 설정하고 전화 했습니까 ? 잘못 문서화되어 있지만 때때로 필요할 수 있습니다.
Thomas

답변:


306

이중 버퍼링으로 해결할 수있는 것은 플리커의 종류가 아닙니다. BeginUpdate 또는 SuspendLayout도 없습니다. 컨트롤이 너무 많으면 BackgroundImage가 상황을 훨씬 더 악화시킬 수 있습니다.

UserControl이 스스로 칠할 때 시작됩니다. BackgroundImage를 그려 자식 컨트롤 창이가는 곳에 구멍을 남깁니다. 그런 다음 각 자식 컨트롤은 자체적으로 페인트하라는 메시지를 받고 창 내용으로 구멍을 채 웁니다. 컨트롤이 많으면 해당 구멍이 잠시 동안 사용자에게 표시됩니다. 그들은 일반적으로 흰색이며 어두울 때 BackgroundImage와 나쁘게 대조됩니다. 또는 양식에 Opacity 또는 TransparencyKey 속성이 설정되어 있으면 검은 색이 될 수 있습니다.

이것은 Windows Forms의 매우 근본적인 제한 사항이며 Windows가 창을 렌더링하는 방식과 관련이 있습니다. WPF btw에 의해 수정되었으며 자식 컨트롤에 창을 사용하지 않습니다. 원하는 것은 자식 컨트롤을 포함하여 전체 양식을 이중 버퍼링하는 것입니다. 가능 합니다. 이 스레드의 내 코드에서 솔루션을 확인하십시오. 그러나 부작용이 있으며 실제로 페인팅 속도를 높이지는 않습니다. 코드는 간단합니다. 사용자 정의 컨트롤이 아닌 양식에 다음을 붙여 넣으십시오.

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

플리커가 더 이상 눈에 띄지 않을 정도로 페인팅 속도를 향상시키기 위해 할 수있는 일이 많이 있습니다. BackgroundImage를 다루는 것으로 시작하십시오. 소스 이미지가 크고 컨트롤에 맞게 축소해야 할 때 비용 많이들 수 있습니다 . BackgroundImageLayout 속성을 "Tile"로 변경합니다. 눈에 띄게 속도가 빨라지면 페인팅 프로그램으로 돌아가 일반적인 컨트롤 크기와 더 잘 일치하도록 이미지 크기를 조정합니다. 또는 UC의 OnResize () 메서드에 코드를 작성하여 컨트롤을 다시 그릴 때마다 크기를 조정할 필요가 없도록 적절한 크기의 이미지 복사본을 만듭니다. 해당 사본에 Format32bppPArgb 픽셀 형식을 사용하면 다른 픽셀 형식보다 약 10 배 더 빠르게 렌더링됩니다.

다음으로 할 수있는 일은 구멍이 너무 눈에 띄고 이미지와 나쁘게 대조되는 것을 방지하는 것입니다. UC에 대한 WS_CLIPCHILDREN 스타일 플래그를 수 있습니다 .이 플래그는 자식 컨트롤이 이동하는 영역에 UC가 페인팅되는 것을 방지합니다. UserControl의 코드에 다음 코드를 붙여 넣습니다.

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

이제 자식 컨트롤이 배경 이미지 위에 페인트됩니다. 여전히 그들 스스로 그림을 그리는 것을 볼 수 있지만 못생긴 중간 흰색 또는 블랙홀은 보이지 않습니다.

마지막으로, 자식 컨트롤의 수를 줄이는 것은 항상 느린 페인팅 문제를 해결하는 좋은 방법입니다. UC의 OnPaint () 이벤트를 재정의하고 현재 자식에 표시되는 것을 그립니다. 특정 Label과 PictureBox는 매우 낭비입니다. 포인트 앤 클릭에 편리하지만 가벼운 대안 (문자열 또는 이미지 그리기)은 OnPaint () 메서드에서 한 줄의 코드 만 사용합니다.


나를 위해 WS_CLIPCHILDREN 개선 된 사용자 경험을 해제하십시오.
Mahesh

절대적으로 완벽합니다! .. 감사합니다
AlejandroAlis

8

이것은 실제 문제이며 Hans Passant가 제공 한 대답은 깜박임을 줄이는 데 좋습니다. 그러나 그가 언급했듯이 부작용이 있으며 추악 할 수 있습니다 (UI 추악). " WS_CLIPCHILDRENUC에 대한 스타일 플래그를 해제 할 수 있습니다."라고 말한 것처럼 UC에 대해서만 해제됩니다. 기본 양식의 구성 요소에는 여전히 문제가 있습니다.

예를 들어 패널 스크롤 막대는 기술적으로 하위 영역에 있기 때문에 페인트하지 않습니다. 그러나 자식 구성 요소는 스크롤 막대를 그리지 않으므로 마우스를 위에 놓을 때까지 (또는 다른 이벤트가 트리거 할 때까지) 그려지지 않습니다.

또한 애니메이션 아이콘 (대기 루프에서 아이콘 변경)이 작동하지 않습니다. 의 아이콘을 제거해도 tabPage.ImageKey다른 tabPages의 크기가 적절하게 조정 / 다시 그려지지 않습니다.

그래서 WS_CLIPCHILDREN초기 페인팅 을 끄는 방법을 찾고 있었기 때문에 Form이 멋지게 그려 지거나 많은 구성 요소로 폼 크기를 조정하는 동안에 만 켜는 것이 좋습니다.

트릭은 응용 프로그램 CreateParams이 원하는 WS_EX_COMPOSITED/WS_CLIPCHILDREN스타일 로 호출 하도록하는 것 입니다. 여기서 해킹을 찾았습니다 ( https://web.archive.org/web/20161026205944/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of- flicker-on-windows-forms-applications.aspx ) 훌륭하게 작동합니다. 감사합니다 AngryHacker!

나는 넣어 TurnOnFormLevelDoubleBuffering()양식에 전화를 ResizeBegin이벤트 및 TurnOffFormLevelDoubleBuffering()양식 ResizeEnd 이벤트에서 호출 (또는 그냥두고 WS_CLIPCHILDREN가 처음 제대로 그린 후.)

    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;
    }

TurnOnFormLevelDoubleBuffering () 메소드를 포함하지 않습니다 코드 ...
댄 W

@DanW이 답변에 게시 된 URL을 살펴보십시오 ( angryhacker.com/blog/archive/2010/07/21/… )
ChrisB

이 답변의 링크는 죽은 것으로 보입니다. 솔루션에 대해 궁금합니다. 다른 예에 대한 링크가 있습니까?
Pratt Hinds

6

컨트롤에서 사용자 지정 페인팅을 수행하는 경우 (예 : OnPaint 재정의) 직접 이중 버퍼링을 시도 할 수 있습니다.

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);
}

그리고 속성으로 제어를 무효화하십시오. NeedRepaint

그렇지 않으면 SuspendLayout 및 ResumeLayout에 대한 위의 대답이 아마도 원하는 것입니다.


이것은 이중 버퍼를 시뮬레이션하는 창의적인 방법입니다!. if (image != null) image.Dispose();이전에 추가 할 수 있습니다image = new Bitmap...
S.Serpooshan


2

배경 이미지가있는 기본 양식 또는 사용자 정의 컨트롤에서 BackgroundImageLayout속성을 Center또는로 설정합니다 Stretch. 사용자 컨트롤이 렌더링 될 때 큰 차이를 알 수 있습니다.


2

댓글로 추가하려고했지만 포인트가 부족합니다. 이것은 Hans의 게시물에 감사드립니다. 저와 같은 C ++ 빌더를 사용하는 모든 사람을 위해 여기에 번역이 있습니다.

애플리케이션의 기본 양식 .h 파일에 CreateParams 선언을 추가합니다.

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

이것을 .cpp 파일에 추가하십시오

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

2

생성자 또는 OnLoad 이벤트에 다음 코드를 입력하고 하위 컨트롤이있는 일종의 사용자 지정 사용자 컨트롤을 사용하는 경우 이러한 사용자 지정 컨트롤도 이중 버퍼링되었는지 확인해야합니다 (MS 설명서에서는 기본적으로 true로 설정되어 있습니다).

사용자 지정 컨트롤을 만드는 경우이 플래그를 액터에 추가 할 수 있습니다.

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

선택적으로 양식 / 컨트롤에서이 코드를 사용할 수 있습니다.

foreach (Control control in Controls)
{
    typeof(Control).InvokeMember("DoubleBuffered",
        BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
        null, control, new object[] { true });
}

폼 / 컨트롤의 모든 컨트롤을 반복하고 해당 컨트롤에 액세스합니다. DoubleBuffered 속성에 한 다음 폼의 각 컨트롤을 이중 버퍼링하도록 만들기 위해 true로 변경합니다. 여기서 반영하는 이유는 액세스 할 수없는 자식 컨트롤이있는 컨트롤이 있다고 가정하기 때문입니다. 이렇게하면 개인 컨트롤이더라도 해당 속성을 true로 변경할 수 있습니다.

이중 버퍼링 기술에 대한 자세한 내용은 여기에서 확인할 수 있습니다 . .

이 문제를 정렬하기 위해 일반적으로 재정의하는 또 다른 속성이 있습니다.

protected override CreateParams CreateParams
{
    get
    {
        CreateParams parms = base.CreateParams;
        parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
        return parms;
    }
}

WS_EX_COMPOSITED -이중 버퍼링을 사용하여 창의 모든 하위 항목을 아래에서 위로 페인팅 순서로 페인팅합니다.

여기 에서 이러한 스타일 플래그를 더 많이 찾을 수 있습니다 .

도움이 되었기를 바랍니다.


1

Hans가 준 답변에 추가하기 위해 :

(TLDR 버전 : 투명도가 생각보다 무겁고 모든 곳에서 단색 만 사용)

WS_EX_COMPOSITED, DoubleBuffered 및 WS_CLIPCHILDREN이 깜박임을 해결하지 못한 경우 (저에게는 WS_CLIPCHILDREN이 더 나빠졌습니다) 다음을 시도하십시오. 모든 컨트롤과 모든 코드를 살펴보고 BackColor, ForeColor에 대한 투명성 또는 반 투명성이있는 곳이면 어디든지 다른 색상은 제거하고 단색 만 사용하십시오. 당신이 방금 가지고 있다고 생각하는 대부분의 경우 투명성을 사용하여, 당신은하지 않습니다. 코드와 컨트롤을 다시 디자인하고 단색을 사용하십시오. 나는 끔찍하고 끔찍한 깜박임이 있었고 프로그램이 느리게 실행되었습니다. 투명도를 제거하면 속도가 크게 빨라지고 깜박임이 없습니다.

편집 : 더 추가하려면 WS_EX_COMPOSITED가 창 전체 일 필요가 없으며 특정 컨트롤에만 적용 할 수 있음을 발견했습니다! 이것은 나에게 많은 문제를 해결했습니다. 필요한 컨트롤에서 상속 된 사용자 지정 컨트롤을 만들고 WS_EX_COMPOSITED에 대해 이미 게시 된 재정의를 붙여 넣습니다. 이렇게하면이 컨트롤에서만 낮은 수준의 이중 버퍼를 얻을 수 있으므로 나머지 응용 프로그램에서 불쾌한 부작용을 방지 할 수 있습니다!


0

이 질문이 매우 오래되었다는 것을 알고 있지만 그것에 대한 경험을 제공하고 싶습니다.

.NET 4.0을 사용하는 Windows 8 및 / 또는 Tabcontrol재정의 된 양식에서 깜박임에 많은 문제가있었습니다 .OnPaintOnPaintBackGround

작동하는 유일한 생각 은 재정의 에서 메서드를 사용하지 않았다는 것입니다. 즉,에서 제공하는 Graphics에 직접 그리기를 수행했을 때 모든 직사각형을 칠해도 깜박임이 사라졌습니다. 그러나 전화하면Graphics.DrawImageOnPaintPaintEventArgsDrawImage 메서드를 클립 된 비트 맵을 그리는 경우에도 (이중 버퍼링을 위해 생성됨) 깜박임이 나타납니다.

도움이 되었기를 바랍니다.


0

나는 결합 이 플리커 수정이 글꼴 수정을 나는 그것이 오프 스크린 다시 등을 갈 때 무효화에있는 TabControl을 페인트에 타이머를 시작하는 내 자신의 코드의 비트를 추가 한 다음 ..

세 가지 모두 다음을 수행합니다.

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();
    }
}

나는 제작자는 아니지만 비트 맵이 모든 버그를 우회한다는 것을 이해합니다.

이것은 TabControl (아이콘 포함) 깜박임을 확실히 해결 한 유일한 것입니다.

차이 결과 비디오 : 바닐라 tabcontrol 대 tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

추신. HotTrack = true로 설정해야합니다. 이렇게하면 해당 버그도 수정됩니다.


-2

Control.DoubleBuffered속성 을 사용해 보셨습니까 ?

깜박임을 줄이거 나 방지하기 위해이 컨트롤이 보조 버퍼를 사용하여 표면을 다시 그려야하는지 여부를 나타내는 값을 가져 오거나 설정합니다.

또한 이것이것은 도움 될 수 있습니다.


-9

더블 버퍼링과 그 모든 것들이 필요하지 않습니다.

간단한 솔루션 ...

MDI 인터페이스를 사용하는 경우 기본 양식에 아래 코드를 붙여 넣으십시오. 페이지에서 모든 깜박임이 제거됩니다. 그러나 로딩에 더 많은 시간이 필요한 일부 페이지는 1 초 또는 2 초 내에 표시됩니다. 그러나 이것은 각 항목이 하나씩 나오는 깜박이는 페이지를 표시하는 것보다 낫습니다.

이것은 전체 응용 프로그램을위한 유일한 최상의 솔루션입니다. 기본 양식에 넣을 코드를 참조하십시오.

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

12
그래서, 당신이 말하는 것은 Hans가 2 년 전에 제공 한 대답이 사실 맞다는 것입니다. 고마워, Kshitiz. 정말 도움이됩니다!
Fernando
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.