提问人:Lexxy_B 提问时间:12/22/2022 最后编辑:Reza AghaeiLexxy_B 更新时间:1/28/2023 访问量:70
多线程 UI、WindowsForms 文本和颜色属性之间的不同 bahavior..为什么?
Multithreading UI WindowsForms different bahavior between Text an Color Property..WHY?
问:
我已经搜索了它,但我找不到任何可以正确解释它的东西。 如果答案已经存在,请链接。
问题:
为什么 ForeColor 属性的行为与 Text 属性不同?
Text 和 Forecolor 属性之间似乎存在差异。
我需要 Label1.Text 的 Invoke,但不需要 Label1.Forecolor 的调用?
如果我将 Forecolor 绑定到模块中分配的属性(代码如下),则交叉线程没有问题。
当我以相同的方式绑定文本时,会出现一条错误消息:
英文的错误消息是:
无效的跨线程操作:lbl_con控件是从与创建该控件的线程不同的线程访问的
我知道我可以通过调用MainForm-(Thread)来避免这种情况,但是从另一个模块执行此操作时,这有点困难。有没有其他方法可以做到这一点?(感谢输入)。
MainForm.vb
这是主窗体,从这里我开始一个单独的线程
Public Class FormBindings
Sub New()
InitializeComponent()
Bind()
End Sub
Public th As Thread
Sub Bind()
lbl_con.DataBindings.Add(NameOf(lbl_con.ForeColor), mdl_Bind, NameOf(mdl_Bind.ConnectedColor), False, DataSourceUpdateMode.OnPropertyChanged)
lbl_con.DataBindings.Add("Text", mdl_Bind, "Txt", False, DataSourceUpdateMode.OnPropertyChanged)
End Sub
Private Sub Btn_StartThread_Click(sender As Object, e As EventArgs) Handles Btn_StartThread.Click
th = New Thread(AddressOf ThreadWork)
th.Start()
End Sub
Private Sub Btn_StopThread_Click(sender As Object, e As EventArgs) Handles Btn_StopThread.Click
th.Abort()
End Sub
Sub ThreadWork()
Do While True
If mdl_Bind.Connected = True Then
mdl_Bind.Connected = False
mdl_Bind.Txt = String.Format("True")
Else
mdl_Bind.Connected = True
mdl_Bind.Txt = String.Format("False")
End If
Thread.Sleep(1000)
Loop
End Sub
End Class
模块.vb
在此模块中,我创建了一个类对象,其中包含所有可以更改的属性
Module mdl_Results
Public mdl_Bind As New cl_Bindings With {.Connected = False, .Txt = "ConnectionLabel"}
End Module
cl_Bingings.vb
这是属性的类
Public Class cl_Bindings
Inherits cl_PropChanged
Private _connected As Boolean
Public Property Connected() As Boolean
Get
Return _connected
End Get
Set(ByVal value As Boolean)
_connected = value
If value Then
ConnectedColor = Color.Green
Else
ConnectedColor = Color.Red
End If
NotifyPropertyChanged("Connected")
End Set
End Property
Private _ConnectedColor As Color
Public Property ConnectedColor() As Color
Get
Return _ConnectedColor
End Get
Set(ByVal value As Color)
_ConnectedColor = value
NotifyPropertyChanged()
End Set
End Property
Private _Txt As String
Public Property Txt() As String
Get
Return _Txt
End Get
Set(ByVal value As String)
_Txt = value
NotifyPropertyChanged()
End Set
End Property
End Class
cl_PropChanged.vb
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Public MustInherit Class cl_PropChanged
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Overridable Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
详细
为了更具体,让你作为想给我答案的人更容易,让我试着把所有东西都总结一下:
Sub ThreadWork()
'This is the problematic Part inside the MainForm.vb
Do While True
If mdl_Bind.Connected = True Then
mdl_Bind.Connected = False ' This Works perfectly
mdl_Bind.Txt = String.Format("False") ' Heres comes the boom
Else
mdl_Bind.Connected = True ' This Works perfectly
mdl_Bind.Txt = String.Format("True") ' Heres comes the boom
End If
Thread.Sleep(1000) ' just to see it alternating on UI
Loop
End Sub
感谢您的所有回答。
答:
因为 Foreground 属性显然不会直接更改 UI。只是一些内部实现细节,但不要依赖它,永远不要从另一个线程访问 UI 控件(这也适用于读取操作!
根据我的经验,最好使用 (使用 UI 线程)使用 中的值更新 UI 控件。创建一个帮助类来跟踪属性是否已更改(我通常称之为 ),并像这样存储所有属性。然后,在计时器的事件处理程序中,检查所有 ChangeTracker-Properties 的值自上次检查以来是否已更改,如果是,则将该值设置为 UI 控件,而忘记 WPF 的 .System.Windows.Forms.Timer
cl_bindings
ChangeTracker(Of T)
True
INotifyPropertyChanged
对于相反的方向,请在 UI 控件上使用普通事件处理程序,以保持 的属性同步。OnChange
cl_bindings
下面是一个示例:ChangeTracker
Public Structure ChangeTracker(Of T)
<DebuggerBrowsable(DebuggerBrowsableState.Never)>
Private HasNotChanged As Boolean
<DebuggerBrowsable(DebuggerBrowsableState.Never)>
Private _Value As T
Public Sub New(initialValue As T)
Value = initialValue
End Sub
Public ReadOnly Property HasChanged As Boolean
Get
Dim result As Boolean = Not HasNotChanged
If (result) Then
HasNotChanged = True
End If
Return result
End Get
End Property
Public Property Value As T
Get
Return _Value
End Get
Set(value As T)
_Value = value
HasNotChanged = False
End Set
End Property
Public Shared Widening Operator CType(value As T) As ChangeTracker(Of T)
Return New ChangeTracker(Of T)(value)
End Operator
Public Shared Widening Operator CType(changeTracker As ChangeTracker(Of T)) As T
Return changeTracker.Value
End Operator
End Structure
你会像这样使用它:cl_Bindings
Public Class cl_Bindings
Private _connected As ChangeTracker(Of Boolean)
Public Property Connected() As Boolean
Get
Return _connected
End Get
Set(ByVal value As Boolean)
_connected = value
If value Then
ConnectedColor = Color.Green
Else
ConnectedColor = Color.Red
End If
End Set
End Property
Public Property ConnectedColor As ChangeTracker(Of Color)
Public Property Txt As ChangeTracker(Of String)
End Class
评论
ForeColor
Control.ForeColor
是 Ambient 属性,因此它只影响控件的 PropertyStore。相反,设置 Text 属性值是对 的调用,它不是线程安全的(在读取 Text 时,这意味着调用是安全的,或者可以是)——关键是,你不需要线程。在任何情况下,您也不得致电。如果需要卸载工作,请使用 awaitable Tasks。当无法等待任务时,将使用委托执行 UI 更新,并将其传递给任务SetWindowText()
GetWindowTextLength()
GetWindowText()
Thread.Abort()
IProgress<T>