Jak naprawić migotanie w kontrolkach użytkownika

W mojej aplikacji ciągle przechodzę od jednej kontroli do drugiej. Stworzyłem nie. kontrolek użytkownika, ale podczas nawigacji moje kontrolki migotają. aktualizacja trwa 1 lub 2 sek. Próbowałem ustawić to

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.DoubleBuffer, true);
Ale to nie pomogło... Każda kontrolka ma ten sam obraz tła z różnymi kontrolkami. Więc jakie jest rozwiązanie dla niego.. Dzięki.
Author: Royson, 2010-04-10

12 answers

Nie jest to rodzaj migotania, który może rozwiązać podwójne buforowanie. Ani BeginUpdate ani SuspendLayout. Masz zbyt wiele kontroli, tło może sprawić, że będzie to dużo gorzej.

Zaczyna się, gdy UserControl sam się maluje. Rysuje Tło, pozostawiając otwory w miejscach, w których znajdują się okna kontroli rodzicielskiej. Każda kontrolka potomna otrzymuje wiadomość do pomalowania się, wypełni dziurę zawartością okna. Gdy masz dużo kontroli, te otwory są widoczne dla użytkownika przez jakiś czas. Zwykle są białe, źle kontrastują z tłem, gdy jest ciemno. Lub mogą być czarne, jeśli formularz ma ustawioną właściwość krycie lub TransparencyKey, źle kontrastując z niemal wszystkim.

Jest to dość fundamentalne ograniczenie formularzy Windows, utkwiło w sposobie, w jaki Windows renderuje okna. Poprawione przez WPF btw, nie używa windows dla kontrolek potomnych. To, czego chcesz, to podwójne buforowanie całego formularza, w tym dziecka sterowanie. To możliwe, sprawdź mój kod w w tym wątku , aby znaleźć rozwiązanie. Ma jednak skutki uboczne i w rzeczywistości nie zwiększa szybkości malowania. Kod jest prosty, wklej to do formularza (nie do kontrolki użytkownika):

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

Jest wiele rzeczy, które możesz zrobić, aby poprawić szybkość malowania, do tego stopnia, że migotanie nie jest już zauważalne. Zacznij od zajęcia się tłem. Mogą być naprawdę drogie, gdy obraz źródłowy jest duży i musi zostać zmniejszony aby dopasować sterowanie. Zmień właściwość BackgroundImageLayout na "Tile". Jeśli to daje zauważalne przyspieszenie, wróć do programu malowania i zmień rozmiar obrazu, aby lepiej pasował do typowego rozmiaru kontrolnego. Możesz też napisać kod w metodzie OnResize() UC, aby utworzyć kopię obrazu o odpowiednim rozmiarze, aby nie musiał być zmieniany za każdym razem, gdy kontrolka zostanie przemalowana. Użyj formatu piksela Format32bppPArgb dla tej kopii, renderuje około 10 razy szybciej niż jakikolwiek inny piksel format.

Następną rzeczą, którą możesz zrobić, to zapobiec tak zauważalnym i źle kontrastującym z obrazem otworom. Możesz wyłączyć flagę w stylu WS_CLIPCHILDREN dla UC, flagę, która zapobiega malowaniu UC w obszarze, w którym znajdują się elementy sterujące dziecka. Wklej ten kod w kodzie UserControl:

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

Elementy sterujące dla dzieci będą teraz malować się na obrazku tła. Można jeszcze zobaczyć, jak malują się jeden po drugim, ale brzydkie pośrednia biała lub czarna dziura nie będzie widoczna.

Wreszcie, zmniejszenie liczby kontrolek dla dzieci jest zawsze dobrym podejściem do rozwiązywania problemów z powolnym malowaniem. Nadpisuje Zdarzenie OnPaint() UC i rysuje to, co jest teraz pokazane w potomku. Poszczególne etykiety i PictureBox są Bardzo marnotrawne. Wygodny do wskaż i kliknij, ale ich lekka alternatywa (rysowanie ciągu znaków lub obrazu) zajmuje tylko jeden wiersz kodu w metodzie OnPaint ().

 276
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:09:54

To jest prawdziwy problem, a odpowiedź, którą dał Hans Passant, jest świetna dla ratowania migotania. Istnieją jednak skutki uboczne, jak wspomniał, i mogą być brzydkie (UI ugly). Jak wspomniano ," możesz wyłączyć flagę stylu WS_CLIPCHILDREN dla UC", ale to wyłącza ją tylko dla UC. Komponenty w głównym formularzu nadal mają problemy.

