【问题标题】:Winforms listbox not updating when bound data changes绑定数据更改时Winforms列表框不更新
【发布时间】:2010-10-20 14:00:48
【问题描述】:

下图显示了我的代码是如何工作的。当我按下按钮 2 时,列表框会更新,但当我按下按钮 1 时不会。为什么?

问题与线程有关吗?如果是,我应该在哪里添加对 (Begin)Invoke 的调用?

需要注意的一个有趣的事情是,如果我先按 button1,然后按 button2,那么当我点击 button2 时,会显示 button1 点击生成的数据。所以看起来 doFoo 生成的数据在某处缓冲,然后在我按下 button2 后推送到列表框。

编辑:

我尝试将 AddNumber 添加到表单代码中,并在 listBox1.InvokeRequired 返回 true 时添加对 Invoke 的调用。这解决了问题,但不是最好的设计。我不希望 GUI 不得不“担心”如何将项目添加到作为模型一部分的列表中。

如何保持添加到列表类内部列表背后的逻辑,同时在列表更改时仍更新 gui?

编辑 2:

既然我们已经确认这是一个线程问题,我已经更新了图像以更准确地反映我正在处理的实际代码的设计。

虽然 Lucero 的建议仍然可以解决问题,但我希望得到一些不需要表单来了解有关 dll 或 CDllWrapper 的任何内容。

模型(ListBoxDataBindingSource 等)应该对视图(列表框、按钮、标签等)一无所知

【问题讨论】:

    标签: c# winforms multithreading data-binding listbox


    【解决方案1】:

    我知道这是旧的,虽然我有一个非常相似的问题。

    这是解决方案:BindingList not updating bound ListBox

    【讨论】:

    • 谢谢。我很久以前就把这个项目搁置了,但是当我再次拿起它时,我会看看这是否有帮助:)
    【解决方案2】:

    我需要调用我的视图模型向绑定列表添加东西,所以我需要编写一个匿名函数

    参考 Lucero 的回答和以下帖子: Anonymous method in Invoke call

    我的代码:

    listBox.Invoke((Action)delegate
    {
        MyViewModel.AddItem(param1, param2);
    });
    

    【讨论】:

      【解决方案3】:

      我的猜测是这是因为更新消息在错误的线程上处理。背景:每个线程都有自己的消息队列。默认情况下,发布到消息队列中的消息将与调用者位于同一线程中。因此,回调可能会在错误的线程上发布消息。

      试试这个:将 AddNumber() 方法移动到表单并使用 Invoke()(由 Control 继承)将项目添加到正确的线程中。这可能会解决问题。

      编辑以反映您的后续行动: UI 不必知道您的组件。您需要的只是将项目添加到列表和 UI 之间的适当同步,因为 UI 更新仅在线程匹配时才起作用。因此,您可能希望将 Control 提供给包装 BindingList 的类,然后对列表本身执行 Invoke。这使得列表担心触发 UI 线程上的更新,并且确实消除了 UI 和外部组件在正确线程上调用处理程序的担心。

      像这样:

      internal class ListBoxDataBindingSource {
        private readonly Control uiInvokeControl;
        private readonly BindingList<Item> list = new BindingList<Item>();
      
          public ListBoxDataBindingSource(Control uiInvokeControl) {
            if (uiInvokeControl == null) {
                  throw new ArgumentNullException("uiInvokeControl");
            }
              this.uiInvokeControl = uiInvokeControl;
              CDIIWrapper.setFP(AddNumber);
          }
      
          public void AddNumber(int num) {
            Item item = new Item(num.ToString());
              if (uiInvokeControl.InvokeRequired) {
                  uiInvokeControl.Invoke(list.Add, item);
              } else {
                  list.Add(item);
              }
          }
      
          private BindingList<Item> List {
              get {
                  return list;
              }
          }
      }
      

      【讨论】:

      • 我明白问题出在哪里,现在我们只需要想出一个好的解决方案 :) 我正在尝试根据 MVC 模式进行设计,所以让 ListBoxDataBindingSource(模型的一部分)了解控件(视图的一部分)通常被视为不好的做法。
      • 好吧,它并不真正了解控件,而只需要知道 hot 即可将 add 调用编组到正确的线程。您可以创建自己的编组类,该类知道控件并将其传递给列表,以便正确隐藏此方面。
      • @Tobbe: 也许this answer 我之前的问题之一——Winforms data-binding to business objects in a multi-threaded scenario without InvokeRequired?——在这里适用。)
      【解决方案4】:

      不要让 setFP 将回调设置为 lbDataBindingSource.AddNumber,而是在代码中创建一个私有方法来处理回调,然后从该回调中调用 lbDataBindingSource.AddNumber。

      void MyForm_Load(object sender, EventArgs e)
      {
          //...
      
          cdll.setFP(FPCallback);
      }
      
      private void FPCallback(int num)
      {
          lbDataBindingSoruce.AddNumber(num);
      }
      

      【讨论】:

        猜你喜欢
        • 2011-07-14
        • 1970-01-01
        • 2014-08-06
        • 1970-01-01
        • 1970-01-01
        • 2012-10-12
        • 1970-01-01
        • 2016-09-29
        • 1970-01-01
        相关资源
        最近更新 更多