Как получить контекст синхронизации для показанной второй формы

[РЕДАКТИРОВАТЬ] Перефразированный и упрощенный весь пост [/EDIT]

В этом блоге в качестве примера использования объекта SynchronizationContext для выполнения Задачи в потоке пользовательского интерфейса приводится следующее (я немного упростил его):

Task.Factory.StartNew(() =>"Hello World").ContinueWith(
            task => textBox1.Text = task.Result,
            TaskScheduler.FromCurrentSynchronizationContext());

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

Мой код (в MainForm_Load(...)) выглядит следующим образом: он работает в новом проекте с textBox1, добавленным в основную форму, но не работает в моем текущем проекте:

var one = Task.Factory.StartNew(
        () => "Hello, my name is Inigo Montoya");
var two = one.ContinueWith(
        task => textBox1.Text = one.Result,
        TaskScheduler.FromCurrentSynchronizationContext());

У кого-нибудь есть мысли о том, что может быть гонгом.

[РЕДАКТИРОВАТЬ]

Я проследил ошибку до создания экземпляра объекта, который использует форму, чтобы запросить у пользователя информацию для входа в систему. Ошибка происходит только тогда, когда форма была показана. (Если я верну жестко закодированное значение до этой формы Show бывает все работает отлично).

Новый вопрос: как я могу получить SynchronizationContext для формы, которую я создаю, если его собственный конструктор отображает другую форму до того, как она была показана? Вот как вы можете воспроизвести происходящее:

1) Создайте две формы: Form1 с TextBox и Form2 с Button

2) Создать класс OwnedBy1Uses2

Form1:

public partial class Form1 : Form
{
    OwnedBy1Uses2 member;
    public Form1()
    {
        InitializeComponent();
        member = new OwnedBy1Uses2();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        var ui = TaskScheduler.FromCurrentSynchronizationContext();
        Task<string> getData = Task.Factory.StartNew(
            () => "My name is Inigo Montoya...");
        Task displayData = getData.ContinueWith(
            t => textBox1.Text = t.Result, ui);
    }
}

Form2:

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
        DialogResult = System.Windows.Forms.DialogResult.Cancel;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        DialogResult = System.Windows.Forms.DialogResult.OK;
        Hide();
    }
}

OwnedBy1Uses2:

class OwnedBy1Uses2
{
    int x;
    public OwnedBy1Uses2()
    {
        using (Form2 form = new Form2())
        {
            if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                x = 1;
            }
            else
            {
                x = 2;
            }
        }
    }
}

1 ответ

Решение

Просто быть в главном потоке недостаточно. Вы должны иметь действительный SynchronizationContext.Current (установите точку останова на FromCurrentSynchronizationContext линии и изучить значение SynchronizationContext.Current; если это nullтогда что-то не так).

Самое чистое решение - выполнить код задачи, включая FromCurrentSynchronizationContext из цикла сообщений пользовательского интерфейса - то есть из чего-то вроде Form.Load для WinForms или Window.Loaded для WPF.

Редактировать:

В WinForms была ошибка, из-за которой Form.Load тоже было недостаточно - вам действительно пришлось заставить Win32 создавать дескрипторы, читая Handle имущество. У меня сложилось впечатление, что эта ошибка была исправлена, но я могу ошибаться.

Редактировать 2 (скопировано из комментария):

Я подозреваю, что ваша проблема в том, что вы звоните ShowDialog вне Application.Run, ShowDialog является вложенным циклом сообщений, но в этом случае родительский цикл сообщений отсутствует. Если вы установите часы на SynchronizationContext.Current и пройти через ShowDialogвы увидите, что это WindowsFormsSynchronizationContext до отображения диалогового окна, но изменяется на не-WinForms SynchronizationContext после того, как диалог показан. Перемещение создания члена (включая ShowDialog) к Load событие решает проблему.

Другие вопросы по тегам