Kullanıcı kontrollerinde titreme nasıl düzeltilir


108

Uygulamamda sürekli olarak bir kontrolden diğerine geçiyorum. Hayır yarattım. kullanıcı kontrolleri, ancak gezinme sırasında kontrollerim titriyor. güncellenmesi 1 veya 2 saniye sürer. Bunu ayarlamaya çalıştım

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

ama yardımcı olmadı ... Her kontrolün farklı kontrollerle aynı arka plan görüntüsü vardır. Peki bunun için çözüm nedir ..
Teşekkürler.


Bu ifadeler nerede? İdeal olarak onları kurucuya koyun. Bunları UpdateStylesayarladıktan sonra aradın mı? Kötü bir şekilde belgelenmiştir, ancak bazen gerekli olabilir.
Thomas

Yanıtlar:


307

Çift arabelleğe almanın çözebileceği bir titreşim türü değildir. BeginUpdate veya SuspendLayout. Çok fazla kontrolünüz var, BackgroundImage bunu çok daha kötü hale getirebilir .

UserControl kendi kendini boyadığında başlar. BackgroundImage'ı çizerek çocuk kontrol pencerelerinin gittiği yerde delikler bırakır. Her çocuk kontrol daha sonra kendini boyaması için bir mesaj alır, deliği pencere içeriğiyle doldururlar. Çok fazla kontrole sahip olduğunuzda, bu delikler kullanıcı tarafından bir süre görülebilir. Normalde beyazdırlar ve karanlık olduğunda BackgroundImage ile kötü bir kontrast oluştururlar. Veya formun Opacity veya TransparencyKey özelliği varsa, hemen hemen her şeyle kötü bir tezat oluşturacak şekilde siyah olabilirler.

Bu, Windows Forms'un oldukça temel bir sınırlamasıdır, Windows'un pencereleri işleme biçimine bağlı kalmıştır. WPF btw tarafından düzeltildi, alt kontroller için pencere kullanmıyor. İstediğiniz şey, alt denetimler de dahil olmak üzere tüm formu çift arabelleğe almaktır. Bu mümkün, çözüm için bu başlıktaki kodumu kontrol edin . Yine de yan etkileri vardır ve aslında boyama hızını artırmaz. Kod basittir, bunu formunuza yapıştırın (kullanıcı kontrolüne değil):

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

Boyama hızını artırmak için yapabileceğiniz pek çok şey var, öyle ki artık titreme fark edilmiyor. BackgroundImage ile uğraşarak başlayın. Kaynak görüntü büyük olduğunda ve kontrole uyması için küçültülmesi gerektiğinde gerçekten pahalı olabilirler . BackgroundImageLayout özelliğini "Döşeme" olarak değiştirin. Bu fark edilir bir hızlanma sağlarsa, boyama programınıza geri dönün ve görüntüyü tipik kontrol boyutuyla daha iyi eşleşecek şekilde yeniden boyutlandırın. Veya görüntünün uygun şekilde boyutlandırılmış bir kopyasını oluşturmak için UC'nin OnResize () yönteminde kod yazın, böylece kontrol her yeniden boyandığında yeniden boyutlandırılmasına gerek kalmaz. Bu kopya için Format32bppPArgb piksel formatını kullanın, diğer piksel formatlarından yaklaşık 10 kat daha hızlı işler.

Yapabileceğiniz bir sonraki şey, deliklerin bu kadar belirgin olmasını ve görüntüyle kötü bir şekilde kontrast oluşturmasını önlemektir. Şunları yapabilirsiniz kapatmak , UC için bayrak WS_CLIPCHILDREN stil bayrak, çocuk kontrolleri gitmek alanda resimden önler UC. Bu kodu UserControl'ün koduna yapıştırın:

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

Alt kontroller artık kendilerini arka plan görüntüsünün üstüne boyayacak. Hala teker teker boyadıklarını görebilirsiniz, ancak çirkin ara beyaz veya kara delik görünmez.

Son olarak, çocuk kontrollerinin sayısını azaltmak, yavaş boyama problemlerini çözmek için her zaman iyi bir yaklaşımdır. UC'nin OnPaint () olayını geçersiz kılın ve şimdi bir çocukta gösterileni çizin. Özel Etiket ve PictureBox çok israf edicidir. İşaretle ve tıklamak için uygun, ancak hafif alternatifleri (bir dize veya görüntü çizme) OnPaint () yönteminizde yalnızca tek bir kod satırı alır.


WS_CLIPCHILDREN gelişmiş kullanıcı deneyimini benim için kapat.
Mahesh

Kesinlikle mükemmel! .. Çok teşekkürler
AlejandroAlis

9

