ListView - 如何防止递归点击? [英] ListView - How do I prevent recursive clicks?

查看:54
本文介绍了ListView - 如何防止递归点击?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个listView,第一项是全选。我可以使用ItemChecked事件选择和取消选择listView中的所有项目 - 当用户点击全选复选框时很容易。当用户单击全选然后取消选择列表中的一个项目时,我的问题就出现了...我想取消选中全选项目,但它再次执行ItemChecked事件

,一切都搞砸了。有些项目被点击有些没有。我唯一的代码是以下(我扔了其他所有东西 - 太令人沮丧了):



表单加载事件:

I have a listView, first item is "Select All". I can select and deselect all the items in the listView using the ItemChecked event - easily when a user clicks on the "Select All" checkbox. My problem comes in when a user has clicked "Select All" and then de-selects one of the items in the list...I want to un-check the "Select All" item only but it does the ItemChecked event again
and everything get's messed up. Some items are clicked some aren't. The only code I have is the following (I've tossed everything else - so frustrating):

Form Load event:

listView1.Items.Add("SELECT ALL");
listView1.Items.Add("ITEM 1");
listView1.Items.Add("ITEM 2");
listView1.Items.Add("ITEM 3");
listView1.Items.Add("ITEM 4");





函数调用(工作正常,非常简单):



Function called (works fine, very easy):

public static bool SetListItemsCheckAll(ListView l, ItemCheckedEventArgs e)
{
    try
    {
        if (e.Item.Text == "SELECT ALL")
        {
            if (e.Item.Checked)
            {
                foreach (ListViewItem li in l.Items)
                {
                    li.Checked = true;
                }
            }
            else if (!e.Item.Checked)
            {
                foreach (ListViewItem li in l.Items)
                {
                    li.Checked = false;
                }
            }
        }

        return true;
    }
    catch (Exception exc)
    {
        string ex;
        ex = exc.Message.ToString();
        MessageBox.Show(ex, "Error - List Selection All", MessageBoxButtons.OK);
        return false;
    }
}







private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
{
    ret = SetListItemsCheckAll(listView1, e);
}





任何帮助将不胜感激。谢谢。



Any help would be greatly appreciated. Thanks.

推荐答案

添加一个字段以防止重入:

Add a field to guard against reentrancy:
private bool _inListView1ItemChecked;

private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
{
    if (!_inListView1ItemChecked)
    {
        _inListView1ItemChecked = true;
        try
        {
            SetListItemsCheckAll(listView1, e);
        }
        finally
        {
            _inListView1ItemChecked = false;
        }
    }
}





您可能还想更新您的方法,以便它不会不要试图改变当前项目的状态:



You might also want to update your method so that it doesn't try to change the state of the current item:

public static void SetListItemsCheckAll(ListView l, ItemCheckedEventArgs e)
{
    if (e.Item.Text == "SELECT ALL")
    {
        // Select / de-select all items:
        foreach (ListViewItem li in l.Items)
        {
            if (li != e.Item)
            {
                li.Checked = e.Item.Checked;
            }
        }
    }
    else if (!e.Item.Checked)
    {
        // De-select the "Select All" item:
        foreach (ListViewItem li in l.Items)
        {
            if (li.Text == "SELECT ALL")
            {
                li.Checked = false;
            }
        }
    }
}


另一种方法... 不使用bool变量来防止递归 ...是使用ListView的'ItemCheck事件,这是'预览事件......在'ItemChecked之前调用它。



当'SelectAll CheckBox的CheckState发生变化时,通过断开ItemCheck EventHandler,我们可以继续将所有CheckBox设置为相同的状态;然后,我们在完成后重新连接ItemCheck EventHandler,因此恢复所有其他CheckBox的正常使用。
Another way to go about this ... without using a bool variable to prevent recursion ... is to use the ListView 'ItemCheck event which is a 'preview Event ... it's called before 'ItemChecked.

By "dis-connecting" the ItemCheck EventHandler when the 'SelectAll CheckBox has its CheckState changed, we can then go ahead and set all the CheckBoxes to that same state; then, we re-connect the ItemCheck EventHandler when we're done, so "normal" use is restored for all the other CheckBoxes.
private void listView1_ItemCheck(object sender, ItemCheckEventArgs e)
{
    // note we assume "select all" is the first ListViewItem
    if (e.Index == 0)
    {
        bool isCheckAll = e.NewValue == CheckState.Checked; 
        
        listView1.SuspendLayout();

        // let the user know what' next
        listView1.Items[0].Text =  isCheckAll 
            ? "Deselect All"
            : "Select All";

        // disconnect the EventHandler
        listView1.ItemCheck -= listView1_ItemCheck;

        // set CheckState of all but 0th. item
        for (int i = 1; i < listView1.Items.Count; i++)
        {
            listView1.Items[i].Checked = isCheckAll;
        }

        // re-connect the EventHandler
        listView1.ItemCheck += listView1_ItemCheck;

        listView1.ResumeLayout();
    }
}

注意:



1.使用变量防止递归这样做是没有错的!



2. WinForm ListView是一个古老的生物,您可能会注意到,与其他控件相比,结构和语法不一致。许多其他WinForm控件具有等同于ListView的'ItemCheck事件,其名称以'Before ...开头,并且那些事件处理程序通常具有内置方式以允许您取消补充'After ...事件处理程序。



3.对于生产代码:我会选择跟踪所有ListView项目的状态(而不是'Select / DeSelect项)以便随时它们都被选中或未选中,'Select / DeSelect ListViewItem的文本将反映其下一个CheckState将执行的操作。但是,这变得很奇特:你实现了,你的客户/雇主应该多付钱给你:)

Notes:

1. there's nothing wrong doing this another way using a variable to prevent recursion !

2. the WinForm ListView is an old creature, and, you may notice, is inconsistent in structure and syntax compared to other Controls. Many other WinForm Controls have an equivalent to the 'ItemCheck Event of the ListView whose name starts with 'Before..., and those Event Handlers often have a built-in way to allow you to cancel the complementary 'After... Event Handler.

3. for production code: I would choose to keep track of the state of all the ListView Items (not the 'Select/DeSelect item) so that any time they were all either checked, or unchecked, the Text of the 'Select/DeSelect ListViewItem would reflect what its next CheckState will do. But, that's getting 'fancy: you implement that, your client/employer should pay you more :)


这篇关于ListView - 如何防止递归点击?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