【问题标题】:How to prevent marshalling errors when accessing UI elements from non-UI threads?从非 UI 线程访问 UI 元素时如何防止编组错误?
【发布时间】:2015-11-21 02:14:53
【问题描述】:

我有这个函数,它接收一个数字输出引脚地址 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 毫秒通过一个计时器调用一次。 调试显示该功能只工作一次。 从第二次和所有其他时间开始,该函数都会引发异常

应用程序调用了一个接口,该接口被编组为 不同的线程。 (来自 HRESULT 的异常:0x8001010E (RPC_E_WRONG_THREAD))

如果不使用 f... 调度程序和类似的繁琐东西,我怎样才能防止这种情况发生。 此外,如果不存在 .Refresh 方法,如何在不使用 f... 调度程序的情况下再次刷新 UIelements?

旧的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>

【问题讨论】:

  • 旧的winforms确实有这些问题,你用完全相同的方式解决问题——通过在UI线程上运行setter。您只需要在主窗体上使用 WPF 调度程序而不是 BeginInvoke 方法。您要么必须这样做,要么使用 WPF 数据绑定。更新 ViewModel 并触发“Property Changed”事件将导致控件刷新其绑定。
  • 我不想使用调度程序。并且 begininvoke 不是 UWP 中调度程序的成员或方法。
  • 是的,我并不清楚:Dispatcher 与 WPF 一起使用就像 BeginInvoke 与 Winforms 一起使用
  • 在 winforms 中使用线程是一种选择。在 UWP 中不是一个选项。

标签: vb.net uwp windows-10-iot-core


【解决方案1】:

数据绑定!

创建一个 LED ViewModel 对象,将一个绑定到每个 LED UI 元素。为 LED ViewModel 提供适当类型的“填充”属性。然后,每 500 毫秒,当您的计时器计时,更新属性并触发 PropertyChanged 事件。当UI元素看到PropertyChanged事件时,会刷新“Fill”

这是架构上的一点改变,但您不必担心使用调度程序。

该网站http://blogs.msdn.com/b/jerrynixon/archive/2012/10/12/xaml-binding-basics-101.aspx 有一个将字符串绑定到按钮文本的好示例。请参阅“绑定属性” - 您将需要执行类似的操作,但您将绑定到“填充”

这个问题:How can I bind a background color in WPF/XAML? 也在做一些与你正在尝试做的事情非常相似的事情。

跟进解决 cmets 中的问题

在您的视图模型中,您需要创建一个“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 的语法将与我的略有不同。

【讨论】:

  • 谢谢,虽然我对绑定很天真。所以我有这个堆栈面板&lt;StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Height="60" Margin="10,10,10,569" Width="340"&gt;&lt;TextBlock x:Name="DelayText1" Text="Uscita digitale 1" Margin="10" TextAlignment="Center" FontSize="26.667" HorizontalAlignment="Right" /&gt;&lt;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"/&gt;` `
  • 我必须绑定什么?像这样? &lt;Ellipse Content="{Binding Path=LEDfill}" /&gt; 并实例化一个新类 public class myLED?我在里面放什么?一个 UIElement 获取/设置
  • 谢谢,伙计,wtf...我需要更改表单上圆圈的颜色。
【解决方案2】:

好的,我做了什么:

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 = 什么?

【讨论】:

  • 只是为了让您知道绑定的东西没有按建议和预期工作。我不得不为 dispatcher.timer 事件提供资源。无论如何,感谢您的时间和精力。这非常有帮助和激励。
猜你喜欢
  • 1970-01-01
  • 2016-09-17
  • 2021-11-21
  • 1970-01-01
  • 2012-07-06
  • 2018-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多