从非 UI 线程访问 UI 元素时如何防止编组错误?
How to prevent marshalling errors when accessing UI elements from non-UI threads?
我有这个函数接收数字输出引脚地址 uscita 作为整数和状态高作为布尔值,并更新引脚输出并在 UI 上的类似 LED 的椭圆中填充红色.
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 毫秒调用一次。
调试显示该函数只工作一次。
从第二次开始和所有其他时间,函数抛出异常
The application called an interface that was marshalled for a
different thread. (Exception from HRESULT: 0x8001010E
(RPC_E_WRONG_THREAD))
如果不使用 f... 调度程序和类似繁琐的东西,我怎么能防止这种情况发生。
此外,如果不存在 .Refresh 方法,又如何在不使用 f... 调度程序的情况下刷新 UI 元素?
旧的 winforms 没有这些问题...啊,旧时代。
我的 UI 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>
数据绑定!
创建一个 LED ViewModel 对象,将一个对象绑定到每个 LED UI 元素。为 LED ViewModel 提供适当类型的 "Fill" 属性。然后,每 500 毫秒当您的计时器计时时,更新 属性 并触发 PropertyChanged 事件。当UI元素看到PropertyChanged事件,就会刷新"Fill"
这是一个架构变化,但您无需担心使用调度程序。
此网站 http://blogs.msdn.com/b/jerrynixon/archive/2012/10/12/xaml-binding-basics-101.aspx 有一个将字符串绑定到按钮文本的 OK 示例。请参阅 "Binding a property" - 您将需要做类似的事情,但您将绑定到 "Fill"
这个问题:How can I bind a background color in WPF/XAML? 也在做一些与您正在尝试做的事情非常相似的事情。
跟进解决评论中的问题
在您的视图模型中,您需要创建类型为 "Brush" 的 属性,然后在 XAML 中,将其绑定到椭圆的填充 属性喜欢:
//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 的语法将与我的略有不同.
好的,所以我做了什么:
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 = 什么?
我有这个函数接收数字输出引脚地址 uscita 作为整数和状态高作为布尔值,并更新引脚输出并在 UI 上的类似 LED 的椭圆中填充红色.
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 毫秒调用一次。 调试显示该函数只工作一次。 从第二次开始和所有其他时间,函数抛出异常
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
如果不使用 f... 调度程序和类似繁琐的东西,我怎么能防止这种情况发生。 此外,如果不存在 .Refresh 方法,又如何在不使用 f... 调度程序的情况下刷新 UI 元素?
旧的 winforms 没有这些问题...啊,旧时代。
我的 UI 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>
数据绑定!
创建一个 LED ViewModel 对象,将一个对象绑定到每个 LED UI 元素。为 LED ViewModel 提供适当类型的 "Fill" 属性。然后,每 500 毫秒当您的计时器计时时,更新 属性 并触发 PropertyChanged 事件。当UI元素看到PropertyChanged事件,就会刷新"Fill"
这是一个架构变化,但您无需担心使用调度程序。
此网站 http://blogs.msdn.com/b/jerrynixon/archive/2012/10/12/xaml-binding-basics-101.aspx 有一个将字符串绑定到按钮文本的 OK 示例。请参阅 "Binding a property" - 您将需要做类似的事情,但您将绑定到 "Fill"
这个问题:How can I bind a background color in WPF/XAML? 也在做一些与您正在尝试做的事情非常相似的事情。
跟进解决评论中的问题
在您的视图模型中,您需要创建类型为 "Brush" 的 属性,然后在 XAML 中,将其绑定到椭圆的填充 属性喜欢:
//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 的语法将与我的略有不同.
好的,所以我做了什么:
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 = 什么?