|
-
Aug 22nd, 2013, 08:35 AM
#1
Thread Starter
Hyperactive Member
[RESOLVED] Help getting started with DataRepeater
Hi Folks
I have a dictionary(Of UInteger, object) that I would like to display some of the properties of the value objects in list form.
Three of the properties are simple text strings. The fourth is a ProgressBar.
I've been thinking that a DataRepeater would be best for this (though I'm not sure, and it could easily be changed at the current stage)
However I have been having trouble understanding how to correctly bind the dictionary to the DataRepeater. I have tried searching Google for some time, but not come across any helpful information.
Firstly, can/should dictionaries be bound to DataRepeaters?
Secondly, if a dictionary is bound to a GUI control will accessing the properties of one of the dictionary's values require invocation? i.e. will the dictionary be tied to the GUI thread?
Thirdly, and most importantly, how should the view be updated when an object is added or removed from the dictionary? From my exceedingly limited understanding of binding, this should happen automatically, yet in my trials so far it is not.
My experiments so far have been to create the dictionary and add one item to it. Bind the dictionary to the DataRepeater (as shown in the code below). Add a button that adds a either adds a second item to the dictionary, or if it has already done that, removes the second item from the dictionary.
I would have expected that when I clicked the button to add a dictionary item, the addition would be shown by listing another item in the DataRepeater, and removed again for a subsequent click... but it does not.
VB Code:
Public Class Test Public Shared currentDuts As New Dictionary(Of UInteger, DeviceUnderTest) ' Collection of all current DUTs. Dim bs As New BindingSource Dim testTemp As Boolean = False Private Sub Form1_Load () Handles Me.Load '... currentDuts.Add(0, New DeviceUnderTest(Me.user, 0)) currentDuts.Item(0).RackBay = "012345678901" currentDuts.Item(0).AssemblySerial = "123456789" currentDuts.Item(0).SetProgram(1, "Program1") bs.DataSource = currentDuts.Values LocationLabel.DataBindings.Add("Text", bs, "AssemblySerial") DutLabel.DataBindings.Add("Text", bs, "Id") ProgramLabel.DataBindings.Add("Text", bs, "Program") DutProgress.DataBindings.Add("Value", bs, "Progress") DutData.DataSource = bs '... Me.Show End Sub Private Sub Button1_Click() Handles Button1.Click If testTemp = False Then currentDuts.Add(1, New DeviceUnderTest(Me.user, 1)) currentDuts.Item(1).RackBay = "109876543210" currentDuts.Item(1).AssemblySerial = "1319A5126" currentDuts.Item(1).SetProgram(1, "Program1") testTemp = True Else currentDuts.Remove(1) testTemp = False End If End Sub End Class
Thanks 
-
Aug 22nd, 2013, 11:51 AM
#2
Re: Help getting started with DataRepeater
Three of the properties are simple text strings. The fourth is a ProgressBar.
Huh? How can a ProgressBar be a property?
Firstly, can/should dictionaries be bound to DataRepeaters?
Well, you've done the experiments. You tell us! The question I'm asking myself however, is why you're using a dictionary at all. It appears that you need nothing more or less than a List(Of DeviceUnderTest) and that definitely can be bound to a DataRepeater (or any other binding capable control).
Secondly, if a dictionary is bound to a GUI control will accessing the properties of one of the dictionary's values require invocation? i.e. will the dictionary be tied to the GUI thread?
This really doesn't make any sense, even as a question. Invocation applies to controls on the UI thread. Since neither the Dictionary nor the Object in question appear to be controls, I have no idea how it could possible apply.
Thirdly, and most importantly, how should the view be updated when an object is added or removed from the dictionary? From my exceedingly limited understanding of binding, this should happen automatically, yet in my trials so far it is not.
There's binding and then there's binding. Not all bindings auto-update but I confess it's so long since I even looked at the DataRepeater that I can't tell you whether this is one of them (I might have a play in a minute). I suspect that you have other issues to sort out before you worry about that though.
As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"
Reviews: "dunfiddlin likes his DataTables" - jmcilhinney
Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!
-
Aug 22nd, 2013, 12:19 PM
#3
Thread Starter
Hyperactive Member
Re: Help getting started with DataRepeater
Hi Dunfiddlin
Thanks for your reply. apologies, I realise I wasn't too clear on some things 
As regards the PogressBar I meant that the fourth item in the DataRepeaterItemTemplate is a ProgressBar - The progress level should be set by the Progress property of a DeviceUnderTest value from the dictionary.
The reason I am using a dictionary is that the value objects are acting as contexts for a set of devices I am testing. each device has a set of kind of "independent" threads running tasks for it. The threads know which context they are using by the key. As objects don't need to be removed in any strict order I could not just used the index of a list or array as that would change as objects are added or removed. I suppose I could change to using a list and having the object store a UID, but I kind of thought that was the point of dictionaries? (Im actually asking here )
I have done some experiments and they have, obv, only part way worked. So Im none the wiser. In some places the documentation says that objects to be bound must have the IList interface (or a variation thereof). Though the documentation seems to imply that dictionaries don't have this, it is a collection and collections do... so again, confused. Maybe Im attempting to do something that cannot be done or maybe Im just doing it wrong, I dont know - this is the crux of my question.
I have since tried using a list instead in a test and the same problem is found - what is on the list at bind-time is what is displayed and the display never seems to change regardless of changes to the bound list - so I guess Im just doing it wrong - yet I have found no source of information that suggests other ways of doing this (more on that below).
What I meant about the invocation thing is that the DataRepeater is a control. The object is bound to it. If a thread tries to change a property of the bound object is it by proxy changing the control and will such an action thus require invocation?
My own attempts to get this working or find out any further info on the net have been frustrating. Nearly every web page I could find on this either uses very simple examples of binding to a database datasource, or does nearly the same in ASP or C# where it seems that the DataRepeater and binding to it is sufficiently different to confound me in this matter.
Without already having this knowledge its hard to know what more to look for or how to search more directly fro the information I need.
Thanks 
-
Aug 22nd, 2013, 12:56 PM
#4
Re: Help getting started with DataRepeater
Though the documentation seems to imply that dictionaries don't have this, it is a collection and collections do... so again, confused.
In so far as it is a collection it is a collection of keyvaluepairs. It's distinct from all other collections in this respect. The properties of objects in the value half are subsumed by the value so if it was IList compatible all you would be able to bind directly would be the key and the value object as a single entity (a two column table, if you like) which is clearly not what you want. (Just to really complicate matters you could have a value which is itself a keyvaluepair in which the value is a keyvaluepair in which the value is a keyvalue pair ... ad nauseam ad infinitum!). It is in this way much more akin to a tree than a table which is why it doesn't play nicely with the usual data controls.
what is on the list at bind-time is what is displayed and the display never seems to change regardless of changes to the bound list
As I said, there are bindings and then there are bindings. The only auto updating binding I know of is DataTable/DataGridView (DataTable/DataRepeater is a candidate but as I say I'd need to confirm that). jmc's probably your man for this. I tend to the simple where binding's concerned (for which he has upbraided me more than once) so if there is a wrinkle I freely confess to be ignorant of it!
What I meant about the invocation thing is that the DataRepeater is a control. The object is bound to it. If a thread tries to change a property of the bound object is it by proxy changing the control and will such an action thus require invocation?
With the non-updating binding, I guess that's not really an issue. I have to confess that I've never thought about it in the auto-updating situation (datatable/DGV). Another area for experimentation, I guess.
As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"
Reviews: "dunfiddlin likes his DataTables" - jmcilhinney
Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!
-
Aug 23rd, 2013, 06:15 AM
#5
Thread Starter
Hyperactive Member
Re: Help getting started with DataRepeater
I have found that changing List(Of T) to System.Component.BindingList(Of T) fixes this for use with lists.
However this is obv specific for lists. For collections or dictionaries Im still not sure how to proceed.
I see there is a System.Web.Services.Description.BindingCollection, but I am not sure it is supposed to serve the same purpose....
I may be able to try to change my dictionary to a list BindingList(Of KeyValuePair(Of TKey, TValue)) instead...
I also note that Shared BindingList members are thread safe though Im not sure how exactly this affects my current threading iimplementation.
more testing
Last edited by wolf99; Aug 23rd, 2013 at 06:19 AM.
Thanks 
-
Aug 23rd, 2013, 11:52 AM
#6
Thread Starter
Hyperactive Member
Re: Help getting started with DataRepeater
OK so I have attempted to test with a BindingList(Of KeyValuePair(Of TKey, TValue)), and I can either display the repeater with one row with data that doesnt update with changes to the list or the repeater with rows that update but contain no data... 
The code I currently have is shown below such that their are data-less rows that update with list changes. If I add "bs.DataMember = "Value" at line 20 and remove the "Value." from the strings in lines 21 and 22 then the repeater shows only the first list item and the attempt to remove anything by clicking the button appears to have no difference to the repeater display!
VB Code:
Imports System.ComponentModel Public Class Form1 Dim DeviceList As New BindingList(Of KeyValuePair(Of UInteger, Device)) Dim bs As New BindingSource() Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load Dim myDevice1 As New Device myDevice1.Name = "Test1" myDevice1.Progress = 50 Me.DeviceList.Add(New KeyValuePair(Of UInteger, Device)(0, myDevice1)) Dim myDevice2 As New Device myDevice2.Name = "TestN" myDevice2.Progress = 50 Me.DeviceList.Add(New KeyValuePair(Of UInteger, Device)(1, myDevice2)) bs.DataSource = DeviceList Label1.DataBindings.Add("Text", bs, "Value.Name") Label2.DataBindings.Add("Text", bs, "Value.Progress") DR1.DataSource = bs Me.Show() End Sub Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click If DeviceList.Count > 0 Then DeviceList.Remove(DeviceList.Last) End If End Sub End Class
Last edited by wolf99; Aug 23rd, 2013 at 11:55 AM.
Thanks 
-
Aug 23rd, 2013, 02:20 PM
#7
Re: Help getting started with DataRepeater
well tickle me pink... I've almost got it working... that is I got everythign to work fine with a List(of) ... using this code:
Code:
Dim item1 As New TestClass
item1.Name = "Item 1 Test"
item1.Progress = 50
_TestList.Add(item1)
Dim item2 As New TestClass
item2.Name = "Item 2 Test"
item2.Progress = 80
_TestList.Add(item2)
Label1.DataBindings.Add("Text", _TestList, "Name")
Label2.DataBindings.Add("Text", _TestList, "Progress")
DataRepeater1.DataSource = _TestList
the trick was setting the DataBindings FIRST... then binding the list to the DR... I get the right things showing up in the right place... I'm going to see now if I can get it to render with a Dictionary...
-tg
-
Aug 23rd, 2013, 02:28 PM
#8
Re: Help getting started with DataRepeater
HA! got it to work!
1 form, two buttons, one datarepeater with two labels....
full code:
Code:
Public Class Form2
Private _TestList As New List(Of TestClass)
Private _DeviceList As New Dictionary(Of String, TestClass)
Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Label1.DataBindings.Clear()
Label2.DataBindings.Clear()
DataRepeater1.DataSource = Nothing
_TestList.Clear()
Dim item1 As New TestClass
item1.Name = "Item 1 Test"
item1.Progress = 50
_TestList.Add(item1)
Dim item2 As New TestClass
item2.Name = "Item 2 Test"
item2.Progress = 80
_TestList.Add(item2)
Label1.DataBindings.Add("Text", _TestList, "Name")
Label2.DataBindings.Add("Text", _TestList, "Progress")
DataRepeater1.DataSource = _TestList
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Label1.DataBindings.Clear()
Label2.DataBindings.Clear()
DataRepeater1.DataSource = Nothing
_DeviceList.Clear()
Dim item1 As New TestClass
item1.Name = "Item 1 Test"
item1.Progress = 50
_DeviceList.Add(item1.Name, item1)
Dim item2 As New TestClass
item2.Name = "Item 2 Test"
item2.Progress = 80
_DeviceList.Add(item2.Name, item2)
Label1.DataBindings.Add("Text", _DeviceList.Values.ToList, "Name")
Label2.DataBindings.Add("Text", _DeviceList.Values.ToList, "Progress")
DataRepeater1.DataSource = _DeviceList.Values.ToList
End Sub
End Class
'Test Class
Public Class TestClass
Public Property Name As String
Public Property Progress As Integer
End Class
the .Values needs to be converted into a list (ala .ToList) ... I didn't check the updatablility of this... but I'm off to a meeting... maybe this will help in the mean time.
-tg
-
Aug 23rd, 2013, 04:46 PM
#9
Thread Starter
Hyperactive Member
Re: Help getting started with DataRepeater
I had to switch to a BindingList of objects and adding a "key" member to the list object class.
Because the BindingList uses events in its interfaces that mean adding/removing/changing list items need to made from the UI thread.
I have this working by using the standard DataRepeater.InvokeRequired check and Invoke delegate pattern. Im not sure however if this could also be done by checking IsSynchronized and SyncLock, but I dont know how this would be tested, maybe someone could let me know?
As I have a test project, I plan to write a tutorial on this when done so it would be great to know if folks think this is the best way to do this.
VB Code:
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Enum ListAction
ADD
REMOVE
End Enum
Public Class Device
Public Property DeviceKey As UInteger
Public Property Name As String
Public Property Progress As UInteger
End Class
Dim myDevice As New Device
Dim threadDevice As Device
Dim DeviceList As New BindingList(Of Device)
Dim bs As New BindingSource(DeviceList, Nothing)
Dim AddOrRemove As ListAction = ListAction.ADD
Dim worker As Thread
Private Delegate Sub DeviceListActionDelegate(ByVal deviceContext As Object)
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
' Bind DataSource List item properties to the DataRepeaterItem controls.
Label1.DataBindings.Add("Text", bs, "Name")
Label2.DataBindings.Add("Text", bs, "Progress")
Repeater1.DataSource = bs
' Create and populate 2 Devices and add them to the List.
myDevice = New Device
myDevice.DeviceKey = 0
myDevice.Name = "Test1"
myDevice.Progress = 50
Me.DeviceList.Add(myDevice)
myDevice = New Device
myDevice.DeviceKey = 1
myDevice.Name = "Test2"
myDevice.Progress = 50
Me.DeviceList.Add(myDevice)
' Show the Form.
Me.Show()
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If AddOrRemove = ListAction.ADD Then
' Populate new device.
threadDevice = New Device()
threadDevice.DeviceKey = 3
threadDevice.Name = "ThreadDevice"
threadDevice.Progress = 40
' Start thread to add device.
worker = New Thread(AddressOf DoAdd)
worker.Start(threadDevice)
' Flip flag.
AddOrRemove = ListAction.REMOVE
Else
threadDevice = New Device
' Remove last item from the List.
worker = New Thread(AddressOf DoRemove)
worker.Start(threadDevice)
' Flip flag
AddOrRemove = ListAction.ADD
End If
End Sub
Private Sub DoAdd(ByVal deviceContext As Object)
AddToDeviceList(deviceContext)
End Sub
Private Sub DoRemove(ByVal deviceContext As Object)
RemovefromDeviceList(deviceContext)
End Sub
Private Sub AddToDeviceList(ByVal deviceContext As Object)
If Repeater1.InvokeRequired Then
Repeater1.Invoke(New DeviceListActionDelegate(AddressOf AddToDeviceList), deviceContext)
Else
DeviceList.Add(deviceContext)
End If
End Sub
Private Sub RemovefromDeviceList(ByVal deviceContext As Object)
If Repeater1.InvokeRequired Then
Repeater1.Invoke(New DeviceListActionDelegate(AddressOf RemovefromDeviceList), deviceContext)
Else
DeviceList.Remove(DeviceList.Last)
End If
End Sub
End Class
Thanks 
-
Aug 23rd, 2013, 07:14 PM
#10
Re: Help getting started with DataRepeater
Woah... what's with the background worker that just do adds and deletes? What the heck? There isn't a reason for that. Especially since the only thing they do is then interact with the DR on the primary thread. It's like going out the front door, getting into your car, backing up, using the garage remote to open it up, plulling back in, going into the garage, get a hammer, back into the car, close garage door, then go back through your front door. You're creating a thread that then has to go back to the primary thread and update the UI.... why? Just update the UI in the first place... no need for the thread or the invoke.
-tg
-
Aug 24th, 2013, 09:57 AM
#11
Thread Starter
Hyperactive Member
Re: Help getting started with DataRepeater
Ah yes in this particular test case. However, I wanted to test how it would work if I was using multiple threads that also perform other work with access to the list as only part of that for some reason (as I actually am doing in my own application) then the list actions are effectively bound to the UI thread and so seem to require invocation - though if you have other ways of doing the same do let me know.
Sure if my threads aren't doing anything else then they are not needed and the actions can be done as one would normally expect
Wow, thanks for putting the time in with the dictionary!! I'll have a play around with your code now - With the copying to list idea, wouldn't that need to be done every time I made any change to the dictionary?
A little more background on my application and why I've threaded the list actions: The list item objects represent real devices that require a set of instructions to be run on them at certain times. EG. itemN device starts, some setting is sent to it over TCP and then runs for 2 hours then the setting changes and it runs for another 1.5 hours and so on. There may be many items running these processes at the same time.
The settings and times are stored in a database.
Rather than have a thread dedicated to each item, that could be doing nothing but timing for the majority of its lifetime of around 10 hours or so, I decided to have one thread handle all the timing (like a kind of scheduler) and then just run extra threads for only when they are required.
I call these setting/time pairs "program steps". Each item then needs to be added to the list when it is created, needs to have which step it is on (along with several other things) set and got by the threads handling DB and TCP whenever these extra work threads are created.
Then the final step is when the item is finished and so needs to be removed from the list. This is also run just like any other step, on a thread .
So yes, massive overkill for simple single-threaded CRUD list operations. But essential for any multiple-threaded application - whats more, it's not easily found out by those who dont already know what they're doing. I only started the thread here after I'd got quite frustrated trying to find VB information specific to binding lists rather than the most basic datatable binding.
Thanks 
-
Aug 25th, 2013, 07:30 AM
#12
Re: Help getting started with DataRepeater
"With the copying to list idea, wouldn't that need to be done every time I made any change to the dictionary?"
It doesn't actually copy it per se... but yes... I found numerous threads where people complained about the DR not autoupdating when items are added/removed for what ever reason. The common thing people found to work was to set the DR datasource to nothing then immediately re-set it back to the original datasource... no one seems to know why.
-tg
-
Aug 25th, 2013, 01:42 PM
#13
Thread Starter
Hyperactive Member
Re: Help getting started with DataRepeater
OK, Im only spit-balling here but I think resetting the DR works because it "rescans" the list and whatever new changes are on it when it is bound again.
This is needed because the normal List(Of T) doesn't have any events to notify the bound control (the DR) that the list has changed.
This is why I switched to the BindingList(Of T) as this does expose these events for the DR
Check out the second paragraph at http://www.dapfor.com/en/net-suite/n...fe-bindinglist for info on this. I didnt understand 95% of what is written on that page but I think thats basically the gist of what they are saying about the IBindingList.Listchanged event.
I'm assuming that changes to the DR do propogate to a normal List(Of T) because the DR does expose change events that notify the list. Thus with a normal list the Dr will notify the list but the list will not notify the DR.
With a BindingList that changes to a two-way street rather than a one way.
Now Im a bit more sure of my understanding I will post a tutorial in the code bank, save anyone else needing to trawl around for the same info.
My first tut so you'd be welcome to take a look at it and let me know any edits that could be made 
First post is up here: http://www.vbforums.com/showthread.p...ulti-threading
Last edited by wolf99; Aug 25th, 2013 at 02:44 PM.
Reason: Add post url
Thanks 
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|