web-dev-qa-db-ja.com

更新時にc#のリストビューがちらつく

定期的に(60秒ごとに)更新されるリストビューがあります。それが更新されるたびにちらつきが発生するのは私にとって悩みの種でした。使用されている方法は、すべてのアイテムをクリアしてから再作成することでした。新しいテキストでセルに直接書き込むだけのアイテムをクリアする代わりに決めました。これはより良いアプローチですか、それとも誰かがより良いソリューションを持っていますか?.

50
Brad

ListViewコントロールにはちらつきの問題があります。問題は、コントロールの更新オーバーロードが不適切に実装されているため、更新のように動作することです。更新によってコントロールの無効な領域のみが再描画され、更新によってコントロールのクライアント領域全体が再描画されるはずです。たとえば、リスト内の1つのアイテムの背景色を変更する場合、その特定のアイテムのみを再描画する必要があります。残念ながら、ListViewコントロールは意見が異なるようであり、1つのアイテムをいじるたびに、そのアイテムが現在表示されていない場合でも、その表面全体を再描画したいと考えています。したがって、とにかく、次のように独自にロールすることで、ちらつきを簡単に抑えることができます。

class ListViewNF : System.Windows.Forms.ListView
{
    public ListViewNF()
    {
        //Activate double buffering
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);

        //Enable the OnNotifyMessage event so we get a chance to filter out 
        // Windows messages before they get to the form's WndProc
        this.SetStyle(ControlStyles.EnableNotifyMessage, true);
    }

    protected override void OnNotifyMessage(Message m)
    {
        //Filter out the WM_ERASEBKGND message
        if(m.Msg != 0x14)
        {
            base.OnNotifyMessage(m);
        }
    }
}

From: Geekswithblogs.net

90
Stormenet

他の応答に加えて、多くのコントロールには[Begin|End]Update()メソッドがあり、これを使用してコンテンツを編集するときのちらつきを減らすことができます-例えば:

    listView.BeginUpdate();
    try {
        // listView.Items... (lots of editing)
    } finally {
        listView.EndUpdate();
    }
22
Marc Gravell

リストビューなどのサブクラス化を必要としないC#実装のクイックフィックスを次に示します。

リフレクションを使用してDoubleBufferedプロパティを設定し、フォームコンストラクターで試行します。

    lvMessages
        .GetType()
        .GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
        .SetValue(lvMessages, true, null);
8
WraithNath

はい、ダブルバッファーにします。ちらつきを軽減します;) http://msdn.Microsoft.com/en-us/library/system.windows.forms.listview.doublebuffered.aspx

5
Gonzalo Quero

これが役立つ場合は、次のコンポーネントが.NET 3.5でのListViewのちらつきの問題を解決しました

[ToolboxItem(true)]
[ToolboxBitmap(typeof(ListView))]
public class ListViewDoubleBuffered : ListView
{
    public ListViewDoubleBuffered()
    {
        this.DoubleBuffered = true;
    }
}

ListView.Itemsの操作を行う.BeginUpdate()および.EndUpdate()メソッドと組み合わせて使用​​します。

このプロパティが保護されている理由がわかりません... .NET 4.5でも(おそらくセキュリティの問題)

5

すばらしい質問とストーメネントの答えはすぐにわかりました。 C++/CLIの実装に取り​​組んでいる可能性のある他の人のための彼のコードのC++ポートです。

#pragma once

#include "Windows.h" // For WM_ERASEBKGND

using namespace System;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;

public ref class FlickerFreeListView : public ListView
{
public:
    FlickerFreeListView()
    {
        //Activate double buffering
        SetStyle(ControlStyles::OptimizedDoubleBuffer | ControlStyles::AllPaintingInWmPaint, true);

        //Enable the OnNotifyMessage event so we get a chance to filter out 
        // Windows messages before they get to the form's WndProc
        SetStyle(ControlStyles::EnableNotifyMessage, true);
    }

protected:
    virtual  void OnNotifyMessage(Message m) override
    {
        //Filter out the WM_ERASEBKGND message
        if(m.Msg != WM_ERASEBKGND)
        {
            ListView::OnNotifyMessage(m);
        }
    }

};
4
Jon Cage

最も簡単なソリューションはおそらく使用するでしょう

       listView.Items.AddRange(listViewItems.ToArray());

の代わりに

       foreach (ListViewItem listViewItem in listViewItems)
       {
           listView.Items.Add(listViewItem);
       }

これはうまく機能します。

3
Sifty

次の拡張クラスを使用して、DoubleBufferedプロパティをtrueに設定できます。

using System.Reflection;

public static class ListViewExtensions
{
    public static void SetDoubleBuffered(this ListView listView, bool value)
    {
        listView.GetType()
            .GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
            .SetValue(listView, value);
    }
}
2
Ghost4Man

シンプルなソリューション

yourlistview.BeginUpdate()

//Do your update of adding and removing item from the list

yourlistview.EndUpdate()
1
jaiveeru

それが価値があることのために、私の場合、私は単に呼び出しを追加する必要がありました

Application.EnableVisualStyles()

次のように、アプリケーションを実行する前に:

    private static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new Form1());
    }

それ以外の場合、ダブルバッファリングでは不十分です。多分それは非常に古いプロジェクトであり、新しいプロジェクトはデフォルトでその設定を持っています...

0
Fred Mauroy

これは極端に古い質問と回答であることを知っています。ただし、これは「C++/cli listview flicker」を検索する場合の最高の結果です-これはC++についても言及していないという事実にもかかわらずです。これがC++バージョンのこれです:

これをメインフォームのヘッダーファイルに配置します。他の場所に配置することもできます...

static void DoubleBuffer(Control^ control, bool enable) {
    System::Reflection::PropertyInfo^ info = control->GetType()->
        GetProperty("DoubleBuffered", System::Reflection::BindingFlags::Instance 
            | System::Reflection::BindingFlags::NonPublic);
    info->SetValue(control, enable, nullptr);
}

マネージドC++の同様の答えを探してここにたどり着いた場合、それは私にとってはうまくいきます。 :)

0
Chris Parker

Winrt Windows Phone 8.1では、次のコードを設定してこの問題を修正できます。

<ListView.ItemContainerTransitions>
    <TransitionCollection/>      
</ListView.ItemContainerTransitions>
0

ダブルバッファリングプロパティをtrueに設定してみてください。

また、使用することができます:

this.SuspendLayout();

//update control

this.ResumeLayout(False);

this.PerformLayout();
0
Ironicnet

これは私に最適です。
セルを直接編集しているため、この場合の最善の解決策は、テーブル全体ではなく、特定のセル/行を単純にリフレッシュ/リロードすることです。
リストビューの指定された範囲のアイテム/行のみを基本的に再描画するRedrawItems(...)メソッドを使用できます。

public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly);

参照

これは、完全にリストビューのフリッカーを完全に取り除きました。
更新中は、関連するアイテム/レコードのみが点滅します。

乾杯!

0
murphy1310