Working with bindinglist - vb.net

I have a datagridview and a bindinglist. They work together pretty ok, but I want to make the properties appear in the rows, not on the column. Is there any way to achieve that ?
My code for anyone who is interested.
Public Class Form1
Dim listaBindingSource As New BindingList(Of pessoa)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim b1 As New pessoa()
listaBindingSource.Add(b1)
dgv.DataSource = listaBindingSource
End Sub
End Class
Public Class pessoa
Dim sells_Month1 As String
Public Sub New() 'ByVal nome_fora As String)
sells_Month1 = "0"
End Sub
Property vendas1 As String
Get
Return sells_Month1
End Get
Set(value As String)
sells_Month1 = value
End Set
End Property
The other properties are vendas2, vendas3.. and are the same as this one.
Edit:
I´m kind of lost here. What I want is to make the values of the properties of my objects appear on some kind of data visualizer. When I add new objects on the list, they appear on this data visualizer and when I change the values of the cells there, the values of the properties change. Anyone has a good sugestion ? Apparentely dgv is not the way to go.
Thanks,
Ricardo S.

I want to make the properties appear in the rows´ headers, not on
the column.
I'm afraid this is not possible, there is no built-in solution for that in DataGidView. You can display the properties in columns only.
To control the text displayed in the column header, try to set the DisplayName attribut:
<System.ComponentModel.DisplayName("DisplayedText")>
Property vendas1 As String
Get
Return sells_Month1
End Get
Set(value As String)
sells_Month1 = value
End Set
End Property
Or if you import System.ComponentModel namespace.
<DisplayName("DisplayedText")>
Property vendas1 As String
Get
Return sells_Month1
End Get
Set(value As String)
sells_Month1 = value
End Set
End Property

Related

VB.NET Forms ListBox doesn't display DataSource

