CallBacks, or what do I really need?


Public ClassxxForm
Public zPanel as New xxPanel
End Class

Public ClassxxPanel
Public zLabel as New xxLabel(Me)
Public Property Ticker() As Long
End Class

Public ClassxxLabel
Private zParentPanel as xxPanel(<constructed in New(xxPanel) sub)
Me.Text = zParentPanel.Ticker.toString
End Class



Above pseudo-code: the xxForm creates an xxPanel, and the xxPanel creates an xxLabel.

How can I bind an xxLabel.Text output to its parent xxPanel's Ticker returnvalue so that the label continuously reflects the changing returnvalue, in a threadsafe manner?

After looking into databinding, events and multithreading, I now think I need to set a delegate in xxPanel that points to a function in xxPanel which returns the [TickerValue] from the Ticker property, and then the xxLabel needs to invoke the xxPanel's delegate to fetch the TickerValue.

I think this is what a 'callback' is, but I've never used it, so if someone could show me the way with my xxPanel/xxLabel example, I would very much appreciate it.

I've gotten as far as the following but I'm obviously not 'getting it' yet as I'm still seeing IllegalCrossThread errors, so what am I doing wrong?


Public ClassxxForm
Public zPanel as New xxPanel
End Class

Public ClassxxPanel
Delegate Function fName() As [Long]
Public zDelegate as fName(<constructed toNew fName(AddressOf delFunction))
Public zLabel as New xxLabel(Me)
Public Function delFunction() As [Long](<returns Me.Ticker)
Public Property Ticker() As Long
End Class

Public ClassxxLabel
Public zParentPanel as xxPanel
(<constructed in New(xxPanel) sub)
Me.Text =zParentPanel.Invoke(zParentPanel.zDelegate).toString
End Class


[4929 byte] By [nogChoco] at [2007-12-28]
# 1

Hi,

Are you using .NET 2.0? If so, you can use the BackgroundWorker. Here is an example: http://geertverhoeven.blogspot.com/2007/01/using-backgroundworker.html

Greetz,

Geert

Geert Verhoeven
Consultant @ Ausy Belgium

My Personal Blog

GeertVerhoeven at 2007-9-4 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 2

Hello!

About your request

Public Class xxForm
Public zPanel as New xxPanel
End Class

Public Class xxPanel
Public zLabel as New xxLabel(Me)
Public Property Ticker() As Long
End Class

Public Class xxLabel
Private zParentPanel as xxPanel (<constructed in New(xxPanel) sub)
Me.Text = zParentPanel.Ticker.toString
End Class

you can easily use the idea of events for example

if i have a user control that uses n-Label

the question is how can i handle the event click for example

the answer is by using The AddHandler

and here in ur clase xxPanel , u can use this

Public Class xxPanel

Public zLabel As xxLabel

Public Sub New()

zLabel=New xxLabel(...)

AddHandler zLabel.TextChanged, AddressOf zLabel_TextChanged

End Sub

Private Sub zLabel_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _

Handles zLabel.TextChanged

' here goes your code to control the changes in ur zLabel control

End Sub

End Class

Monah84 at 2007-9-4 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 3

Hi

Is it possible that you can refactor your design a little as follows.

If the panel creates the label in its constructor, then I fail to see why it makes sense to encapsulate the binding to the parent Ticker method within the label class.

Perhaps it would be simpler to have the parent update the labels text each time it changes its property value?

If you cannot take this approach, please leave a little more information as to why you must bind from the label to the panel, and I'll put together a data binding example for you

Richard

Public Class xxPanel

Inherits ContainerControl

Private ReadOnly mLabel As xxLabel

Private mTimer As Timer

Public Sub New()

MyBase.New()

Me.mLabel = New xxLabel : MyBase.Controls.Add(Me.mLabel)

Me.mTimer = New Windows.Forms.Timer()

Me.mTimer.Interval = 1000 : Me.mTimer.Start()

AddHandler Me.mTimer.Tick, New EventHandler(AddressOf Me.TimerTick)

End Sub

Private Sub TimerTick(ByVal sender As Object, ByVal e As EventArgs)

Static tickCount As Long

tickCount += 1

Me.mLabel.Text = tickCount.ToString

End Sub

End Class

DickDonny at 2007-9-4 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 4
Thanks for your replies so far - it's really appreciated!

I'm using VB.NET to create a little app that shows the current networkspeeds (or whatever network statistic I point it at) and displays it as a nice looking historycurve (=customized panel that does the calculations) with extra labels that show the maximum/average/speeds as they change. The picture in link below has a running example of my app.

