Как вызвать поток пользовательского интерфейса компонента WinForm?

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

Task myTask = Task.Factory.StartNew (() => SomeMethod(someArgs));
myTask.ContinueWith (antecedant => uiTextBox.Text = antecedant.Exception.Message,
                     TaskContinuationOptions.OnlyOnFaulted);

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

Однако в классе Component не определены Invoke или BeginInvoke.

Как действовать дальше?


ОБНОВЛЕНИЕ

Также обратите внимание, что Invoke/BeginInvoke/InvokeRequired недоступны из моего класса, производного от Component, поскольку Component их не предоставляет.


person Stécy    schedule 25.10.2010    source источник


Ответы (2)


Вы можете просто добавить свойство к своему компоненту, позволяющее клиенту установить ссылку на форму, которую вы можете использовать для вызова его метода BeginInvoke().

Это также можно сделать автоматически, желательно, чтобы никто не забыл. Это требует немного магии времени разработки, которая довольно непроницаема. Я не сам придумал это, я получил это от компонента ErrorProvider. Надежный источник и все такое. Вставьте это в исходный код вашего компонента:

using System.Windows.Forms;
using System.ComponentModel.Design;
...
    [Browsable(false)]
    public Form ParentForm { get; set; }

    public override ISite Site {
        set {
            // Runs at design time, ensures designer initializes ParentForm
            base.Site = value;
            if (value != null) {
                IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
                if (service != null) this.ParentForm = service.RootComponent as Form;
            }
        }
    }

Конструктор автоматически устанавливает свойство ParentForm, когда пользователь перетаскивает ваш компонент на форму. Используйте ParentForm.BeginInvoke().

person Hans Passant    schedule 25.10.2010
comment
Довольно умное решение. - person Kamran; 30.04.2020
comment
Я наблюдаю, что этот код вызывается во время выполнения, а также делает то, что ожидается, когда вы перетаскиваете его во время разработки. Во время выполнения: service.RootComponent as Form; возвращает ноль. - person BillW; 05.10.2020

Для этого можно использовать делегатов.

    delegate void UpdateStatusDelegate (string value);


    void UpdateStatus(string value)
    {
        if (InvokeRequired)
        {
            // We're not in the UI thread, so we need to call BeginInvoke
            BeginInvoke(new UpdateStatusDelegate(UpdateStatus), new object[]{value});
            return;
        }
        // Must be on the UI thread if we've got this far
        statusIndicator.Text = value;
    }
person jimplode    schedule 25.10.2010
comment
Итак, создание делегата и его вызов будут использовать поток пользовательского интерфейса? - person Stécy; 25.10.2010
comment
должен сделать, не тестировал этот код, но я так делаю в стандартных winforms - person jimplode; 25.10.2010
comment
К сожалению, это не сработает, так как метод InvokeRequired недоступен внутри компонента. - person Stécy; 25.10.2010
comment
Обычно вам не нужно создавать делегата; вместо этого используйте Action<T>. - person Fredrik Mörk; 25.10.2010
comment
Не могли бы вы создать событие для компонента, а затем подписаться на него дальше? - person jimplode; 25.10.2010
comment
BeginInvoke(new MethodInvoker(() => UpdateStatus(value))) должно работать, наверное - person Sam; 26.10.2010
comment
@Sam BeginInvoke недоступен в классе компонентов и производных. - person Stécy; 26.10.2010
comment
@ Сэм Ты прав. Я успешно использовал это решение до того, как использовал решение Ганса, которое, на мой взгляд, является оптимальным, поскольку может случиться так, что позже я использую не текстовое поле, а какой-то другой компонент. - person Stécy; 26.10.2010