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

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

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

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

감사합니다.



답변

이중 버퍼링으로 해결할 수있는 것은 플리커의 종류가 아닙니다. 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 () 메서드에서 한 줄의 코드 만 사용합니다.


답변

이것은 실제 문제이며 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;
    }


답변

컨트롤에서 사용자 지정 페인팅을 수행하는 경우 (예 : 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에 대한 위의 대답이 아마도 원하는 것입니다.


답변

BeginUpdate / EndUpdate 또는 SuspendLayout / ResumeLayout 메서드를 사용해보십시오. 다음을 참조하십시오
중첩 된 winform 컨트롤 깜박임 문제를 해결하는 방법 WinForms (예 : DataGridView)의 컨트롤 업데이트 중 깜박임


답변

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


답변

댓글로 추가하려고했지만 포인트가 부족합니다. 이것은 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);
}


답변

생성자 또는 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 -이중 버퍼링을 사용하여 창의 모든 하위 항목을 아래에서 위로 페인팅 순서로 페인팅합니다.

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

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