C # windows формирует пользовательские элементы управления кросс-потоковой работы - PullRequest
0 голосов
/ 14 декабря 2011

У меня есть одна форма главного окна, и в этой форме у меня есть пользовательские элементы управления, которые представляют разные экраны в приложении. Я хочу получить доступ к дочерним элементам управления этого элемента управления. Есть кое-что, чего я не получаю ... иногда я получаю эту ошибку:

Cross-thread operation not valid: 
Control 'lblText' accessed from a thread 
other than the thread it was created on.

но иногда все работает нормально. Я не совсем понимаю, почему ошибка ... вероятно, что-то с внешним устройством (MEI BillAcceptor), который имеет событие (внутри класса Form1), который вносит изменения в элемент управления ... поэтому позвольте мне написать простой код ...

//user control
public partial class Screen2 : UserControl
{
    public void changeValue(string txt)
    {
        lblText.Text = txt;
    }
}

и метод changeValue вызывается из формы1 при возникновении определенного события ...

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        BillAcceptor.SomeBillAcceptorEvent += 
            new SomeBillAcceptorEventHandler(changeText);
    }

    private void changeText(object sender, EventArgs args)
    {
        _screen2.changeValue("some text");
    }
}

Так что самое неприятное в том, что иногда все действительно работает ... Итак, мой вопрос: "Должен ли я использовать Invoke здесь?" или как мне решить эту проблему с меньшими изменениями в приложении ...

Ответы [ 4 ]

3 голосов
/ 14 декабря 2011

В вашем обработчике. сделай что-нибудь подобное.

        if (this.InvokeRequired)
        {
            Invoke(new MethodInvoker(() => 
            {
                _screen2.changeValue("some text");
            }));
        }
        else
        {
            _screen2.changeValue("some text");
        }

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

1 голос
/ 14 декабря 2011

Да, вам нужно использовать Invoke, если есть возможность вызова этого метода из другого потока.

Вы можете проверить this.InvokeRequired(), если true, затем использовать invoke, если false, сделать обычный вызов.

0 голосов
/ 14 декабря 2011

Короткий ответ - да, вы должны использовать Invoke.См. этот вопрос и его принятый ответ, если вам нужны подробности .

Причина, по которой исключение возникает только в некоторых случаях, кстати, сводится к срокам.В настоящее время у вас есть состояние гонки, при котором иногда вам везет, а иногда нет.

Кстати, вот довольно удобный пример для такого рода вещей.

  1. Refactorлюбой код, который устанавливает значения формы в свой собственный метод (ы) void.
  2. В этом новом методе вызовите InvokeRequired.Если он возвращает true, вызовите Invoke, передав текущий метод, чтобы вернуться в него.Если он возвращает false, продолжайте и внесите изменение.
  3. Вызовите этот новый метод из обработчика событий.

Например:

private void ChangeScreen2() {
    if (this.InvokeRequired) {
        this.Invoke(new MethodInvoker(ChangeScreen2));
    }
    else {
        _screen2.changeValue("some text");
    }
}

private void changeText(object sender, EventArgs args)
{
    ChangeScreen2();
}

Идеято, что вы изолируете весь код, который модифицирует форму, в эти методы, которые всегда начинаются с проверки InvokeRequired и всегда Invoke сами, если это требуется.Этот шаблон работает с .NET 1.0 и выше.Для более аккуратного подхода см. Принятый ответ на этот вопрос , который работает с .NET 3.0 и более поздними версиями.

0 голосов
/ 14 декабря 2011

Это происходит из-за thread unsafe call

Вы должны совершать в программе только потоковые вызовы

Проверьте эту ссылку.

...