Как получить контекст синхронизации для показанной второй формы - PullRequest
3 голосов
/ 22 декабря 2010

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

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

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

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

Мой код (в MainForm_Load (...)) похож на этот, который работает в новом Project с textBox1, добавленным в основную форму, но не работает в моем текущем проекте:

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

У любого есть мысли о том, что может быть гонгом.

[EDIT]

Я проследил ошибку до создания экземпляра объекта, который использует форму, чтобы запросить у пользователя информацию для входа в систему. Ошибка происходит только тогда, когда форма была показана. (Если я верну жестко запрограммированное значение до того, как 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 Ответ

4 голосов
/ 23 декабря 2010

Просто быть в главном потоке недостаточно.Вам нужно иметь действительное значение 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 устраняет проблему.

...