سؤال

إذا كان المستخدم تحديد كافة العناصر الموجودة في a .NET 2.0 ListView ، ListView سوف النار SelectedIndexChanged الحدث لكل بند, بدلا من إطلاق الحدث للإشارة إلى أن اختيار قد تغير.

إذا كان المستخدم ثم بالنقر لتحديد عنصر واحد فقط في القائمة ListView سوف النار SelectedIndexChanged الحدث كل العنصر الذي هو على غير محددة ، SelectedIndexChanged الحدث واحد حديثا العنصر المحدد, بدلا من إطلاق الحدث للإشارة إلى أن الاختيار قد تغير.

إذا كان لديك التعليمات البرمجية في SelectedIndexChanged معالج الحدث هذا البرنامج سوف تصبح جميلة عن الاستجابة عند بدء بضع مئات/آلاف العناصر في القائمة.

لقد فكرت يسكن توقيت, ، وما إلى ذلك.

ولكن هل لدى أحدكم حل جيد لتجنب آلاف وغني عن ListView.SelectedIndexChange الأحداث عندما حقا حدث واحد سوف تفعل ؟

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

المحلول

حل جيد من ايان.أخذت وجعله في فئة القابل لإعادة الاستخدام ، مع التأكد من التخلص من جهاز ضبط الوقت بشكل صحيح.أنا أيضا تقليل الفاصل الزمني للحصول على أكثر استجابة التطبيق.هذه السيطرة أيضا doublebuffers لتقليل الوميض.

  public class DoublebufferedListView : System.Windows.Forms.ListView
  {
     private Timer m_changeDelayTimer = null;
     public DoublebufferedListView()
        : base()
     {
        // Set common properties for our listviews
        if (!SystemInformation.TerminalServerSession)
        {
           DoubleBuffered = true;
           SetStyle(ControlStyles.ResizeRedraw, true);
        }
     }

     /// <summary>
     /// Make sure to properly dispose of the timer
     /// </summary>
     /// <param name="disposing"></param>
     protected override void Dispose(bool disposing)
     {
        if (disposing && m_changeDelayTimer != null)
        {
           m_changeDelayTimer.Tick -= ChangeDelayTimerTick;
           m_changeDelayTimer.Dispose();
        }
        base.Dispose(disposing);
     }

     /// <summary>
     /// Hack to avoid lots of unnecessary change events by marshaling with a timer:
     /// http://stackoverflow.com/questions/86793/how-to-avoid-thousands-of-needless-listview-selectedindexchanged-events
     /// </summary>
     /// <param name="e"></param>
     protected override void OnSelectedIndexChanged(EventArgs e)
     {
        if (m_changeDelayTimer == null)
        {
           m_changeDelayTimer = new Timer();
           m_changeDelayTimer.Tick += ChangeDelayTimerTick;
           m_changeDelayTimer.Interval = 40;
        }
        // When a new SelectedIndexChanged event arrives, disable, then enable the
        // timer, effectively resetting it, so that after the last one in a batch
        // arrives, there is at least 40 ms before we react, plenty of time 
        // to wait any other selection events in the same batch.
        m_changeDelayTimer.Enabled = false;
        m_changeDelayTimer.Enabled = true;
     }

     private void ChangeDelayTimerTick(object sender, EventArgs e)
     {
        m_changeDelayTimer.Enabled = false;
        base.OnSelectedIndexChanged(new EventArgs());
     }
  }

لا اسمحوا لي أن أعرف إذا كان هذا يمكن تحسينها.

نصائح أخرى

هذا هو يسكن توقيت الحل أنا أستخدم الآن (يسكن يعني فقط "انتظر قليلا").هذا الرمز قد يعانون من حالة سباق ، ولعل مرجع فارغة استثناء.

Timer changeDelayTimer = null;

private void lvResults_SelectedIndexChanged(object sender, EventArgs e)
{
        if (this.changeDelayTimer == null)
        {
            this.changeDelayTimer = new Timer();
            this.changeDelayTimer.Tick += ChangeDelayTimerTick;
            this.changeDelayTimer.Interval = 200; //200ms is what Explorer uses
        }
        this.changeDelayTimer.Enabled = false;
        this.changeDelayTimer.Enabled = true;
}