The reason I made the xxLabels seperate objects as opposed to incorporating them in the xxPanel's paintcode, is because this allows me to easily resize/reposition them manually within their parent xxPanel on runtime. (and I'd also like to make my app available as opensource on SourceForge afterwards,

which is why I'd like it to have as much 'proper' object coding as possible).

http://img77.imageshack.us/my.php?image=netspeedexampleqi8.jpg
The xxPanel with the speedhistorycurve is filling the form, and the xxLabel is in the upper-left corner (orange). The form's text is showing the maxspeed which is a property of xxPanel, and the xxLabel should be showing that too (currently using databindings but it doesn't update).


@ Monah84:

The eventhandlers aren't really a problem, I think,

it's the setting of xxLabel.Text = xxParentPanel.Ticker within the eventhandling subs that is causing

the illegalcrossthread errors.


@ Dick Donny:
I tried your code, and the thing that makes it work, is the fact that you're using a Windows.Forms.Timer -- I'm using a System.Timers.Timer -- so I'm assuming that the Forms.TimerTicks are synchronizing both objects somehow, but I don't know whether this may still lead to crossthreading errors in certain situations. I'll try switching to a Forms.Timer in both xxPanel and xxLabel and see what happens.

I tried databindings ( xxLabel.Text = xxLabel.databindings.add("Text", xxParentPanel, "Ticker") ) with INotifyPropertyChanged implemented in the xxPanel, but the xxLabel only updates once when the xxLabel is created - which I frankly don't understand why it wouldn't persist. Extra examples are always welcome ;)

@ Geert Verhoeven: ( toffe blog ;) )
I'm assuming a backgroundworker in my app (as it is coded now) would be created by the xxPanel simply to host the calculated netspeed/TickerValue and then use its completion event to signal to the xxLabel that a new speedtickervalue is ready to be shown. And better would probably be, to offload the whole speedcalculation onto the Backgroundworker and have that signal both xxPanel and xxLabel? Do I need seperate backgroundworkers for each object's property that I want to access from another object?

The control.invoke method had also lead me to BackgroundWorker as it's

supposed to be the newer way of doing asynchronous stuff, but I'm gonna

have to look into it a little longer to get the multithreading stuff. The

control.invoke + Delegate stuff is supposed to be threadsafe too, so if

anyone could clarify that with a working xxPanel/xxLabel example, I'd

sure appreciate it.

nogChoco at 2007-9-4 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 5
Yes!

Dick Donny's use of the Windows.Forms.Timer made me think about the hidden object-synchronizations done by it, and that made me realize that the System.Timers.Timer I was using in xxLabel, has a .SynchronizingObject method that I was pointing at 'Me' rather than the parent xxPanel object. So now there's several options that all work:

(1) Simply use Windows.Forms.Timer which automatically syncs the controls (during the Timer's Tick event).
(2) When using a System.Timers.Timer object, you can set its .SynchronizingObject to point at the parent object and that makes sure that access to the parent's properties from a child is possible during the Timer.Elapsed event.
(3) In the Elapsed event of the Timers.Timer, only update the object if its Parent.InvokeRequired = false (this avoids invoking a property on the parent when the parent isn't available/sync'd with the childobject that is calling the property). This solution will still work if the childobject's timer.SynchronizingObject is not pointing at the parent, so this is basically the preferred solution (for my problem at least). Note

that control.InvokeRequired also returns False if the control doesn't

have a handle yet, so the extra IsHandleCreated check in the example below, is necessary.

A working example pseudo-code with System.Timers (using solution (2)+(3) together):


Public Class xxForm
Public zPanel as New xxPanel
End Class

Public Class xxPanel
Public zLabel as New xxLabel(Me)
Public PanelTimer as New System.Timers.Timer
Sub New()
PanelTimer.SynchronizingObject = Me
End Sub
Public Property Ticker() As Long
End Class

Public Class xxLabel

Public zParentPanel as xxPanel
Public LabelTimer as New System.Timers.Timer
Sub New(ByVal zGivenParent as xxPanel)
zParentPanel = zGivenParent
AddHandler LabelTimer.Elapsed, AddressOf LabelTimer_Tick
LabelTimer.SynchronizingObject = zParentPanel
End Sub
Sub LabelTimer_Tick(Object, ElapsedEventArgs)
If zParentPanel.InvokeRequired = False And zParentPanel.IsHandleCreated = True Then
Me.Text = zParentPanel.Ticker.ToString
End If
End Sub
End Class



I will still look into the BackgroundWorker thing and MultiThreading, though - it'll probably come in handy one day (soon).
Thanx for the replies, folks, you helped me out a lot and it's truly appreciated!

nogChoco at 2007-9-4 > top of Msdn Tech,Visual Basic,Visual Basic Language...