Как предотвратить ошибки маршаллинга при доступе к элементам пользовательского интерфейса из потоков, не связанных с пользовательским интерфейсом?

У меня есть эта функция, которая получает адрес вывода цифрового выхода uscita как Integer и высокий статус как boolean, а также обновляет распиновку и заполняет красный цвет эллипса в виде светодиода в пользовательском интерфейсе.

    Private Function comandaUscita(ByVal uscita As Integer, ByVal high As Boolean) As Boolean

        If high Then
            Select Case uscita
                Case 1
                    If gpin1 IsNot Nothing Then gpin1.Write(GpioPinValue.High)
                    LED1.Fill = redBrush
                Case 2
                    If gpin1 IsNot Nothing Then gpin2.Write(GpioPinValue.High)
                    LED2.Fill = redBrush
...
                End Select
                Return True
            End If
        End Function

Функция вызывается каждые 500 мс через таймер. Отладка показывает, что функция работает только один раз. Во второй раз и во всех остальных случаях функция выдает исключение.

Приложение вызвало интерфейс, который был упорядочен для другого потока. (Исключение из HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

Как я могу предотвратить это без использования f ... диспетчеров и подобных громоздких вещей. Кроме того, как обновить UIelements, учитывая, что метод .Refresh не существует, опять же без использования диспетчеров f ...?

В старых winforms этих проблем не было ... ах, старые времена.

Мой пользовательский интерфейс XAML выглядит примерно так:

<StackPanel HorizontalAlignment="Center" 
            VerticalAlignment="Center" 
            Height="60"
            Margin="10,10,10,569" 
            Width="340"> 
    <TextBlock x:Name="DelayText1" 
               Text="Uscita digitale 1"
               Margin="10" 
               TextAlignment="Center"
               FontSize="26.667"
               HorizontalAlignment="Right" /> 
    <Ellipse x:Name="LED1" 
             Fill="LightGray"
             Stroke="White" 
             Width="25"
             Height="25"
             Margin="10,-40,10,0"
             HorizontalAlignment="Left"
             VerticalAlignment="Top"
             RenderTransformOrigin="3.524,-1.209"/>
</StackPanel>

person Alessandro Mandelli    schedule 26.08.2015    source источник
comment
В старых формах winform были эти проблемы, и вы решаете проблему точно так же - запуская сеттер в потоке пользовательского интерфейса. Вам просто нужно использовать диспетчер WPF вместо метода BeginInvoke в основной форме. Вам либо придется это сделать, либо использовать привязку данных WPF. Обновление ViewModel и запуск события Property Changed приведет к тому, что элемент управления обновит свои привязки.   -  person Pete Baughman    schedule 27.08.2015
comment
Я не хочу пользоваться диспетчерами. А begininvoke не является членом или методом диспетчера в UWP.   -  person Alessandro Mandelli    schedule 27.08.2015
comment
Да, мне было непонятно: Dispatcher работает с WPF так же, как BeginInvoke идет с Winforms   -  person Pete Baughman    schedule 27.08.2015
comment
Возможным вариантом было использование потоков в winforms. В UWP это не вариант.   -  person Alessandro Mandelli    schedule 27.08.2015


Ответы (2)


Привязка данных!

Создайте объект LED ViewModel, привяжите его к каждому элементу LED UI. Присвойте LED ViewModel свойство «Fill» соответствующего типа. Затем каждые 500 мс при срабатывании таймера обновляйте свойство и запускайте событие PropertyChanged. Когда элемент пользовательского интерфейса видит событие PropertyChanged, он обновляет «Заливку».

Это небольшое изменение архитектуры, но вам не нужно беспокоиться об использовании диспетчера.

Этот веб-сайт http://blogs.msdn.com/b/jerrynixon/archive/2012/10/12/xaml-binding-basics-101.aspx имеет хороший пример привязки строки к тексту кнопки. См. «Привязка свойства» - вам нужно будет сделать что-то подобное, но вы будете привязаны к «Заливке».

Этот вопрос: Как привязать цвет фона в WPF / XAML? также делает что-то очень похожее на то, что вы пытаетесь сделать.

Ответы на вопросы в комментариях

В вашей модели просмотра вам нужно будет создать свойство типа «Brush», а затем в XAML привязать его к свойству Fill эллипса, например:

//The viewmodel:
public sealed class LedViewModel: INotifyPropertyChanged {
    // Update this property and fire the PropertyChanged
    // event to propagate the change to the UI.
    public Brush LedFill { get; set; } 
. . .

//In the XAML:
<Ellipse x:Name="LED Whatever" 
         Fill="{Binding Path=LedFill}"
. . .

Извините - я только что понял, что мой пример - C #, а вы используете VB - часть XAML будет такой же - синтаксис вашей ViewModel будет немного отличаться от моего.

person Pete Baughman    schedule 26.08.2015
comment
Спасибо, хотя к привязкам я довольно наивен. Итак, у меня есть эта панель стека <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Height="60" Margin="10,10,10,569" Width="340"> <TextBlock x:Name="DelayText1" Text="Uscita digitale 1" Margin="10" TextAlignment="Center" FontSize="26.667" HorizontalAlignment="Right" /> <Ellipse x:Name="LED1" Fill="LightGray" Stroke="White" Width="25" Height="25" Margin="10,-40,10,0" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="3.524,-1.209"/> `‹/StackPanel›` - person Alessandro Mandelli; 27.08.2015
comment
Что мне к чему привязать? Нравится? <Ellipse Content="{Binding Path=LEDfill}" /> и создать экземпляр нового класса public class myLED? А что мне положить внутрь? Получить / установить UIElement - person Alessandro Mandelli; 27.08.2015
comment
Спасибо, чувак, черт ... Мне нужно изменить цвет кружка на форме. - person Alessandro Mandelli; 27.08.2015

Итак, что я сделал:

Public NotInheritable Class LedViewModel
    Implements INotifyPropertyChanged

    Private _LEDFill As Brush
    Public Property LEDfill As Brush
        Get
        Return _LEDFill
    End Get
    Set(value As Brush)
        _LEDFill = value
    End Set
End Property

Public Event PropertyChanged As PropertyChangedEventHandler 
  Implements  INotifyPropertyChanged.PropertyChanged
End Class

в XAML

    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Height="60" Margin="10,10,10,569" Width="340">
        <TextBlock x:Name="DelayText1" Text="Uscita digitale 1" Margin="10" TextAlignment="Center" FontSize="26.667" HorizontalAlignment="Right" />
        <Ellipse x:Name="LED1" Fill="{Binding Path=LEDfill}" Stroke="White" Width="25" Height="25" Margin="10,-40,10,0" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="3.524,-1.209"/>
    </StackPanel>

А в коде? LED1.fill = что угодно?

person Alessandro Mandelli    schedule 26.08.2015
comment
Просто чтобы вы знали, что привязка работает не так, как предполагалось и ожидалось. Мне пришлось использовать ресурс для события dispatcher.timer. В любом случае спасибо за ваше время и усилия. Это было очень полезно и мотивировало. - person Alessandro Mandelli; 27.08.2015