【问题标题】:Updates in the DataSource reset CheckedListBox checkboxesDataSource 中的更新重置 CheckedListBox 复选框
【发布时间】:2019-08-22 10:26:24
【问题描述】:

我有一个 CheckedListBox,绑定到一个 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;
}

当点击某个按钮时,列表会更新:

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

它工作正常,CheckedListBox 已更新。但是,当某些项目被选中时,单击按钮不仅会更新列表,还会重置所有未选中的项目。我该如何解决?

谢谢!

【问题讨论】:

    标签: c# winforms data-binding checkedlistbox


    【解决方案1】:

    您需要自己跟踪检查状态。

    作为一个选项,您可以为包含文本和检查状态的项目创建模型类。然后在控件的ItemCheck事件中,设置项目模型的检查状态值。同样在您的BindingList&lt;T&gt; 刷新项目检查状态的ListChenged 事件中。

    示例

    创建CheckedListBoxItem 类:

    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;
        }
    }
    

    像这样设置CheckedListBox

    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);
        }
    }
    

    【讨论】:

    • 谢谢!有一个问题:当表单加载时,所有复选框都未选中。只有当我更改列表时,才会检查正确的列表。
    • 没问题。目前我们只是在添加/删除/编辑项目时同步项目检查状态。可以将ListChange中的逻辑封装起来,放到RefreshCheckState这样的方法中,设置DataSource后调用。
    【解决方案2】:

    另一种方法。它不需要自定义类的支持(尽量不要)。

    由于在这种情况下,项目的基础列表是非托管(在其他地方管理),因此必须手动处理项目的选中状态
    查看从BindingList 中添加或删除项目时引发的事件顺序可能很有趣(例如,在更新列表之前没有通知列表更改的事件,ListChangedEventArgs.OldIndex 永远不会设置,因此总是-1 等)。

    由于 CheckedListBox 的来源是一个简单的List&lt;string&gt;,当更新列表时,Item 的CheckState 会丢失。因此,需要存储这些状态并在需要时重新应用,调用SetItemCheckedState 方法。

    由于必须调整项目的状态以匹配新的列表组合(在删除或插入项目之后)并且此过程是同步的,因此会引发 ItemCheck 事件(用于更新所有项目 CheckState)不合时宜,需要延迟执行。这就是这里使用BeginInvoke() 的原因。

    总而言之,内部存储这些状态的专用类对象,如Reza Aghaei's answer 所示,是要走的路。在这里,绑定受到基类缺乏支持的影响。并不是说它在任何地方都另有说明:例如,CheckedListBox.DataSource 甚至不可浏览。

    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();
    }
    

    【讨论】:

    • 谢谢!当我对并发理解更有信心时,我会尝试这个解决方案。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 2018-10-03
    • 1970-01-01
    • 2019-07-16
    相关资源
    最近更新 更多