كيفية منع الخفقان في ListView عند تحديث نص ListViewItem واحد؟

StackOverflow https://stackoverflow.com/questions/87795

  •  01-07-2019
  •  | 
  •  

سؤال

كل ما أريده هو تحديث نص ListViewItem بدون رؤية أي وميض.

هذا هو الكود الخاص بي للتحديث (يتم استدعاؤه عدة مرات):

listView.BeginUpdate();
listViewItem.SubItems[0].Text = state.ToString();    // update the state
listViewItem.SubItems[1].Text = progress.ToString(); // update the progress
listView.EndUpdate();

لقد رأيت بعض الحلول التي تتضمن تجاوز المكونات WndProc():

protected override void WndProc(ref Message m)
{
    if (m.Msg == (int)WM.WM_ERASEBKGND)
    {
        m.Msg = (int)IntPtr.Zero;
    }
    base.WndProc(ref m);
}

يقولون أنه يحل المشكلة، ولكن في حالتي لم يحدث ذلك.أعتقد أن هذا لأنني أستخدم الرموز في كل عنصر.

هل كانت مفيدة؟

المحلول 2

لإنهاء هذا السؤال، إليك فئة مساعدة يجب استدعاؤها عند تحميل النموذج لكل ListView أو أي عنصر تحكم مشتق من ListView في النموذج الخاص بك.شكرًا لـ "Brian Gillespie" على تقديم الحل.

public enum ListViewExtendedStyles
{
    /// <summary>
    /// LVS_EX_GRIDLINES
    /// </summary>
    GridLines = 0x00000001,
    /// <summary>
    /// LVS_EX_SUBITEMIMAGES
    /// </summary>
    SubItemImages = 0x00000002,
    /// <summary>
    /// LVS_EX_CHECKBOXES
    /// </summary>
    CheckBoxes = 0x00000004,
    /// <summary>
    /// LVS_EX_TRACKSELECT
    /// </summary>
    TrackSelect = 0x00000008,
    /// <summary>
    /// LVS_EX_HEADERDRAGDROP
    /// </summary>
    HeaderDragDrop = 0x00000010,
    /// <summary>
    /// LVS_EX_FULLROWSELECT
    /// </summary>
    FullRowSelect = 0x00000020,
    /// <summary>
    /// LVS_EX_ONECLICKACTIVATE
    /// </summary>
    OneClickActivate = 0x00000040,
    /// <summary>
    /// LVS_EX_TWOCLICKACTIVATE
    /// </summary>
    TwoClickActivate = 0x00000080,
    /// <summary>
    /// LVS_EX_FLATSB
    /// </summary>
    FlatsB = 0x00000100,
    /// <summary>
    /// LVS_EX_REGIONAL
    /// </summary>
    Regional = 0x00000200,
    /// <summary>
    /// LVS_EX_INFOTIP
    /// </summary>
    InfoTip = 0x00000400,
    /// <summary>
    /// LVS_EX_UNDERLINEHOT
    /// </summary>
    UnderlineHot = 0x00000800,
    /// <summary>
    /// LVS_EX_UNDERLINECOLD
    /// </summary>
    UnderlineCold = 0x00001000,
    /// <summary>
    /// LVS_EX_MULTIWORKAREAS
    /// </summary>
    MultilWorkAreas = 0x00002000,
    /// <summary>
    /// LVS_EX_LABELTIP
    /// </summary>
    LabelTip = 0x00004000,
    /// <summary>
    /// LVS_EX_BORDERSELECT
    /// </summary>
    BorderSelect = 0x00008000,
    /// <summary>
    /// LVS_EX_DOUBLEBUFFER
    /// </summary>
    DoubleBuffer = 0x00010000,
    /// <summary>
    /// LVS_EX_HIDELABELS
    /// </summary>
    HideLabels = 0x00020000,
    /// <summary>
    /// LVS_EX_SINGLEROW
    /// </summary>
    SingleRow = 0x00040000,
    /// <summary>
    /// LVS_EX_SNAPTOGRID
    /// </summary>
    SnapToGrid = 0x00080000,
    /// <summary>
    /// LVS_EX_SIMPLESELECT
    /// </summary>
    SimpleSelect = 0x00100000
}

public enum ListViewMessages
{
    First = 0x1000,
    SetExtendedStyle = (First + 54),
    GetExtendedStyle = (First + 55),
}

/// <summary>
/// Contains helper methods to change extended styles on ListView, including enabling double buffering.
/// Based on Giovanni Montrone's article on <see cref="http://www.codeproject.com/KB/list/listviewxp.aspx"/>
/// </summary>
public class ListViewHelper
{
    private ListViewHelper()
    {
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr handle, int messg, int wparam, int lparam);

    public static void SetExtendedStyle(Control control, ListViewExtendedStyles exStyle)
    {
        ListViewExtendedStyles styles;
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        styles |= exStyle;
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }

    public static void EnableDoubleBuffer(Control control)
    {
        ListViewExtendedStyles styles;
        // read current style
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        // enable double buffer and border select
        styles |= ListViewExtendedStyles.DoubleBuffer | ListViewExtendedStyles.BorderSelect;
        // write new style
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }
    public static void DisableDoubleBuffer(Control control)
    {
        ListViewExtendedStyles styles;
        // read current style
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        // disable double buffer and border select
        styles -= styles & ListViewExtendedStyles.DoubleBuffer;
        styles -= styles & ListViewExtendedStyles.BorderSelect;
        // write new style
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }
}

نصائح أخرى

تعمل الإجابة المقبولة، ولكنها طويلة جدًا، ومشتقة من عنصر التحكم (كما هو مذكور في الإجابات الأخرى) فقط لتمكين التخزين المؤقت المزدوج هو أيضًا مبالغ فيه بعض الشيء.لكن لحسن الحظ لدينا تفكير ويمكننا أيضًا استدعاء الأساليب الداخلية إذا أردنا ذلك (ولكن تأكد مما تفعله!).

بتغليف هذا النهج في طريقة تمديد، سنحصل على فصل دراسي قصير جدًا:

public static class ControlExtensions
{
    public static void DoubleBuffering(this Control control, bool enable)
    {
        var method = typeof(Control).GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic);
        method.Invoke(control, new object[] { ControlStyles.OptimizedDoubleBuffer, enable });
    }
}

والتي يمكن استدعاؤها بسهولة داخل الكود الخاص بنا:

InitializeComponent();

myListView.DoubleBuffering(true); //after the InitializeComponent();

وذهب كل الخفقان.

تحديث

لقد تعثرت هذا السؤال ونتيجة لهذه الحقيقة، ينبغي (ربما) أن تكون طريقة التمديد هي:

public static void DoubleBuffered(this Control control, bool enable)
{
    var doubleBufferPropertyInfo = control.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
    doubleBufferPropertyInfo.SetValue(control, enable, null);
}

يدعم ListView في CommonControls 6 (XP أو الأحدث) التخزين المؤقت المزدوج.ولحسن الحظ، يقوم .NET بتضمين أحدث CommonControls على النظام.لتمكين التخزين المؤقت المزدوج، قم بإرسال رسالة Windows المناسبة إلى عنصر التحكم ListView.

التفاصيل هنا:http://www.codeproject.com/KB/list/listviewxp.aspx

في .NET Winforms 2.0 توجد خاصية محمية تسمى DoubleBuffered.

من خلال الوراثة من ListView، يمكن للمرء تعيين هذه الخاصية المحمية إلى true.سيؤدي هذا إلى تمكين التخزين المؤقت المزدوج دون الحاجة إلى استدعاء SendMessage.

إعداد الخاصية DoubleBuffered هو نفس إعداد النمط التالي:

listview.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94096

أعلم أن هذا السؤال قديم جدًا، ولكن نظرًا لأن هذه إحدى نتائج البحث الأولى على Google، فقد أردت مشاركة الحل الذي قمت به.

كانت الطريقة الوحيدة التي يمكنني من خلالها إزالة الخفقان بنسبة 100٪ هي الجمع بين إجابة أوليفر (فئة الامتداد مع التخزين المؤقت المزدوج) واستخدام BeignUpdate() و EndUpdate() طُرق.

لم يتمكن أي من هؤلاء بمفرده من إصلاح الخفقان بالنسبة لي.من المؤكد أنني أستخدم قائمة معقدة للغاية، وأحتاج إلى إدخالها في القائمة وأحتاج أيضًا إلى تحديثها كل ثانية تقريبًا.

هذا سيساعد:

class DoubleBufferedListView : System.Windows.Forms.ListView
{
    public DoubleBufferedListView()
        :base()
    {
        this.DoubleBuffered = true;
    }
}

الحل البسيط هو هذا:

yourlistview.BeginUpdate()

// قم بتحديث إضافة وإزالة العنصر من القائمة

yourlistview.EndUpdate()

إذا كنت تريد تحديث النص فقط، فما عليك سوى تعيين نص العنصر الفرعي الذي تم تغييره مباشرة بدلاً من تحديث ListViewItem بأكمله (لم تذكر كيف تقوم بالتحديثات).

التجاوز الذي تعرضه يعادل ببساطة تجاوز OnPaintBackground، والذي سيكون طريقة مُدارة "أكثر صحة" للقيام بهذه المهمة، ولن يساعد في عنصر واحد.

إذا كنت لا تزال تواجه مشكلات، فسنحتاج إلى توضيح بشأن ما جربته بالفعل.

هذه لقطة في الظلام، لكن يمكنك تجربة التخزين المؤقت المزدوج لعنصر التحكم.

SetStyle(
  ControlStyles.AllPaintingInWmPaint |
  ControlStyles.UserPaint |
  ControlStyles.DoubleBuffer, true)

قم باستدعاء أسلوب BeginUpdate() في ListView قبل تعيين أي من عناصر عرض القائمة، ثم اتصل بـ EndUpdate() فقط بعد إضافة كافة العناصر.

وهذا سوف يوقف الوميض.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top