Przykład: pasek przewijania panelu nie jest malowany, ponieważ znajduje się technicznie w obszarze potomnym. Komponent potomny nie rysuje paska przewijania, więc nie zostanie namalowany dopóki nie zostanie najechany kursorem myszy (lub inne zdarzenie go uruchomi).

Również animowane ikony (Zmiana ikon w pętli oczekiwania) nie działają. Usuwanie ikon na zakładce.ImageKey nie zmienia rozmiaru / nie maluje odpowiednio innych stron kart.

Więc szukałem sposobu, aby wyłączyć WS_CLIPCHILDREN przy początkowym malowaniu, aby mój formularz ładował się ładnie pomalowany, lub jeszcze lepiej tylko włączyć go podczas zmiany rozmiaru formularza z wieloma komponentami.

Sztuczka polega na tym, aby aplikacja do wywoływania CreateParams z żądanym stylem WS_EX_COMPOSITED / WS_CLIPCHILDREN? Znalazłem włamanie.(http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) i działa świetnie. Dzięki AngryHacker!

Umieściłem wywołanie TurnOnFormLevelDoubleBuffering() w zdarzeniu ResizeBegin. TurnOffFormLevelDoubleBuffering () wywołanie w zdarzeniu ResizeEnd formularza (lub po prostu zostaw go WS_CLIPCHILDREN po jego początkowym odpowiednio pomalowane.)

    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;
    }
 8
Author: user2044810,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-20 18:54:46

Jeśli robisz dowolne niestandardowe malowanie w kontrolce (np. nadpisując OnPaint), możesz spróbować podwójnego buforowania samodzielnie.

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

I Unieważnić kontrolę nad nieruchomością NeedRepaint

W Przeciwnym Razie powyższa odpowiedź z SuspendLayout i ResumeLayout jest prawdopodobnie tym, czego chcesz.

 4
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-10 08:40:19
 3
Author: Brij,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:54

W głównym formularzu lub kontrolerze użytkownika, w którym znajduje się obraz tła, ustaw właściwość BackgroundImageLayout na Center lub Stretch. Zauważysz dużą różnicę podczas renderowania kontrolki użytkownika.

 2
Author: revobtz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-31 16:00:39

Próbowałem dodać to jako komentarz, ale nie mam wystarczająco dużo punktów. To jedyna rzecz, która kiedykolwiek pomogła moim migotliwym problemom, dzięki Hansowi za jego post. Dla każdego, kto używa C++ builder jak ja oto tłumaczenie

Dodaj deklarację CreateParams do głównego formularza aplikacji .plik h np.

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

I dodaj to do swojego .plik cpp

void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
    Params.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    TForm::CreateParams(Params);
}
 2
Author: NoComprende,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-05 09:10:15

Umieść poniższy kod w swoim konstruktorze lub zdarzeniu OnLoad, a jeśli używasz niestandardowej kontroli użytkownika, która ma kontrolki podrzędne, musisz się upewnić, że te niestandardowe kontrolki są również dwukrotnie buforowane (nawet jeśli w dokumentacji MS mówią, że domyślnie ustawiono ją na true).

Jeśli tworzysz niestandardową kontrolę, możesz dodać tę flagę do swojego ctor:

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

Opcjonalnie możesz użyć tego kodu w formularzu / sterowaniu:

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

Przechodzimy przez wszystkie kontrolki w form / control i uzyskujemy dostęp do ich właściwości DoubleBuffered, a następnie zmieniamy ją na true, aby każda kontrolka w formularzu była dwukrotnie buforowana. Powodem, dla którego się tu zastanawiamy, jest to, że wyobraź sobie, że masz kontrolkę, która ma kontrolki dla dzieci, które nie są dostępne, w ten sposób, nawet jeśli są to kontrole prywatne, nadal zmienimy ich własność na true.

Więcej informacji na temat techniki podwójnego buforowania można znaleźć tutaj .

Jest jeszcze jedna właściwość Zwykle nadpisuję, aby rozwiązać ten problem:

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

WS_EX_COMPOSITED - maluje wszystkie potomkinie okna w kolejności malowania od dołu do góry za pomocą podwójnego buforowania.

Więcej tych flag stylowych znajdziesz tutaj .

Mam nadzieję, że to pomoże!
 2
Author: Gilad Reich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-07 23:15:29

