在VB.net中将多个控件与多个事件同步

时间:2016-11-28 22:20:14

标签: vb.net winforms event-handling

我在WinForms GUI中有几个不同的控件,它们必须全部相互同步:我有2个文本框,它们以不同的单位显示相同的数量,2个自定义控件以图形方式显示数量为旧式模拟量表。更改文本框或自定义控件中的值会导致通过USB将新值写入硬件。

我不想要的是在更改一个控件时,将多个USB消息发送到硬件。但是,当一个控件的事件处理程序更新其他三个控件时,它们的事件处理程序也会运行。这是我到目前为止解决问题的两种方式,但我希望有更好的方法:

  1. 首先,我为每组控件制作了一个全局标志。当事件处理程序运行时,它检查标志,如果设置它,它立即存在。如果未设置该标志,则事件处理程序会设置它,然后更新其他3个控件,这些控件的执行处理程序在执行时不应执行任何操作,因为现在已设置全局标志。

  2. 现在,当我在第一个事件处理程序中更新它们时,我使用AddHandler和RemoveHandler语句来关闭/打开其他3个控件的事件处理程序。这会阻止其他事件处理程序在第一时间点火。

  3. 我的第一个解决方案很简单,但感觉就像一个大规模的黑客攻击。第二个解决方案意味着我的所有事件处理程序都需要为AddHandler / RemoveHandler语句添加6个额外的行。

    我希望有一些更好的东西,我从来没有梦想过(也许是一些固有的方式告诉vb.net控件是否链接?)但谷歌搜索还没有帮助我。我很感激你能得到的任何帮助!

2 个答案:

答案 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实例绑定到你的控件上 您可以创建FormatParse事件处理程序,以便将原始单位转换为您要显示和返回的单位。
例如,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