我在WinForms GUI中有几个不同的控件,它们必须全部相互同步:我有2个文本框,它们以不同的单位显示相同的数量,2个自定义控件以图形方式显示数量为旧式模拟量表。更改文本框或自定义控件中的值会导致通过USB将新值写入硬件。
我不想要的是在更改一个控件时,将多个USB消息发送到硬件。但是,当一个控件的事件处理程序更新其他三个控件时,它们的事件处理程序也会运行。这是我到目前为止解决问题的两种方式,但我希望有更好的方法:
首先,我为每组控件制作了一个全局标志。当事件处理程序运行时,它检查标志,如果设置它,它立即存在。如果未设置该标志,则事件处理程序会设置它,然后更新其他3个控件,这些控件的执行处理程序在执行时不应执行任何操作,因为现在已设置全局标志。
现在,当我在第一个事件处理程序中更新它们时,我使用AddHandler和RemoveHandler语句来关闭/打开其他3个控件的事件处理程序。这会阻止其他事件处理程序在第一时间点火。
我的第一个解决方案很简单,但感觉就像一个大规模的黑客攻击。第二个解决方案意味着我的所有事件处理程序都需要为AddHandler / RemoveHandler语句添加6个额外的行。
我希望有一些更好的东西,我从来没有梦想过(也许是一些固有的方式告诉vb.net控件是否链接?)但谷歌搜索还没有帮助我。我很感激你能得到的任何帮助!
答案 0 :(得分:1)
您肯定只需要一个可以进行更新的地方 你没有提到你有什么样的UI(Winforms,WPF - 假设你在谈论桌面应用程序)。
我建议使用MVVM样式 - 它适用于Winforms和WPF。
Public Class USBViewModel
Implements INotifyPropertyChanged
Private _Quantity As Integer
Public Property Quantity As Integer
Get
Return _Quantity
End Get
Set(value As Integer)
If value = _Quantity Then Exit Property
' Here you can update value to the USB
_Quantity = value
RaisePropertyChanged()
End Set
End Property
#Region "INotifyPropertyChanged"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub RaisePropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
#End region
End Class
然后你可以将一个(相同的)USBViewModel实例绑定到你的控件上
您可以创建Format
和Parse
事件处理程序,以便将原始单位转换为您要显示和返回的单位。
例如,Winforms中的文本框可以这样限制:
Public Class YourForm
Private ReadOnly _ViewModel As USBViewModel
Public Sub New()
InitializeComponents()
_ViewModel = new USBViewModel()
var unitOneBunding = new Binding("Text", _ViewModel, "Quantity", True);
unitOneBunding.Format += bindUnitOne_Format;
unitOneBunding.Parse += bindUnitOne_Parse;
TextBoxUnitOneQuantity.DataBindings.Add(unitOneBunding);
End Sub
private void bindUnitOne_Format(object sender, ConvertEventArgs e)
{
int originalUnitValue = (int)e.Value;
int unitOneValue = ConvertToUnitOne(originalUnitValue);
e.Value = unitOneValue;
}
private void bindUnitOne_Parse(object sender, ConvertEventArgs e)
{
int unitOneValue = (int)e.Value;
int originalUnitValue = ConvertToOriginalUnit(unitOneValue);
e.Value = originalUnitValue;
}
End Class
对于自定义控件,您可以将此相同的实例传递给自定义控件构造函数,并将其绑定到同一属性。
此解决方案基于INotifyPropertyChanged
和数据绑定
当您使用数据绑定控件时,将&#34; listen&#34;对于INotifyPropertyChanged
事件,并在值更改时自行更新
在用户更新控件的情况下,控件将调用有界属性的setter。
答案 1 :(得分:1)
通过级联事件处理程序很容易将自己绑在结上。我建议进行&#34;保存更改&#34;按钮和USB消息仅在单击该按钮时发送。
对于您的文本框,您可以使用TextChanged
中的其他事件。例如,如果您使用Validating
,则可以在提交之前检查输入的值,更新其他文本框不会触发无限循环:
Private Sub TextBox1_Validating(sender As Object, e As CancelEventArgs) Handles TextBox1.Validating
If Val(TextBox1.Text) < 0 Then
e.Cancel = True 'don't allow negative numbers
Else
TextBox2.Text = (Val(TextBox1.Text) / 10).ToString
End If
End Sub
Private Sub TextBox2_Validating(sender As Object, e As CancelEventArgs) Handles TextBox2.Validating
If Val(TextBox2.Text) < 0 Then
e.Cancel = True 'don't allow negative numbers
Else
TextBox1.Text = (Val(TextBox2.Text) * 10).ToString
End If
End Sub
如果您真的想要实时单位转换,那么另一种方法是检查ActiveControl
:
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
If ActiveControl Is TextBox1 Then
TextBox2.Text = (Val(TextBox1.Text) / 10).ToString
End If
End Sub
Private Sub TextBox2_TextChanged(sender As Object, e As EventArgs) Handles TextBox2.TextChanged
If ActiveControl Is TextBox2 Then
TextBox1.Text = (Val(TextBox2.Text) * 10).ToString
End If
End Sub