Aby dodać do odpowiedzi Hans dał:

(wersja TLDR: przezroczystość jest cięższa niż myślisz, wszędzie używaj tylko jednolitych kolorów)

Jeśli WS_EX_COMPOSITED, DoubleBuffered i WS_CLIPCHILDREN nie rozwiązały twojego migotania (dla mnie WS_CLIPCHILDREN sprawiło, że było jeszcze gorzej), spróbuj tego: przejdź przez wszystkie kontrolki i cały kod, i wszędzie tam, gdzie masz przezroczystość lub półprzezroczystość dla BackColor, ForeColor lub dowolnego innego koloru, po prostu usuń go, używaj tylko jednolitych kolorów. W większości przypadki, w których myślisz, że po prostu masz , aby użyć przezroczystości, nie. przeprojektuj swój kod i kontrolki, i użyj jednolitych kolorów. Miałem straszne, straszne migotanie i program działał powolnie. Po usunięciu przezroczystości znacznie przyspieszył i jest 0 migotanie.

EDIT: aby dodać jeszcze więcej, właśnie odkryłem, że WS_EX_COMPOSITED nie musi być w całym oknie, może być stosowany tylko do konkretnych kontrolek! Oszczędziło mi to wielu kłopotów. Po prostu zrób Niestandardowy sterowanie dziedziczone z dowolnej kontroli trzeba, i wklej już opublikowane override dla WS_EX_COMPOSITED. W ten sposób otrzymasz niskopoziomowy podwójny bufor tylko na tej kontroli, unikając nieprzyjemnych efektów ubocznych w pozostałej części aplikacji!

 1
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-21 15:35:49

Wiem, że to pytanie jest bardzo stare, ale chcę dać swoje doświadczenie na ten temat.

Miałem wiele problemów z migotaniem Tabcontrol w formie z nadpisanym OnPaint i/lub OnPaintBackGround W Windows 8 przy użyciu.NET 4.0.

Jedynym pomysłem, który zadziałał było nie używać metody Graphics.DrawImage w OnPaint nadpisuje, innymi słowy, gdy rysowanie zostało wykonane bezpośrednio do grafiki dostarczonej przez PaintEventArgs, nawet malując cały prostokąt, migotanie zniknęło. Ale jeśli wywołamy metodę DrawImage, nawet rysując obciętą bitmapę (utworzoną do podwójnego buforowania) pojawia się migotanie.

Mam nadzieję, że to pomoże!
 0
Author: ZeroWorks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-27 17:57:58

Połączyłem tę poprawkę migotaniai tę poprawkę CZCIONKI, następnie musiałem dodać trochę własnego kodu, aby uruchomić timer na paincie, aby unieważnić TabControl, gdy zniknie z ekranu i z powrotem, itp..

Wszystkie trzy tworzą to:

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

Nie jestem twórcą, ale z tego, co rozumiem, bitmapy to omijanie błędów.

To była jedyna rzecz, która definitywnie rozwiązała TabControl (z ikonami) migotanie dla mnie.

Różnica wynik wideo: vanilla tabcontrol vs tabcontrolex

Http://gfycat.com/FineGlitteringDeermouse

Ps. musisz ustawić HotTrack = true, ponieważ to również naprawia ten błąd

 0
Author: user3732487,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-24 04:42:36

Czy próbowałeś Control.DoubleBuffered własność?

Pobiera lub ustawia wartość wskazującą, czy ta kontrolka powinna ponownie narysować swoją powierzchnię za pomocą bufora wtórnego, aby zmniejszyć lub zapobiec migotaniu.

Również to i to może pomóc.

 -2
Author: KMån,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-10 10:06:59

Nie ma potrzeby podwójnego buforowania...

Proste rozwiązanie...

Jeśli używasz interfejsu MDI, po prostu wklej poniższy kod w głównym formularzu. Usunie wszystkie migotanie ze stron. Jednak niektóre strony, które wymagają więcej czasu na ładowanie, pojawią się w ciągu 1 lub 2 sekund. Ale to jest lepsze niż pokazanie migoczącej strony, w której każdy element przychodzi jeden po drugim.

Jest to jedyne najlepsze rozwiązanie dla całej aplikacji. Zobacz też kod do umieszczenia w formularzu głównym:

protected override CreateParams CreateParams {
  get {
    CreateParams cp = base.CreateParams;
    cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    return cp;
  }
} 
 -9
Author: Kshitiz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-02 20:41:37