I'm trying to link my ListBox to an ObservableCollection.
Here's my class for defining mods:
Public Class TroveMod
Private m_FileName As String
Private m_Enabled As Boolean
Public Property FileName() As String
Get
Return m_FileName
End Get
Set(value As String)
m_FileName = value
End Set
End Property
Public Property Enabled() As Boolean
Get
Return m_Enabled
End Get
Set(value As Boolean)
m_Enabled = value
End Set
End Property
Public ReadOnly Property ModName()
Get
Return Path.GetFileNameWithoutExtension(FileName)
End Get
End Property
End Class
And this is the actual Property ModList:
Public Property ModList() As ObservableCollection(Of TroveMod)
Get
Return m_ModList
End Get
Set(value As ObservableCollection(Of TroveMod))
m_ModList = value
End Set
End Property
I add items using:
Private Sub AddMod(file__1 As String, enabled As Boolean)
If File.Exists(file__1) Then
ModList.Add(New TroveMod() With { _
.FileName = file__1, _
.Enabled = enabled _
})
End If
End Sub
Everytime I want to add something to this Collection using AddMod, it won't show off in my listbox :/ I added an ModListBindingSource to the ListBox and set the DisplayMember and ValueMember to ModName but it still won't work. I also got a status label, which says, that it successful added the mods to the collection but it simply won't show them in the ListBox. Did I miss something?
An ObservableCollection is not quite what you want - this would allow your code to see changes to the collection via events. For instance, if there were several actors adding items to the collection and you wanted to know that. In this case, since 'local' code is adding items, it is not needed.
It is also not clear how the collection is mapped to the ListBox as the DataSource. Try this:
Public Class Form...
Private myList As New BindingList(Of TroveMod)
Sub Form_Load(....
theListBox.DataSource = myList
theListBox.DisplayMember = "ModName"
Now, as you add things to the BindingList they will appear in theListBox. If an item changes though (e.g. the Name), that change will not show up without a little more work, but as these are file names that seems unlikely.
The form property is not needed unless an external class also needs access to the collection/BindingList; in that case, you probably do not want need a setter.

DataGridView Auto-calculate cells In Vb

I want to multiply two columns of a datagridview and show the product in Column 3 of the same datagridview.
Example
Column1 - Column2 - Column3
12 2 24
15 2 30
Here is my Code
Private Sub Table1DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles Table1DataGridView1.CellValidated
Try
Dim iCell1 As Integer
Dim icell2 As Integer
Dim icellResult As Integer
iCell1 = Table1DataGridView1.CurrentRow.Cells(1).Value
icell2 = Table1DataGridView1.CurrentRow.Cells(2).Value
If String.IsNullOrEmpty(iCell1) OrElse String.IsNullOrEmpty(icell2) Then Exit Sub
If Not IsNumeric(iCell1) OrElse Not IsNumeric(icell2) Then Exit Sub
icellResult = CDbl(iCell1) * CDbl(icell2)
Table1DataGridView1.CurrentRow.Cells(3).Value = icellResult
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
It works but a new row is added afterwards. So please help.
An elegant way is to use Databinding (the bread and butter method with datagridviews):
We create a new class, I call MultiplyPair. This class basically has to changable properties, Value1 and Value2.
It also has a third, readonly property called Product.
Whenever Value1 or Value2 changes, we notify any bindingsource that also the product has changed (by implementing INotifyPropertyChanged).
This has several advantages:
You avoid many quirks of the DGV that may arise during manual edits
It's a great way to learn to use databinding (I'm just beginning myself to use the concept thoroughly and it's amazing)
It's easily adaptable: Want also the product, sum and quotient? Just add properties and simple notifications.
You have a clear structure, so other people can understand your code.
You can reuse the whole logic, just bind another datagridview to the same bindinglist and you can display and change the information at multiple locations, with hardly any code.
To use the class we create a Bindinglist(Of MultiplyPair) and bind this list to the datagridview. Then we just ass values to the list and the datagridview is populated automatically. You can even change the values in the Datagridview and the product is automatically updated.
Public Class Form1
Private WithEvents Values As New System.ComponentModel.BindingList(Of MultiplyPair) 'This holds one object for every line in the DGV
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Values = New System.ComponentModel.BindingList(Of MultiplyPair) 'Initialize the list
DataGridView1.AllowUserToAddRows = False 'Disallow new rows, as per your question
DataGridView1.DataSource = Values 'Bind the list to the datagridview
'Add two example lines
Values.Add(New MultiplyPair With {.Value1 = 2, .Value2 = 12})
Values.Add(New MultiplyPair With {.Value1 = 15, .Value2 = 2})
End Sub
End Class
Public Class MultiplyPair
Implements System.ComponentModel.INotifyPropertyChanged
'The first value
Private _Value1 As Double
Public Property Value1
Get
Return _Value1
End Get
Set(value)
_Value1 = value
'Notify not only that Value1 has changed, but also that the Product has changed
Notify("Value1")
Notify("Product")
End Set
End Property
'Same as above
Private _Value2 As Double
Public Property Value2
Get
Return _Value2
End Get
Set(value)
_Value2 = value
Notify("Value2")
Notify("Product")
End Set
End Property
'This will show the product of Value1 and Value2 whenever asked
Public ReadOnly Property Product
Get
Return Value1 * Value2
End Get
End Property
'Helper sub to raise the PropertyChanged event with the given Propertyname
Private Sub Notify(name As String)
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(name))
End Sub
'INotifyPropertyChanged implementation
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Edit: To avoid additional rows, set the AllowUsersToAddRows property of the Datagridview to false.

DataBinding a Combobox to custom object fails to show data

