How to prevent marshalling errors when accessing UI elements from non-UI threads?

-2

I've got this function that receives a digital output pin address uscita as Integer and status high as boolean, and updates the pin-out and fills color red in a LED-like Ellipse on the UI.

    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

The function is called every 500ms via a timer. Debugging shows that the function is working only once. From the second time and for all other times, the function throws an exception

The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

How can I prevent that without using the f... dispatchers and similar cumbersome stuff. Additionally, how to refresh the UIelements, given that no .Refresh method exists, again without using the f... dispatchers?

The old winforms didn't have these issues... ah, the old times.

My UI XAML looks something like this:

<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>
vb.net
uwp
windows-10-iot-core
asked on Stack Overflow Aug 26, 2015 by Alessandro Mandelli • edited Aug 26, 2015 by Pete Baughman

2 Answers

2

Data Binding!

Create an LED ViewModel object, bind one to each LED UI element. Give the LED ViewModel a "Fill" property of the appropriate type. Then, every 500 ms when your timer ticks, update the property and fire off the PropertyChanged event. When the UI element sees the PropertyChanged event, it will refresh "Fill"

It's a bit of an architecture change, but you won't need to worry about using the dispatcher.

This website http://blogs.msdn.com/b/jerrynixon/archive/2012/10/12/xaml-binding-basics-101.aspx has an OK example of binding a string to a button's text. See "Binding a property" - you're going to need to do something similar, but you'll be binding to "Fill"

This question: How can I bind a background color in WPF/XAML? is also doing something pretty similar to what you're trying to do.

Followup to address questions in comments

In your viewmodel, you would need to create a property of type "Brush" and then in the XAML, bind it to the Fill property of the ellipse like:

//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}"
. . .

Sorry - I just realized that my example is c# and you're doing VB - the XAML part will be the same - The syntax for your ViewModel will be a little different from mine.

answered on Stack Overflow Aug 26, 2015 by Pete Baughman • edited May 23, 2017 by Community
0

Ok, so what I did:

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

in the 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>

And in the code? LED1.fill = whatever?

answered on Stack Overflow Aug 26, 2015 by Alessandro Mandelli

User contributions licensed under CC BY-SA 3.0