private void ChangeDelayTimerTick(object sender, EventArgs e)
{
    this.changeDelayTimer.Enabled = false;
    this.changeDelayTimer.Dispose();
    this.changeDelayTimer = null;

    //Add original SelectedIndexChanged event handler code here
    //todo
}

الموقت هو أفضل الحلول الشاملة.

مشكلة مع ينس اقتراح هو أنه بمجرد أن القائمة لديها الكثير من العناصر المحددة (آلاف أو أكثر), الحصول على قائمة من العناصر المحددة يبدأ يستغرق وقتا طويلا.

بدلا من إنشاء الموقت الكائن في كل مرة الحدث SelectedIndexChanged يحدث, الأمر أبسط إلى وضعت للتو دائم على شكل مع المصمم و قد تحقق متغير منطقية في الصف لمعرفة ما إذا كان أو لم يكن ينبغي أن استدعاء تحديث وظيفة.

على سبيل المثال:

bool timer_event_should_call_update_controls = false;

private void lvwMyListView_SelectedIndexChanged(object sender, EventArgs e) {

  timer_event_should_call_update_controls = true;
}

private void UpdateControlsTimer_Tick(object sender, EventArgs e) {

  if (timer_event_should_call_update_controls) {
    timer_event_should_call_update_controls = false;

    update_controls();
  }
}

هذا يعمل بشكل جيد إذا كنت تستخدم المعلومات ببساطة لأغراض العرض ، مثل تحديث شريط الحالة إلى القول "X من Y المحدد".

العلم يعمل OnLoad الحدث من نموذج windows / نموذج ويب / موبايل شكل.في واحد حدد Listview ، وليس متعدد تحديد التعليمة البرمجية التالية بسيطة لتنفيذ ويمنع متعددة إطلاق الحدث.

كما ListView دي-يحدد البند الأول ، البند الثاني فهو ما عليك وجمع فقط من أي وقت مضى تحتوي على عنصر واحد.

نفس أدناه تم استخدامها في تطبيقات الهاتف المتحرك ، ولذلك فإن بعض من جمع أسماء قد تكون مختلفة كما يتم استخدام ضغط الإطار, ومع ذلك تطبيق نفس المبادئ.

ملاحظة:تأكد من OnLoad تعبئة من listview تعيين العنصر الأول أن يكون محددا.

// ################ CODE STARTS HERE ################
//Flag  to create at the form level
System.Boolean lsvLoadFlag = true;

//Make sure to set the flag to true at the begin of the form load and after
private void frmMain_Load(object sender, EventArgs e)
{
    //Prevent the listview from firing crazy in a single click NOT multislect environment
    lsvLoadFlag = true;

    //DO SOME CODE....

    //Enable the listview to process events
    lsvLoadFlag = false;
}

//Populate First then this line of code
lsvMain.Items[0].Selected = true;

//SelectedIndexChanged Event
 private void lsvMain_SelectedIndexChanged(object sender, EventArgs e)
{
    ListViewItem lvi = null;

    if (!lsvLoadFlag)
    {
        if (this.lsvMain.SelectedIndices != null)
        {
            if (this.lsvMain.SelectedIndices.Count == 1)
            {
                lvi = this.lsvMain.Items[this.lsvMain.SelectedIndices[0]];
            }
        }
    }
}
################ CODE END HERE    ################

من الناحية المثالية, هذا الرمز يجب أن توضع في UserControl لسهولة إعادة استخدام distrbution في واحد حدد ListView.هذه المدونة لن تستخدم كثيرا في تحديد متعدد, كما حدث يعمل كما ينبغي لهذا السلوك.

آمل أن يساعد.

مع أطيب التحيات ،

أنتوني N.أوروين http://www.manatix.com

السؤال القديم أعلم, ولكن هذا لا يزال يبدو أن قضية.

هنا الحل لا تستخدم أجهزة ضبط الوقت.

