Окно Winforms не обновляется при изменении связанных данных - PullRequest
5 голосов
/ 20 апреля 2009

На рисунке ниже показано, как работает мой код. Когда я нажимаю кнопку 2, список обновляется, но не когда я нажимаю кнопку 1. Почему?

pseudo code

Связана ли проблема с многопоточностью? Если это так, куда мне добавить вызов (Begin) Invoke?

Следует отметить одну интересную вещь: если я сначала нажму кнопку 1, а затем кнопку 2, то данные, сгенерированные нажатием кнопки 1, будут показаны, когда я нажму кнопку 2. Таким образом, кажется, что данные, сгенерированные doFoo, буферизуются где-то, а затем помещаются в список после того, как я нажимаю кнопку 2.

EDIT:

Я попытался добавить AddNumber к коду формы и добавил вызов Invoke, когда listBox1.InvokeRequired возвращает true. Это решает проблему, но не самый лучший дизайн. Я не хочу, чтобы графический интерфейс пользователя беспокоился о том, как добавлять элементы в список, являющийся частью модели.

Как сохранить логику добавления в список, внутренний по отношению к списку, при этом обновляя графический интерфейс при изменении списка?

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

Теперь, когда мы подтвердили, что это проблема с многопоточностью, я обновил изображение, чтобы более точно отразить дизайн реального кода, над которым я работаю.

Хотя предложение Лусеро все еще решает проблему, я надеялся на то, что форме не нужно ничего знать о dll или CDllWrapper.

Модель (ListBoxDataBindingSource и т. Д.) Вообще ничего не должна знать о представлении (списки, кнопки, метки и т. Д.)

Ответы [ 4 ]

2 голосов
/ 20 апреля 2009

Я предполагаю, что это связано с тем, что сообщение об обновлении обрабатывается не в том потоке. Фон: каждый поток имеет свою очередь сообщений. Сообщения, помещенные в очередь сообщений, будут по умолчанию помещаться в ту же ветку, что и вызывающая сторона. Следовательно, обратный вызов может публиковать сообщение в неправильной ветке.

Попробуйте: переместите метод AddNumber () в форму и используйте Invoke () (унаследованный Control), чтобы добавить элемент в правильный поток. Это может избавить от проблемы.

Редактировать , чтобы отразить ваши последующие действия: Пользовательский интерфейс не должен знать о вашем компоненте. То, что вам нужно, это просто правильная синхронизация между добавлением элемента в ваш список и пользовательским интерфейсом, так как обновления пользовательского интерфейса будут работать, если поток совпадает. Следовательно, вы можете захотеть предоставить Control вашему классу, который оборачивает BindingList, а затем выполнить Invoke для самого списка. Это заставляет список беспокоиться о срабатывании upate в потоке пользовательского интерфейса и принимает на себя заботу как пользовательского интерфейса, так и внешнего компонента о вызове обработчика в правильном потоке.

Как это:

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;
        }
    }
}
1 голос
/ 30 января 2011

Я знаю, что это старый, хотя у меня была очень похожая проблема.

Вот решение: BindingList не обновляет связанный ListBox .

0 голосов
/ 25 июня 2014

Мне нужно вызвать мою модель представления, чтобы добавить вещи в список связывания, поэтому мне нужно написать анонимную функцию

Ссылка на ответ Лусеро и следующий пост: Анонимный метод в вызове Invoke

Мой код:

listBox.Invoke((Action)delegate
{
    MyViewModel.AddItem(param1, param2);
});
0 голосов
/ 20 апреля 2009

Вместо того, чтобы setFP установил обратный вызов в lbDataBindingSource.AddNumber, создайте частный метод в своем коде для обработки обратного вызова, а затем вызовите lbDataBindingSource.AddNumber из этого обратного вызова.

void MyForm_Load(object sender, EventArgs e)
{
    //...

    cdll.setFP(FPCallback);
}

private void FPCallback(int num)
{
    lbDataBindingSoruce.AddNumber(num);
}
...