Bu gerçek bir mesele ve Hans Passant'ın verdiği cevap, titremeyi kurtarmak için harika. Ancak, bahsettiği gibi yan etkileri vardır ve çirkin (UI çirkin) olabilirler. Belirtildiği gibi, " WS_CLIPCHILDRENUC için stil bayrağını kapatabilirsiniz ", ancak bu yalnızca bir UC için kapatır. Ana formdaki bileşenlerin hala sorunları var.

Örneğin, bir panel kaydırma çubuğu teknik olarak alt alanda olduğu için boyamıyor. Ancak alt bileşen kaydırma çubuğunu çizmez, bu nedenle fare üzerine gelene kadar (veya başka bir olay tetikleyene) kadar boyanmaz.

Ayrıca, animasyonlu simgeler (bekleme döngüsünde simgelerin değiştirilmesi) çalışmaz. Bir üzerindeki simgelerin kaldırılması tabPage.ImageKey, diğer sekme sayfalarını uygun şekilde yeniden boyutlandırmaz / yeniden boyamaz.

Bu yüzden WS_CLIPCHILDREN, ilk boyamayı kapatmanın bir yolunu arıyordum, böylece Formum güzel bir şekilde boyanmış olarak yüklenecek veya daha iyisi, yalnızca formumu birçok bileşenle yeniden boyutlandırırken açacak.

