2015-01-12 3 views
1

Я хочу обновить пользовательский интерфейс из другого потока
У меня есть много вариантов для выполнения кода в контексте UI темы:
правильный способ выполнения кода в контексте контекста пользовательского интерфейса?

1: с помощью BeginInvoke/EndInvoke методы:
позер-код

public delegate int AddItem(object Item); 
public Form1 F = (Form1)Application.OpenForms["Form1"]; 
private static async DoSomething() 
{ 
    AddItem ad = new AddItem(F.ls1.Items.Add); 
    await Task.Run(() =>F.EndInvoke(F.BeginInvoke(add,"NewItem"))); 
} 


2: использованием Прогресс/IProgress: Я не знаю, как реализовать такую ​​вещь

есть ли другие способы сделать это? и что является предпочтительным способом?

примечание: поток, который вызывает задачу не то же UI Thread поэтому, возможно, прогресс не работает здесь

+1

Я не думаю, что это дублирующий вопрос, на который вы ссылаетесь, для WPF –

+0

Правда, но не меняет подход. Просто используйте «normal' Invoke' »вместо' Dispatcher.Invoke'. Если у вас возникли проблемы с использованием/внедрением, я с удовольствием помогу в специфике ... – ChrFin

+2

Вы не должны быть потоковыми, вы не можете многопоточно использовать интерфейс. Вы используете Invoke совершенно неправильно. Правильное использование 'BeginXXX' и' EndXXX' - 'Task.FromAsync (F.BeginInvoke (add,« NewItem »), F.EndInvoke)'. – Aron

ответ

5

Вы делаете это слишком сложно, IMO:

F.Invoke((MethodInvoker)delegate { F.ls1.Items.Add("NewItem"); }); 

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

Примечание: лично я бы предпочел, если бы F инкапсулировал больше этого непосредственно, обратите внимание; например:

F.Invoke((MethodInvoker)delegate { F.AddCategory("NewItem"); }); 
+0

Прошу прощения, это моя вина. Я не очень хорошо выразил свой вопрос. Думаю, мне нужно удалить этот вопрос. –

+0

@MadMass почему бы просто не отредактировать? –

+0

@MadMass Вы должны прочитать блог Стивена Клири по этому вопросу. http://blog.stephencleary.com/2013/10/taskrun-etiquette-and-proper-usage.html Вкратце. Правильный способ потоковой передачи с помощью async не является ниткой. – Aron

2

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

Лучший подход либо использовать IProgress<T>/Progress<T> (если ваши обновления пользовательского интерфейса обновления прогресса) или IObservable<T> (если бэкенд асинхронно производит ряд значений).

Добавление элементов в список звучит для меня больше как асинхронных серий, но так как вы специально просили о IProgress<T> я покажу пример, что:

private static async Task DoSomethingAsync() 
{ 
    Progress<object> progress = new Progress<object>(item => F.ls1.Items.Add(item)); 
    await Task.Run(() => BackgroundWork(progress)); 
} 

private static void BackgroundWork(IProgress<object> progress) 
{ 
    // Do background work here. 
    var item = "NewItem"; 

    // Notify UI of progress. 
    if (progress != null) 
    progress.Report(item); 
} 

Если вы хотите использовать IObservable<T>, то ваш код может выглядеть примерно так:.

private static void StartSomething() 
{ 
    IObservable<object> items = BackgroundWork(); 
    items.ObserveOn(F).Subscribe(item => F.ls1.Items.Add(item), ex => HandleException(ex)); 
} 

Обратите внимание, что с обеими из этих подходов, код делает фоновую работу не заботится, какой контекст он выполняет в это только производит серию обновлений прогресса или серию O f. Затем слой пользовательского интерфейса потребляет эти отчеты/значения прогресса и соответственно обновляет пользовательский интерфейс.

Смежные вопросы