Updates in the DataSource reset CheckedListBox checkboxes

为君一笑 提交于 2021-02-10 20:25:08

问题


I have a CheckedListBox, binded to a BindingList:

private BindingList<string> list = new BindingList<string>();
public MyForm()
{
    InitializeComponent();

    list.Add("A");
    list.Add("B");
    list.Add("C");
    list.Add("D");

    checkedListBox.DataSource = collection;
}

When a certain button is clicked the list is updated:

private void Button_Click(object sender, EventArgs e)
{
    list.Insert(0, "Hello!");
}

And it works fine, the CheckedListBox is updated. However, when some of the items are checked, clicking the button not only updates the list but reset all the items to be unchecked. How can I fix that?

Thanks!


回答1:


You need to track check-state yourself.

As an option, you can create a model class for items containing text and check state. Then in ItemCheck event of the control, set check state value of the item model. Also in ListChenged event of your BindingList<T> refresh check-state of items.

Example

Create CheckedListBoxItem class:

public class CheckedListBoxItem
{
    public CheckedListBoxItem(string text)
    {
        Text = text;
    }
    public string Text { get; set; }
    public CheckState CheckState { get; set; }
    public override string ToString()
    {
        return Text;
    }
}

Setup the CheckedListBox like this:

private BindingList<CheckedListBoxItem> list = new BindingList<CheckedListBoxItem>();
private void Form1_Load(object sender, EventArgs e)
{
    list.Add(new CheckedListBoxItem("A"));
    list.Add(new CheckedListBoxItem("B"));
    list.Add(new CheckedListBoxItem("C"));
    list.Add(new CheckedListBoxItem("D"));
    checkedListBox1.DataSource = list;
    checkedListBox1.ItemCheck += CheckedListBox1_ItemCheck;
    list.ListChanged += List_ListChanged;
}
private void CheckedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
    ((CheckedListBoxItem)checkedListBox1.Items[e.Index]).CheckState = e.NewValue;
}
private void List_ListChanged(object sender, ListChangedEventArgs e)
{
    for (var i = 0; i < checkedListBox1.Items.Count; i++)
    {
        checkedListBox1.SetItemCheckState(i,
            ((CheckedListBoxItem)checkedListBox1.Items[i]).CheckState);
    }
}



回答2:


Another method. It doesn't require the support of a custom class (tries not to).

Since the underlying list of items is, in this case, unmanaged (managed elsewhere), the checked state of the items must be handled manually.
It can be interesting to see what is the sequence of the events raised when an item is added or removed from the BindingList (e.g., there are no events that notify a list change before the list is updated, ListChangedEventArgs.OldIndex is never set, thus always -1, etc.).

Since the source of the CheckedListBox is a simple List<string>, when the list is updated, the Item's CheckState is lost. Hence, these states need to be stored an re-applied when required, calling the SetItemCheckedState method.

Since the Items' state must be adjusted to match the new list composition (after an item has beed removed or inserted) and this procedure is synchronous, the ItemCheck event (used to update all items CheckState) is raised off-beat and requires deferred execution. That's why BeginInvoke() is used here.

All in all, a specialized class object that stores these states internally, as shown in Reza Aghaei's answer, is the way to go. Here, binding suffers from lack of support in the base classes. Not that it was anywhere stated otherwise: CheckedListBox.DataSource is not even browsable, for example.

private BindingList<string> clbItemsList = new BindingList<string>();
public MyForm()
{
    InitializeComponent();
    clbItemsList.Add("A");
    // (...)
    checkedListBox1.DataSource = clbItemsList;
    clbItemsList.ListChanged += this.clbListChanged;
    checkedListBox1.ItemCheck += (s, e) => { BeginInvoke(new Action(()=> CheckedStateCurrent())); };
}

private void clbListChanged(object sender, ListChangedEventArgs e)
{
    foreach (var item in clbCheckedItems.ToArray()) {
        if (e.ListChangedType == ListChangedType.ItemAdded) {
            checkedListBox1.SetItemCheckState(item.Index >= e.NewIndex ? item.Index + 1 : item.Index, item.State);
        }
        if (e.ListChangedType == ListChangedType.ItemDeleted) {
            if (item.Index == e.NewIndex) { 
                clbCheckedItems.Remove(item);
                continue;
            }
            checkedListBox1.SetItemCheckState(item.Index > e.NewIndex ? item.Index - 1 : item.Index, item.State);
        }
    }
}

private List<(CheckState State, int Index)> clbCheckedItems = new List<(CheckState State, int Index)>();
private void CheckedStateCurrent()
{
    clbCheckedItems = checkedListBox1.CheckedIndices.OfType<int>()
        .Select(item => (checkedListBox1.GetItemCheckState(item), item)).ToList();
}


来源:https://stackoverflow.com/questions/57607243/updates-in-the-datasource-reset-checkedlistbox-checkboxes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!