I'm having a problem with databinding an object to a combobox in VB.NET (VS2008/.NET 3.5). Please take a look at this simplified version of my code:
Friend Class clDocument
Private _items as New List(Of clDocumentItems)
<System.ComponentModel.DisplayName("Items")> _
<System.ComponentModel.Bindable(True)> _
Public Property Items() As List(Of clDocumentItems)
Get
Return _items
End Get
Set(ByVal value As List(Of clDocumentItems))
_items = value
RaiseEvent ItemsChanged(Me, New EventArgs)
End Set
End Property
Public Event ItemsChanged As EventHandler
End Class
Friend Class clDocumentItems
Private _uid as String = ""
Private _docnumber as String = ""
<System.ComponentModel.DisplayName("UID")> _
<System.ComponentModel.Bindable(True)> _
Public Property UID() As String
Get
Return _uid
End Get
Set(ByVal value As String)
_uid = value
RaiseEvent UIDChanged(Me, New EventArgs)
End Set
End Property
<System.ComponentModel.DisplayName("Document")> _
<System.ComponentModel.Bindable(True)> _
Public Property DocNumber() As String
Get
Return _docnumber
End Get
Set(ByVal value As String)
_docnumber = value
RaiseEvent DocNumberChanged(Me, New EventArgs)
End Set
End Property
Public Event UIDChanged As EventHandler
Public Event DocNumberChanged As EventHandler
End Class
Somewhere else, we got this code:
Private Sub cmd_go_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_go.Click
'Try to load the object with data, this works well
Dim _document as New clDocument
_document.Load(somevalue)
cmb_docs.DataSource = Nothing
cmb_docs.Items.Clear()
If _document.UID = "" Then Exit Sub 'Object wasn't loaded so get out
'Create the binding.
cmb_docs.ValueMember = "UID"
cmb_docs.DisplayMember = "DocNumber"
cmb_docs.DataSource = _document.Items
End Sub
Now, the problem is that the ComboBox is populated with as many items as there are objects in _document.Items, but it doesn't passes real data -The combobox is filled with "Namespace.clDocumentItems" strings. Please note that similar code works perfectly when bound to regular class properties (String, integer, etc).
Now, I can guess by using the debugger's reflection that it is because the Datsource is receiving a List of objects instead of fields, but then again I don't know how to avoid that without having to create another Array or List with just those values and passing THAT to the Datasource property...
I explored the site for something similar, the only thing that came close was this question that went unanswered 3 years ago, so I hope to have better luck today ;)
Thanks for your time!
EDIT: Below I add the code I used for databinding to a DataGridView, as requested in the comments.
Private WithEvents _bs as New BindingSource
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
DataGridView1.AutoGenerateColumns = False
Dim column As DataGridViewColumn = New DataGridViewTextBoxColumn()
column.DataPropertyName = "Document"
column.Name = "colDoc"
DataGridView1.Columns.Add(column)
_bs.DataSource = _document.Items
Me.DataGridView1.DataSource = _bs
End Sub
There is actually nothing at all wrong with your code as you have it written above (I copied it into a new WinForms app, supplied some default values, and it worked correctly).
There are two possible reasons that your implementation may not be working:
If there is a typo in DisplayMember.
If the access level of the DisplayMember property prevents the
combobox from accessing it.
If the property cannot be found or accessed, .Net drops back to the default implementation of using the object's ToString method. So one quick and dirty fix would be to override the ToString method of clDocument and return DocNumber. That should resolve the display issue, but not the underlying cause of the problem, which will require a little more research.
ok, The problem was the comboBox was faulty. Removing the control from the form and adding it again solved the problem. I accept the answer because it contains useful information.
Thanks

Datagridview bound to object - Making the new rows appear blank