ينتظر MouseUp أو الحدث KeyUp قبل إطلاق SelectionChanged الحدث.إذا كنت تريد تغيير اختيار برمجيا ، ثم هذا ليس الحدث لن النار ، ولكن يمكنك بسهولة إضافة FinishedChanging الحدث أو ما يؤدي هذا الحدث.

(كما أن لديها بعض الاشياء توقف عن الخفقان وهي ليست ذات الصلة على هذا السؤال).

public class ListViewNF : ListView
{
    bool SelectedIndexChanging = false;

    public ListViewNF()
    {
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
        this.SetStyle(ControlStyles.EnableNotifyMessage, true);
    }

    protected override void OnNotifyMessage(Message m)
    {
        if(m.Msg != 0x14)
            base.OnNotifyMessage(m);
    }

    protected override void OnSelectedIndexChanged(EventArgs e)
    {
        SelectedIndexChanging = true;
        //base.OnSelectedIndexChanged(e);
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        if (SelectedIndexChanging)
        {
            base.OnSelectedIndexChanged(EventArgs.Empty);
            SelectedIndexChanging = false;
        }

        base.OnMouseUp(e);
    }

    protected override void OnKeyUp(KeyEventArgs e)
    {
        if (SelectedIndexChanging)
        {
            base.OnSelectedIndexChanged(EventArgs.Empty);
            SelectedIndexChanging = false;
        }

        base.OnKeyUp(e);
    }
}

يمكنك استخدام async & await:

private bool waitForUpdateControls = false;

private async void listView_SelectedIndexChanged(object sender, EventArgs e)
{
    // To avoid thousands of needless ListView.SelectedIndexChanged events.

    if (waitForUpdateControls)
    {
        return;
    }

    waitForUpdateControls = true;

    await Task.Delay(100);

    waitForUpdateControls = false;

    UpdateControls();

    return;
}

أود إما محاولة ربط إعادة النشر على زر السماح للمستخدم تقديم التغييرات وتحل معالج الحدث.

كنت فقط تحاول معالجة هذه المشكلة أمس.أنا لا أعرف بالضبط ماذا تقصد ب "يسكن" توقيت, ولكن حاولت تنفيذ بلدي جدا نسخة من الانتظار حتى جميع التغييرات تتم.للأسف الطريقة الوحيدة التي يمكنني أن أفكر في أن أفعل هذا في موضوع مستقل و اتضح أنه عند إنشاء موضوع مستقل ، عناصر واجهة المستخدم لا يمكن الوصول إليها في هذا الموضوع..صافي يطرح استثناء مشيرا إلى أن عناصر واجهة المستخدم يمكن الوصول إليها فقط في الموضوع حيث العناصر التي تم إنشاؤها!لذا وجدت طريقة لتحسين بلدي استجابة SelectedIndexChanged وجعله سريع بما فيه الكفاية إلى حيث هو محتمل - لا حل لتحجيم على الرغم من.دعونا نأمل شخص ما لديه فكرة ذكية لمعالجة هذه المشكلة في موضوع واحد.

ربما هذا يمكن أن تساعدك على إنجاز ما تحتاجه دون استخدام أجهزة ضبط الوقت:

http://www.dotjem.com/archive/2009/06/19/20.aspx

أنا لا أحب المستخدم من توقيت إلخ.وأنا أيضا الدولة في ما بعد...

آمل أن يساعد...

أوه نسيت أن أقول إنه .NET framework 3.5 و أنا باستخدام بعض الميزات في linq إلى acomplish "اختيار التغييرات التقييم" إذا كنت يمكن أن نسميها o.O...

على أي حال, إذا كنت على نسخة قديمة ، هذا التقييم يجب أن يتم مع أكثر قليلا من التعليمات البرمجية...>.<...

أوصي من خارج الميزانية الخاصة بك عرض القائمة إذا كان لديه بضع مئات أو آلاف من البنود.

Maylon >>>

والهدف لم يكن العمل مع القائمة أعلاه بضع مئات البنود, ولكن...لقد اختبرت تجربة المستخدم بشكل عام مع 10.000 البنود, تشكيلة 1000-5000 العناصر في وقت واحد (و التغييرات من 1000-3000 العناصر في كل اختيار محدد)...