İşin püf noktası uygulamanın aramasını sağlamaktır CreateParams istenen WS_EX_COMPOSITED/WS_CLIPCHILDRENstille sağlamaktır . Burada bir hack buldum ( https://web.archive.org/web/20161026205944/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of- flicker-on-windows-formları-applications.aspx ) ve harika çalışıyor. AngryHacker teşekkürler!

Ben koymak TurnOnFormLevelDoubleBuffering()formu aramayı ResizeBeginolay ve TurnOffFormLevelDoubleBuffering()biçim ResizeEnd olay çağrısı (veya sadece bırakın WS_CLIPCHILDRENbaşlangıçta düzgün boyanmıştır sonra.)

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

Kodunuz TurnOnFormLevelDoubleBuffering () yöntemini içermiyor ...
Dan W

@DanW Bu yanıtta yayınlanan URL'ye bir göz atın ( angryhacker.com/blog/archive/2010/07/21/… )
ChrisB

Bu yanıttaki bağlantı ölü görünüyor. Çözümü merak ediyorum, başka bir örnekle bağlantınız var mı?
Pratt Hinds

6

Kontrolde herhangi bir özel boyama yapıyorsanız (yani OnPaint'i geçersiz kılıyorsanız), çift arabelleğe almayı kendiniz deneyebilirsiniz.

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

Ve bir mülkle kontrolünüzü geçersiz kılın NeedRepaint

Aksi takdirde, SuspendLayout ve ResumeLayout ile yukarıdaki yanıt muhtemelen istediğiniz şeydir.


Bu, çift arabelleği simüle etmek için yaratıcı bir yöntemdir !. Daha if (image != null) image.Dispose();önce ekleyebilirsinizimage = new Bitmap...
S.Serpooshan


2

Arka plan resminin bulunduğu ana formda veya kullanıcı denetiminde BackgroundImageLayoutözelliği Centerveya olarak ayarlayın Stretch. Kullanıcı kontrolü oluşturulurken büyük bir fark göreceksiniz.


2

Bunu yorum olarak eklemeye çalıştım ama yeterli puanım yok. Hans'a gönderisi için teşekkürler, titreyen sorunlarıma yardımcı olan tek şey bu. Benim gibi c ++ oluşturucuyu kullanan herkes için çeviri burada

CreateParams bildirimini uygulamanızın ana form .h dosyasına ekleyin, örn.

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

ve bunu .cpp dosyanıza ekleyin

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

2

Kodu kurucunuza veya OnLoad olayına aşağıya koyun ve alt kontrollere sahip bir tür özel kullanıcı kontrolü kullanıyorsanız, bu özel kontrollerin de çift arabelleğe alındığından emin olmanız gerekir (MS belgelerinde dedikleri halde varsayılan olarak true olarak ayarlanmıştır).

Özel bir kontrol yapıyorsanız, bu bayrağı ctor'unuza eklemek isteyebilirsiniz:

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

İsteğe bağlı olarak bu kodu Formunuzda / Kontrolünüzde kullanabilirsiniz:

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

Form / kontroldeki tüm kontrolleri yineliyoruz ve özelliklerine erişiyoruz DoubleBufferedve ardından form üzerindeki her kontrolü çift tamponlu hale getirmek için bunu true olarak değiştiriyoruz. Burada derinlemesine düşünmemizin nedeni, erişilebilir olmayan çocuk kontrollerine sahip bir kontrole sahip olduğunuzu hayal etmektir, bu şekilde, özel kontroller olsalar bile, mülkiyetlerini doğru olarak değiştireceğiz.

Çift tamponlama tekniği hakkında daha fazla bilgiyi burada bulabilirsiniz .

Bu sorunu çözmek için genellikle geçersiz kıldığım başka bir özellik var:

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

WS_EX_COMPOSITED - Çift arabellek kullanarak aşağıdan yukarıya boyama sırasına göre bir pencerenin tüm soyundan gelenleri boyar.

Bu stil bayraklarından daha fazlasını burada bulabilirsiniz .

Umarım yardımcı olur!


1

Sadece Hans'ın verdiği cevaba eklemek için:

(TLDR sürümü: Şeffaflık düşündüğünüzden daha ağırdır, her yerde yalnızca düz renkler kullanın)

WS_EX_COMPOSITED, DoubleBuffered ve WS_CLIPCHILDREN titremenizi çözmediyse (benim için WS_CLIPCHILDREN bunu daha da kötüleştirdi), şunu deneyin: TÜM kontrollerinizi ve tüm kodunuzu ve BackColor, ForeColor için herhangi bir şeffaflığa veya yarı saydamlığa sahip olduğunuz her yerde gözden geçirin. başka herhangi bir renk, sadece çıkarın, yalnızca düz renkler kullanın. Sadece sahip olduğunuzu düşündüğünüz çoğu durumdaŞeffaflığı kullanmanız , kullanmazsınız. Kodunuzu ve kontrollerinizi yeniden tasarlayın ve düz renkler kullanın. Korkunç, korkunç titriyordum ve program yavaş çalışıyordu. Şeffaflığı kaldırdıktan sonra önemli ölçüde hızlandı ve 0 titreme var.

DÜZENLEME: Daha fazla eklemek için, WS_EX_COMPOSITED'in pencere çapında olması gerekmediğini keşfettim, sadece belirli kontrollere uygulanabilir! Bu beni çok fazla zahmetten kurtardı. İhtiyacınız olan herhangi bir kontrolden miras alınan özel bir kontrol yapın ve WS_EX_COMPOSITED için önceden yayınlanmış geçersiz kılmayı yapıştırın. Bu şekilde, uygulamanın geri kalanındaki kötü yan etkilerden kaçınarak, yalnızca bu kontrolde düşük seviyeli çift arabellek elde edersiniz!


0

Bu sorunun çok eski olduğunu biliyorum, ancak deneyimlerimi vermek istiyorum.

TabcontrolGeçersiz kılınmış bir formda OnPaintve / veya OnPaintBackGround.NET 4.0 kullanan Windows 8'de titremeyle ilgili birçok sorun yaşadım .

İşeGraphics.DrawImage yarayan tek düşünce , yöntemi OnPaintgeçersiz kılmalarda KULLANMAMAK , başka bir deyişle, doğrudan çizim tarafından sağlanan Grafiklere yapıldığında PaintEventArgs, hatta tüm dikdörtgeni boyadığında, titreme kayboldu. Ancak DrawImageyöntemi çağırırsanız, kırpılmış bir Bitmap çizseniz bile (çift arabelleğe alma için oluşturulmuş) titreme belirir.

Umarım yardımcı olur!


0

Ben kombine bu titreşim düzeltme ve bu yazı düzeltme Ben offscreen ve sırt, vb gittiğinde geçersiz tabcontrol boya bir zamanlayıcı başlatmak için kendi kod biraz eklemek zorunda, sonra ..

Üçü de şunu yapıyor:

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

Ben yaratıcı değilim ama anladığım kadarıyla bit eşlem tüm hatayı atlayarak yapıyor.

TabControl (Simgeler ile) titremesini benim için kesin olarak çözen tek şey buydu.

fark sonuç videosu: vanilya tabcontrol vs tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

ps. HotTrack = true ayarlamanız gerekecek, çünkü bu da bu hatayı düzeltir


-2

Denediniz mi Control.DoubleBufferedMülkiyet?

Bu denetimin titremeyi azaltmak veya önlemek için ikincil bir arabellek kullanarak yüzeyini yeniden çizmesi gerekip gerekmediğini gösteren bir değer alır veya ayarlar.

Ayrıca bu ve bu yardımcı olabilir.


-9

Çifte arabelleğe almaya gerek yok ve tüm bu şeyler çocuklar ...

Basit bir çözüm ...

MDI Arayüzü kullanıyorsanız, aşağıdaki kodu ana forma yapıştırmanız yeterlidir. Sayfalardaki tüm titremeleri kaldıracaktır. Ancak yüklenmesi daha fazla zaman gerektiren bazı sayfalar 1 veya 2 saniye içinde görünecektir. Ancak bu, her öğenin tek tek geldiği titreyen bir sayfa göstermekten daha iyidir.

Bu, tüm uygulama için en iyi çözümdür. Ana forma yerleştirilecek koda bakın:

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

12
Öyleyse, Hans'ın iki yıl önce verdiği cevabın aslında doğru olduğunu söylüyorsunuz? Teşekkür ederim Kshitiz. Bu gerçekten çok yardımcı oldu!
Fernando
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.