Results 1 to 8 of 8

Thread: [RESOLVED] System Timer and DataBinding

  1. #1

    Thread Starter
    Still learning kebo's Avatar
    Join Date
    Apr 2004
    Location
    Gardnerville,nv
    Posts
    3,757

    Resolved [RESOLVED] System Timer and DataBinding

    Hi All,
    If I have a class that implements INotifyPropertyChanged, and I have a property in that class that is bound to a label on a form, how do I avoid a Cross-threaded exception if I set the property value from a System.Timers.Timer.Elapsed event handler?

    The following code demonstrates the exception.

    Thanks for looking
    Kevin

    VB.Net Code:
    1. Imports System.ComponentModel
    2. Imports System.Timers
    3.  
    4. Public Class Form1
    5.  
    6.     Private thisClass As New aClass
    7.  
    8.     Private lbl As System.Windows.Forms.Label
    9.     Public Sub New()
    10.  
    11.         InitializeComponent()
    12.  
    13.         'create the label and add it to the form
    14.         lbl = New Label
    15.         lbl.Text = "some text"
    16.         Me.Controls.Add(lbl)
    17.  
    18.         'set the data binding and start a timer
    19.         lbl.DataBindings.Add("Text", thisClass, "X")
    20.  
    21.  
    22.     End Sub
    23.  
    24.  
    25. End Class
    26.  
    27. Public Class aClass
    28.     Implements INotifyPropertyChanged
    29.  
    30.     Private WithEvents tmr As New System.Timers.Timer()
    31.     Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    32.     Public Sub New()
    33.  
    34.         AddHandler tmr.Elapsed, AddressOf tmr_Elapsed
    35.         tmr.Interval = 1000
    36.  
    37.         tmr.Start()
    38.     End Sub
    39.  
    40.     Private Sub tmr_Elapsed(sender As Object, e As ElapsedEventArgs)
    41.         'change the property value when the timer elapses
    42.         X = Guid.NewGuid.ToString
    43.     End Sub
    44.  
    45.  
    46.     Private _x As String = ""
    47.     Public Property X As String
    48.         Get
    49.             Return _x
    50.         End Get
    51.         Set(value As String)
    52.             _x = value
    53.             RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("X"))
    54.         End Set
    55.     End Property
    56. End Class
    Last edited by kebo; Oct 22nd, 2020 at 04:05 PM.
    Process control doesn't give you good quality, it gives you consistent quality.
    Good quality comes from consistently doing the right things.

    Vague general questions have vague general answers.
    A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.

    ______________________________
    Last edited by kebo : Now. Reason: superfluous typo's

  2. #2
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,711

    Re: System Timer and DataBinding

    I'm mainly posting this because I want to subscribe to this thread because I'm not entirely sure. This looks like something JMcIlhinney, .paul., or TG would have a good idea on.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  3. #3

    Thread Starter
    Still learning kebo's Avatar
    Join Date
    Apr 2004
    Location
    Gardnerville,nv
    Posts
    3,757

    Re: System Timer and DataBinding

    The way to handle it in the original code where the timer is a member of the form is to simply Invoke in the timer elapsed handler...
    VB.net Code:
    1. Me.Invoke(New Action(Sub() thisClass.X = Guid.NewGuid.ToString()))

    That said, my real problem is when the timer is not in the form and is a member of the class. I have updated the code in the original post to reflect this.

    I suppose we'll have to wait for one of the smart kids.
    Last edited by kebo; Oct 23rd, 2020 at 08:37 AM.
    Process control doesn't give you good quality, it gives you consistent quality.
    Good quality comes from consistently doing the right things.

    Vague general questions have vague general answers.
    A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.

    ______________________________
    Last edited by kebo : Now. Reason: superfluous typo's

  4. #4
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,988

    Re: System Timer and DataBinding

    The way I would do this would be to Post an event to the SynchronizationContext. If you want to see an example of that, look for the UDP class I posted in the CodeBank. That used a background thread as a listener, and when a message was received, it added it to a Queue, then posted an event. By posting to the UI SynchronizationContext, the event gets raised on the UI thread. You'd then set the label in there.

    I believe that JMC showed a simpler way to do that than the one that I used in that UDP class, but the one in the UDP class is pretty doggone easy.
    My usual boring signature: Nothing

  5. #5
    Fanatic Member Delaney's Avatar
    Join Date
    Nov 2019
    Location
    Paris, France
    Posts
    845

    Re: System Timer and DataBinding

    Quote Originally Posted by dday9 View Post
    I'm mainly posting this because I want to subscribe to this thread because I'm not entirely sure. This looks like something JMcIlhinney, .paul., or TG would have a good idea on.
    Hello Dday9,

    You can subscribe to a thread without posting: At the top of the thread, there is "Thread Tools" list and in it there is the command "subscribe to this thread..."
    The best friend of any programmer is a search engine
    "Don't wish it was easier, wish you were better. Don't wish for less problems, wish for more skills. Don't wish for less challenges, wish for more wisdom" (J. Rohn)
    “They did not know it was impossible so they did it” (Mark Twain)

  6. #6
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: System Timer and DataBinding

    Quote Originally Posted by Shaggy Hiker View Post
    The way I would do this would be to Post an event to the SynchronizationContext. If you want to see an example of that, look for the UDP class I posted in the CodeBank. That used a background thread as a listener, and when a message was received, it added it to a Queue, then posted an event. By posting to the UI SynchronizationContext, the event gets raised on the UI thread. You'd then set the label in there.

    I believe that JMC showed a simpler way to do that than the one that I used in that UDP class, but the one in the UDP class is pretty doggone easy.
    If you have code that is not UI-related itself but may affect an application's UI and you want to make it thread-safe then the SynchronizationContext is definitely the way to go. The Send method is akin to Invoke and Post is akin to BeginInvoke but you don't need access to a control created on the UI thread in order to call them. You just need to make sure that you create your SynchronizationContext instance on the UI thread in the first place. That's not usually a problem because you'll generally be creating the parent object on the UI thread and you just create the SynchronizationConext at the same time.

  7. #7

    Thread Starter
    Still learning kebo's Avatar
    Join Date
    Apr 2004
    Location
    Gardnerville,nv
    Posts
    3,757

    Re: System Timer and DataBinding

    Thanks Shaggy.
    I have this resolved using the code below. I've added a SynchronizationContextStringArgs class that is used to pass a property name and value to the SynchronizationContext.Post method. In the method, I then use reflection to set the property value (via casting with CtypeDynamic) which in turn raises the PropertyChange event and updates the label.

    VB.net Code:
    1. Imports System.ComponentModel
    2. Imports System.Reflection
    3. Imports System.Timers
    4.  
    5. Public Class Form1
    6.  
    7.     Private thisClass As New aClass
    8.     Private lbl As System.Windows.Forms.Label
    9.  
    10.     Public Sub New()
    11.  
    12.         InitializeComponent()
    13.  
    14.         'create the label and add it to the form
    15.         lbl = New Label
    16.         lbl.Text = "some text"
    17.         Me.Controls.Add(lbl)
    18.  
    19.         'set the data binding and start a timer
    20.         lbl.DataBindings.Add("Text", thisClass, "X")
    21.  
    22.     End Sub
    23. End Class
    24.  
    25. Public Class aClass
    26.     Implements INotifyPropertyChanged
    27.  
    28.     Private WithEvents tmr As New System.Timers.Timer()
    29.     Private Shared myContext As System.Threading.SynchronizationContext
    30.  
    31.     Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    32.  
    33.     Public Sub New()
    34.  
    35.         myContext = System.Threading.SynchronizationContext.Current
    36.  
    37.         AddHandler tmr.Elapsed, AddressOf tmr_Elapsed
    38.         tmr.Interval = 1000
    39.         tmr.Start()
    40.  
    41.     End Sub
    42.  
    43.     Private Sub tmr_Elapsed(sender As Object, e As ElapsedEventArgs)
    44.  
    45.         Dim newValue As String = Guid.NewGuid.ToString
    46.         Dim args As New SynchronizationContextStringArgs
    47.         args.PropertyName = "X"
    48.         args.NewValue = newValue
    49.         [B]myContext.Post(AddressOf setProperty, args)[/B]
    50.  
    51.     End Sub
    52.  
    53.     Private _x As String = ""
    54.     Public Property X As String
    55.         Get
    56.             Return _x
    57.         End Get
    58.         Set(value As String)
    59.             _x = value
    60.             RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("X"))
    61.         End Set
    62.     End Property
    63.  
    64.     Private Sub setProperty(e As SynchronizationContextStringArgs)
    65.  
    66.         Dim prop As PropertyInfo = Me.GetType().GetProperty(e.PropertyName, BindingFlags.Public Or BindingFlags.Instance)
    67.         If (prop IsNot Nothing) Then
    68.             prop.SetValue(Me, CTypeDynamic(e.NewValue, prop.PropertyType), Nothing)
    69.         End If
    70.  
    71.     End Sub
    72. End Class
    73.  
    74. Public Class SynchronizationContextStringArgs
    75.     Inherits EventArgs
    76.     Public Property PropertyName As String
    77.     Public Property NewValue As Object
    78. End Class
    Last edited by kebo; Oct 23rd, 2020 at 09:37 AM.
    Process control doesn't give you good quality, it gives you consistent quality.
    Good quality comes from consistently doing the right things.

    Vague general questions have vague general answers.
    A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.

    ______________________________
    Last edited by kebo : Now. Reason: superfluous typo's

  8. #8

    Thread Starter
    Still learning kebo's Avatar
    Join Date
    Apr 2004
    Location
    Gardnerville,nv
    Posts
    3,757

    Re: [RESOLVED] System Timer and DataBinding

    As I was moving this code into my project, I realized it could be simplified greatly by posting to a ThreadSafe_PropertyChanged method directly from the property setter. That method has a parameter for the property name and it raises the Property_Change method. The following code illustrates this.

    VB.net Code:
    1. Imports System.ComponentModel
    2. Imports System.Reflection
    3. Imports System.Timers
    4.  
    5. Public Class Form1
    6.  
    7.     Private thisClass As New aClass
    8.     Private lblX As System.Windows.Forms.Label
    9.     Private lblY As System.Windows.Forms.Label
    10.  
    11.  
    12.     Public Sub New()
    13.  
    14.         InitializeComponent()
    15.  
    16.         'create the label and add it to the form
    17.         lblX = New Label
    18.         lblX.Text = "X label"
    19.         Me.Controls.Add(lblX)
    20.  
    21.         lblY = New Label
    22.         lblY.Location = New Point(0, 30)
    23.         lblY.Text = "Y label"
    24.         Me.Controls.Add(lblY)
    25.  
    26.         'set the data binding and start a timer
    27.         lblX.DataBindings.Add("Text", thisClass, "X")
    28.         lblY.DataBindings.Add("Text", thisClass, "Y")
    29.     End Sub
    30. End Class
    31.  
    32. Public Class aClass
    33.     Implements INotifyPropertyChanged
    34.  
    35.     Private WithEvents tmr As New System.Timers.Timer()
    36.     Private myContext As System.Threading.SynchronizationContext
    37.     Private rnd As New Random
    38.     Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    39.  
    40.     Public Sub New()
    41.  
    42.         myContext = System.Threading.SynchronizationContext.Current
    43.  
    44.         AddHandler tmr.Elapsed, AddressOf tmr_Elapsed
    45.         tmr.Interval = 1000
    46.         tmr.Start()
    47.  
    48.     End Sub
    49.  
    50.     Private Sub tmr_Elapsed(sender As Object, e As ElapsedEventArgs)
    51.  
    52.         X = Guid.NewGuid.ToString()
    53.         Y = rnd.NextDouble()
    54.  
    55.     End Sub
    56.  
    57.     Private _x As String = ""
    58.     Public Property X As String
    59.         Get
    60.             Return _x
    61.         End Get
    62.         Set(value As String)
    63.             _x = value
    64.             myContext.Post(AddressOf ThreadSafe_PropertyChanged, "X")
    65.         End Set
    66.     End Property
    67.  
    68.     Private _y As Double = 0
    69.     Public Property Y As String
    70.         Get
    71.             Return _y
    72.         End Get
    73.         Set(value As String)
    74.             _y = value
    75.             myContext.Post(AddressOf ThreadSafe_PropertyChanged, "Y")
    76.         End Set
    77.     End Property
    78.  
    79.     Private Sub ThreadSafe_PropertyChanged(propertyName As String)
    80.         RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    81.  
    82.     End Sub
    83. End Class
    Process control doesn't give you good quality, it gives you consistent quality.
    Good quality comes from consistently doing the right things.

    Vague general questions have vague general answers.
    A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.

    ______________________________
    Last edited by kebo : Now. Reason: superfluous typo's

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width