Сохраняйте контрольное состояние элементов в контрольном списке при поиске c# winforms - PullRequest
3 голосов
/ 11 марта 2020

Итак, я работаю над фильтром на основе текстового поля для набора элементов в поле Checked List Box в winforms, используя c#.

Пока у меня есть этот код:

List<string> liscollection = new List<string>();
        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(textBox1.Text) == false)
            {
                checkedListBox1.Items.Clear();

                foreach (string str in liscollection)
                {
                    if (str.Contains(textBox1.Text, StringComparison.OrdinalIgnoreCase))
                    {
                        checkedListBox1.Items.Add(str);
                    }
                }
            }
            else if (textBox1.Text == "")
            {
                checkedListBox1.Items.Clear();
                foreach (string str in liscollection)
                {
                    checkedListBox1.Items.Add(str);
                }
            }
        }

Работает нормально, когда я ввожу текст в TextBox1, все элементы, не содержащие введенный текст, исчезают. Проблема доходит до проверенного состояния на каждом элементе. Каждый раз checkListBox1.Items.Clear (); вызывается, проверенное состояние также очищается.

Есть ли способ, который я мог бы использовать, чтобы фильтр работал, но не очищать проверенное состояние элементов?

Я искал Некоторое время назад я не могу ничего найти по этому вопросу, и я новичок, и не могу придумать решение самостоятельно.

Большое спасибо заранее за ваше время!

И извините, мой английский sh, это не мой родной язык:)

1 Ответ

3 голосов
/ 12 марта 2020

CheckListBox.Items имеет тип ObjectCollection, что означает, что он будет принимать любые object, а не только string. По умолчанию CheckedListBox будет отображать результаты ToString в виде текста элемента.

Это означает, что вы можете написать class для представления элементов, хранящихся в CheckedListBox, которые отслеживают их владеть свойством CheckedState, затем переопределить метод ToString, чтобы он отображал нужный текст.

// CheckedListBoxItem.cs
public class CheckedListBoxItem
{
    /// <summary>
    /// The item's text - what will be displayed in the CheckedListBox.
    /// </summary>
    public string Text { get; set; }

    /// <summary>
    /// The item's check state.
    /// </summary>
    public CheckState CheckState { get; set; } = CheckState.Unchecked;

    /// <summary>
    /// Whether the item is checked or not.
    /// </summary>
    public bool Checked
    {
        get
        {
            return (CheckState == CheckState.Checked || CheckState == CheckState.Indeterminate);
        }
        set
        {
            if (value)
            {
                CheckState = CheckState.Checked;
            }
            else
            {
                CheckState = CheckState.Unchecked;
            }
        }
    }

    public bool Contains(string str, StringComparison comparison)
    {
        return Text.IndexOf(str, comparison) >= 0;
    }

    public override string ToString()
    {
        return Text;
    }
}

Поведение CheckedListBoxItem.Checked основано на CheckedListBox.GetItemChecked который обрабатывает CheckState.Interetminate как проверено, и CheckedListBox.SetItemChecked, который обрабатывает true как CheckState.Checked и false как CheckState.Unchecked.

В вашем Form вы бы затем измените тип lstcollection на List<CheckedListBoxItem> и установите для свойства Text каждого элемента строку, которая у вас есть сейчас.

CheckedListBox никак не может связать CheckState каждого элемента, поэтому вам придется управлять этим самостоятельно. К счастью, есть событие CheckedListBox.ItemChecked, которое будет срабатывать при каждом изменении проверенного состояния элемента. Таким образом, вы можете обработать событие с помощью функции, подобной ...

private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
    // Since each item in the CheckedListBox is of type CheckedListBoxItem, we can
    // just cast to that type...
    CheckedListBoxItem item = checkedListBox1.Items[e.Index] as CheckedListBoxItem;

    // Then set the checked state to the new value.
    item.CheckState = e.NewValue;
}

Ваша функция фильтра остается в основном неизменной, но когда вы добавляете элементы в CheckedListBox, вам нужно передать CheckedState а также ...

private List<CheckedListBoxItem> liscollection;

private void textBox1_TextChanged(object sender, EventArgs e)
{
    checkedListBox1.Items.Clear();
    if (string.IsNullOrEmpty(textBox1.Text) == false)
    {
        foreach (var item in liscollection)
        {
            if (item.Contains(textBox1.Text, StringComparison.OrdinalIgnoreCase))
            {
                checkedListBox1.Items.Add(item, item.CheckState);
            }
        }
    }
    else
    {
        foreach (var item in liscollection)
        {
            checkedListBox1.Items.Add(item, item.CheckState);
        }
    }
}

РЕДАКТИРОВАТЬ

Для такого случая я бы не стал добавлять элементы к checkedListBox1 во время разработки. Я бы добавил их к liscollection во время выполнения, а затем добавил liscollection к checkedListBox1.Items.

public class YourForm : Form
{
    public YourForm()
    {
        InitializeComponent();

        // You would add one item to liscollection for each item that you have in the checkedListBox1's designer and set the Text to whatever the item is now...
        liscollection = new List<CheckedListBoxItem>
        {
           new CheckedListBoxItem { Text = "The" },
           new CheckedListBoxItem { Text = "needs" },
           new CheckedListBoxItem { Text = "of" },
           new CheckedListBoxItem { Text = "the" },
           new CheckedListBoxItem { Text = "many" },
           new CheckedListBoxItem { Text = "outweigh" },
           new CheckedListBoxItem { Text = "the" },
           new CheckedListBoxItem { Text = "needs" },
           new CheckedListBoxItem { Text = "of" },
           new CheckedListBoxItem { Text = "the" },
           new CheckedListBoxItem { Text = "few" },
        };
        checkedListBox1.Items.AddRange(liscollection.ToArray());
    }
}

Если вы действительно предпочитаете заполнять checkedListBox1 из конструктора, вы тоже можете это сделать. Вам просто нужно позвонить checkedListBox1.Items.Clear() после заполнения liscollection и до вызова checkedListBox1.Items.AddRange(...)

public class YourForm : Form
{
    public YourForm()
    {
        InitializeComponent();

        liscollection = new List<CheckedListBoxItem>();
        foreach(string str in checkedListBox1.Items)
        {
            liscollection.Add(new CheckedListBoxItem { Text = str });
        }
        checkedListBox1.Items.Clear();
        checkedListBox1.Items.AddRange(liscollection.ToArray());
    }
}

Важная строка там checkedListBox1.Items.AddRange(liscollection.ToArray()). Вы хотите, чтобы Items были экземплярами CheckedListBoxItem, поэтому в событии ItemCheck вы можете привести элемент к экземпляру CheckedListBoxItem. Заполняя checkedListBox1 из конструктора, вы заполняете его только string s, поэтому при попытке привести к CheckedListBoxItem в событии ItemCheck это не удается. Вот почему вы получаете это NullReferenceException.

...