I have a Datagridview that changes its content according to a selection the user makes in a listBox.
The DGV consits of 2 comboboxes (Country, Product) and 1 textbox (Quantity).
I've created a class combined of 3 integers.
This class is used as a type of list, which is the datasource for the DGV.
There is also another list containing the prior list, so I have a list of datasources.
The DGV's datasource is a BindingSource that changes whenever the SelectedIndex of the listBox is fired.
My problem occurs whenever a new row is added to the DGV:
I use the BindingSource.AddNew which calls the constructor of the class, but it must assign values to each item in the class. That way, whenever I click any cell in the DGV I don't get a blank row.
Moreover, when the BS changes and then returned, another row is added.
What I want to get is a blank row - empty comboboxes and textbox.
Thanks for your help!
The class:
Public Class PoList
Private _CountryID As Integer
Private _ProductID As Integer
Private _Quantity As Integer
Public Sub New(ByVal CountryID As Integer, ByVal ProductID As Integer, ByVal Quantity As Integer)
_CountryID = CountryID
_ProductID = ProductID
_Quantity = Quantity
End Sub
Private Sub New()
_CountryID = 1
_ProductID = 2
_Quantity = Nothing
End Sub
Public Property CountryID() As Integer
Get
Return _CountryID
End Get
Set(ByVal value As Integer)
_CountryID = value
End Set
End Property
Public Property ProductID() As Integer
Get
Return _ProductID
End Get
Set(ByVal value As Integer)
_ProductID = value
End Set
End Property
Public Property Quantity() As Integer
Get
Return _Quantity
End Get
Set(ByVal value As Integer)
_Quantity = value
End Set
End Property
Public Shared Function CreateNewPoList() As PoList
Return New PoList
End Function
End Class
Private Sub List_AddRow(ByVal sender As Object, ByVal e As AddingNewEventArgs) Handles AllListBindingSource.AddingNew
e.NewObject = PoList.CreateNewPoList
End Sub
Creating a new inner list:
AllList.Add(New List(Of PoList))
AllListBindingSource.AddNew()
AllListBindingSource.DataSource = AllList(TableCounter)
AddPoDetails.DataSource = AllListBindingSource
SelectedIndexChanged event:
AllListBindingSource.DataSource = AllList(AddPoList.SelectedIndex)
AddPoDetails.DataSource = Nothing
AddPoDetails.DataSource = AllListBindingSource
Right, lets see if I can help you.
As I interpret it you have a list filled with lists. These lists don't know their own identity and is based on the current index in the list.
First of I wouldn't use Bindingsource.AddNew I would add the new object straight to the list instead.
AllList(TableCounter).Add(New Polist())
This way you know exactly how many objects has been created, by using events you aren't quite sure are you.
To refresh the list do this:
AllListBindingSource.ResetBindings(true)
Which will update your DGV with the new line.
Now you need to restructure your class since when you create a new Polist you set a value to nothing. This will crash your table. What you need to do is this:
Private _Quantity As String
Public Property Quantity() As String
Get
Return _Quantity
End Get
Set(ByVal value As String)
_Quantity = value
End Set
End Property
Using a string is the only way for you to get a blank textbox, I would recommend you to have 0 as default if you are using Quantity as an integer (which you should). Your constructor needs to be changed to this:
Private Sub New()
_CountryID = 0
_ProductID = 0
_Quantity = ""
End Sub
In your combobox columns you have to add a blank item in the top (I'm guessing your adding them manually), should be possible by having a blank row in the top of the items.

How can I make a list in a Repeater Section?

I need to create a repeater section that will show 4 columns - First Name, Last Name, a link based off of stored column data that says.
All the data plus some extra not being used is in a players profile. How do I link the data on the code-behind to the repeater control with the databinders?
I am using visual studio 2008, VB.NET for the code behind.
Have you considered using a DataGrid instead of a repeater?
Here's a bit of a breakdown on when to use each.
http://msdn.microsoft.com/en-us/library/aa479015.aspx
To more directly answer your question you'll need to set the Repeater's DataSource property to a DataView or an ArrayList. As such:
Sub Page_Load(Sender As Object, e As EventArgs)
If Not IsPostBack Then
Dim values As New ArrayList()
values.Add(New PositionData("Microsoft", "Msft"))
values.Add(New PositionData("Intel", "Intc"))
values.Add(New PositionData("Dell", "Dell"))
Repeater1.DataSource = values
Repeater1.DataBind()
Repeater2.DataSource = values
Repeater2.DataBind()
End If
End Sub
Public Class PositionData
Private myName As String
Private myTicker As String
Public Sub New(newName As String, newTicker As String)
Me.myName = newName
Me.myTicker = newTicker
End Sub
Public ReadOnly Property Name() As String
Get
Return myName
End Get
End Property
Public ReadOnly Property Ticker() As String
Get
Return myTicker
End Get
End Property
End Class