المدة الإجمالية من حساب لم تتجاوز 0.1 ثانية ، بعض من أعلى القياسات كان 0.04 ثانية, لقد وجدت هذا مقبول تماما مع العديد من العناصر.

وفي 10.000 البنود, فقط تهيئة قائمة يستغرق أكثر من 10 ثوان ، لذلك عند هذه النقطة كنت قد فكرت في أشياء أخرى قد تأتي في اللعب ، الافتراضية كما جو تشونغ نقطة.

إلى أن قال: وينبغي أن يكون واضحا أن المدونة ليست الحل الأمثل في كيفية ذلك بحساب الفرق في الاختيار ، إذا لزم الأمر يمكن أن يكون هذا تحسنت كثيرا و بطرق مختلفة, ركزت على فهم مفهوم مع رمز بدلا من الأداء.

ومع ذلك ، إذا كانت تعاني من تدهور الأداء أنا مهتم جدا في بعض ما يلي:

  • كم عدد العناصر في القائمة ؟
  • كم من تحديد/إلغاء تحديد العناصر في وقت واحد ؟
  • كم من الوقت تقريبا تأخذ هذا الحدث إلى زيادة ؟
  • الأجهزة منصة ؟
  • المزيد عن حالة استخدامها ؟
  • معلومات أخرى ذات صلة يمكن ان يخطر لك ؟

وإلا فإنه ليس من السهل أن تساعد في تحسين الحل.

ترك ListView وجميع الضوابط القديمة.

جعل DataGridView صديقك وجميع سيكون جيدا :)

ريمون تشن لديه بلوق وظيفة التي (ربما) يفسر لماذا هناك الآلاف من أحداث التغيير, بدلا من واحد فقط:

لماذا هناك LVN_ODSTATECHANGED إخطار عندما يكون هناك بالفعل جيدة تماما LVN_ITEMCHANGED الإشعار ؟

...
على LVN_ODSTATECHANGED إعلام يخبرك أن الدولة من جميع الأصناف في النطاق المحدد قد تغير.هذا الاختزال على إرسال الفردية LVN_ITEMCHANGED للجميع العناصر في مجموعة [iFrom..iTo].إذا لديك ownerdata عرض القائمة مع 500 ، 000 العناصر شخص ما لا حدد جميع, سوف تكون سعيدة أنك الحصول على واحد LVN_ODSTATECHANGED الإخطار مع iFrom=0 و iTo=499999 بدلا من نصف مليون فرد الصغير LVN_ITEMCHANGED الإخطارات.

أقول ربما يفسر لماذا لأنه لا يوجد أي ضمان .صافي عرض القائمة هي التفاف حول Listview السيطرة المشتركة - أن تنفيذ التفاصيل الحرة للتغيير في أي وقت (على الرغم من أنه يكاد يكون من المؤكد سوف أبدا).

وقد ألمح الحل هو استخدام .صافي listview في الوضع الظاهري ، مما يجعل السيطرة على أمر من حجم أكثر من الصعب استخدام.

قد يكون أفضل حل.

حالتي:

  • واحد حدد قائمة عرض (بدلا من متعدد اختيار)
  • أريد أن تجنب معالجة الحدث عندما الحرائق deselection سابقا العنصر المحدد.

الحل:

  • سجل عنصر ما على المستخدم النقر على MouseDown
  • تجاهل الحدث SelectedIndexChanged إذا كان هذا البند هو null و SelectedIndexes.العد == 0

كود:

ListViewItem ItemOnMouseDown = null;
private void lvTransactions_MouseDown(object sender, MouseEventArgs e)
{
    ItemOnMouseDown = lvTransactions.GetItemAt(e.X, e.Y);
}
private void lvTransactions_SelectedIndexChanged(object sender, EventArgs e)
{
    if (ItemOnMouseDown != null && lvTransactions.SelectedIndices.Count == 0)
        return;

    SelectedIndexDidReallyChange();

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