Страницы

Поиск по вопросам

воскресенье, 8 декабря 2019 г.

Работа с Dispatcher

#c_sharp #wpf #net


Есть 2 программы. Одна WinPhone приложение, другая WPF. В обоих используется Dispatcher
для доступа к элементам UI из потока. Вот код из WinPhone

Dispatcher.BeginInvoke(() => RateTextBox.Text = rate)


Здесь все работает. Dispatcher идет как объект, есть метод BeginInvoke.

Вот код WPF

Dispatcher.CurrentDispatcher.BeginInvoke(() => textBox1.Text = content)


Здесь Dispatcher идет как класс, а не объект. Я не могу понять почему. Пытался испробовать
разные конструкции, но в итоге идет ошибка


  Ошибка 2   Невозможно преобразовать "лямбда-выражение" к типу "System.Delegate",
поскольку он не является делегатом


Не могу понять почему ошибка и почему в двух проектах "разные" Dispatcher'ы, В WinPhone
приложении подрублены свои мобильные библиотеки с коробки, ничего не добавлял от себя.
В WPF Добавил WindowsBase.dll откуда взял ссылку на System.Windows.Threading где
и лежит Dispatcher. 

Кто может разъяснить ситуацию? А то я уже больше недели бьюсь над этими вопросами.
Почему на WinPhone все прекрасно работает как надо, а на WPF нет.

Добавлено после:

Теперь все компилица, но не выводит результат запроса. Я испробовал код на консольном
приложении, где не требуется Dispatcher. Такой код

(content => Console.WriteLine(content))


Здесь все работает. Но я добавил Dispatcher.

(content => Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() => Console.WriteLine(content))))


И консоль пуста.
    


Ответы

Ответ 1



Вам нужно явно указать тип делегата - любой, но конкретный тип. BeginInvoke принимает Delegate - делегат любого типа. Для компиляции лямбды как делегата компилятор должен знать тип. Он его не может вывести ни из сигнатуры метода - потому что там любой типа разрешен - ни из лямбды - потому что нет однозначного механизма вывода конкретного типа делегата только по параметрам и возвращаемому значению. Вот он и падает с ошибкой. Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() => textBox1.Text = content)); P.S. Не используйте Invoke - намучаетесь. Используйте async/await.

Ответ 2



WPF писался в до-дженериковую эру, и в нём осталось много наследия: нетипизирвоанные коллекции, невнятно типизированные делегаты и т. п. WinRT создавался уже в современную эпоху, поэтому в нём есть адекватные перегрузки методов. Лечится это просто — добавлением extension-методов. Например: public static class Exts { public static void BeginInvoke (this Dispatcher @this, Action action) { @this.BeginInvoke(action); } public static void BeginInvoke (this Dispatcher @this, DispatcherPriority priority, Action action) { @this.BeginInvoke(action, priority); } } При наличии такого класса будут работать привычные вам перегрузки. Впрочем, по возможности стоит использовать адаптированный для async/await метод — InvokeAsync: await Dispatcher.InvokeAsync(() => RateTextBox.Text = rate);

Ответ 3



Компилятор не может вывести из вашей лямбды конкретный делегат, а потому сигнализирует об ошибке. Скастуйте его например так: Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() => RateTextBox.Text = rate));

Ответ 4



К вопросу о консоли. Dispatcher - это не какая-то магия, а класс, дающий доступ к стандартному виндовому циклу обработки сообщений. В консольном прилдожении никто не крутит этот цикл в основном потоке - вот и не работает Dispatcher.

Комментариев нет:

Отправить комментарий