问题
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