Other way to get/set Visual Basic ObservableCollection property - wpf

Id like to know if there are other way to set/get value of an observableCollection Property. Please see below example
Let say i have
Public Class Record
public Property Field1 as string
public Property Field2 as string
End Class
Public RecordCollection as new ObservableCollection(Of Record)
Public sRecord as Record
Public Sub TestRecord()
sRecord = new Record
sRecord.Field1 = "test"
sRecord.Field2 = "test2"
RecordCollection.add(sRecord)
listview1.ItemsSource = RecordCollection
End Sub
My Question is How can i set or gets Record properties like below example?
Public Sub TestRecord()
sRecord = new Record
sRecord("Field1") = "test"
sRecord("Field2") = "test2"
End Sub
or is there any other way to this?

Here example of using System.Reflection for your case
Testing class
Public Class Record
Public Property Field1 As String
Public Property Field2 As String
End Class
Example
Dim data As New DataTable()
data.Columns.Add("Field1", GetType(String))
data.Columns.Add("Field2", GetType(String))
Dim dr As DataRow
dr = data.NewRow()
dr.SetField("Field1", "0")
dr.SetField("Field2", "Zero")
data.Rows.Add(dr)
dr = data.NewRow()
dr.SetField("Field1", "1")
dr.SetField("Field2", "One")
data.Rows.Add(dr)
For Each row As DataRow In data.Rows
Dim temp As New Record()
For Each prop As PropertyInfo In GetType(Record).GetProperties()
If data.Columns.Contains(prop.Name) = True Then
prop.SetValue(temp, row(prop.Name))
End If
Next
Console.WriteLine($"{temp.Field1}, {temp.Field2}")
Next
Will print result in the console:
0, Zero
1, One
But again, consider of using Entity Framework or some other ORM(object-relational mapper) framework.

You can use the Default Property functionality in the Record class:
Public Class Record
Private fields As New Dictionary(Of String, String)
Public Property Field1 As String
Get
Return fields("Field1")
End Get
Set(value As String)
fields("Field1") = value
End Set
End Property
Public Property Field2 As String
'Same code as Field1 but with "Field2" of course
Default Public Property Item(field As String)
Get
Return fields(field)
End Get
Set(value)
fields(field) = value
End Set
End Property
End Class
Access is then possible like:
Dim record As New Record
record("Field1") = "Hello Default Property!"
record.Field2 = "Hello Field2 !"
Console.WriteLine(record("Field1"))

Related

VBNet: List of items inside a class, I cant change the value for a single item

My Problem:
I have a Class and a list of other class inside:
Public Class Signal_Type_Read
Private c_signal_count As Integer = 0 ' counter for read signals
Private _items As List(Of Signal_Item)
Private item As New Signal_Item
Sub add_sig()
c_signal_count += 1
items.Add(item)
End Sub
Public Property items() As List(Of Signal_Item)
Get
Return _items
End Get
Set(value As List(Of Signal_Item))
_items = value
End Set
End Property
Function item_counter() As Integer
item_counter = c_signal_count
End Function
Public Sub New()
_items = New List(Of Signal_Item)
End Sub
End Class
Public Class Signal_Item
Private _original_name As String
Public Property Original_name() As String
Get
Return _original_name
End Get
Set(value As String)
_original_name = value
End Set
End Property
'Many other properties
End Class
My problem is when I use in a loop
Public Shared ReadSignals As New Signal_Type_Read
//Part of a Loop to read cells values and store in the variable
ReadSignals.add_sig()
Dim c_index As Integer = ReadSignals.item_counter - 1
ReadSignals.items.item(c_index).Original_name = c_row.Cells(e_Signame).Value
It always changes the "Original_name" Property in all items of my Variable. Where is my error? I want only that oe item is changed.
I found the cause of the problem... I need to create a new instane of item in my ADD_sig() sub
Sub add_sig()
Dim s_item As New Signal_Item
c_signal_count += 1
items.Add(s_item)
End Sub

Linking IDataErrorInfo to an existing data model class in a seperate class

I have an existing data model class that cannot be changed, in this example it is named customer. I now need to add data handling using IDataErrorInfo in a different class but cannot seem to work out how to join the 2 classes. What am I doing wrong?
Public Class Customer
Implements INotifyPropertyChanged
Private _firstName As String
Public Property FirstName As String
Get
Return _firstName
End Get
Set(value As String)
_firstName = value
OnPropertyChanged("FirstName")
End Set
End Property
My Error handler looks like this:
Public Class CustomerValidation
Inherits Customer
Implements INotifyPropertyChanged, IDataErrorInfo
Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
Get
Dim errorMessage As String = String.Empty
Select Case columnName
Case "FirstName"
If Customer IsNot Nothing AndAlso String.IsNullOrEmpty(Customer.FirstName) Then
errorMessage = "First Name Must Be At Least 1 Character"
End If
End Select
Return errorMessage
End Get
End Property
Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
Get
Return String.Empty
End Get
End Property
I then declare and bind to it like this:
Private _customer As CustomerValidation = New CustomerValidation
Public Sub New()
InitializeComponent()
_customer.FirstName = "Steve"
Me.DataContext = _customer

Create table dynamically in WPF (VB.Net)

I want to create an editable table and it size is defined by user (size can be 10*10 or 20*20 or 30*30).
--
I found this topic (here) but it's running in WinForms, and the DataGridView is not supported by WPF.
I tried with a DataGrid, but the following row doesn't working :
Me.DataGridTableau.ItemsSource = dt
--
I tried with a RadGridView (Telerik) but rows are only updatable by ItemsSource property, and like I don't know how many columns will be, I can't create an object which represents the table (x properties for x columns).
Can anybody help me?
You can set the ItemsSource of a DataGrid to any IEnumerable, including a DataView of a DataTable:
Me.DataGridTableau.ItemsSource = dt.DefaultView
If anybody need it, I found a solution using a RadGridView (Telerik) :
Create this class :
Imports System.Dynamic
Imports System.Collections.Generic
Imports System.ComponentModel
Public Class MyDataRow
Inherits DynamicObject
Implements INotifyPropertyChanged
ReadOnly data As IDictionary(Of String, Object)
Public Sub New()
data = New Dictionary(Of String, Object)()
End Sub
Public Sub New(ByVal source As IDictionary(Of String, Object))
data = source
End Sub
Public Overrides Function GetDynamicMemberNames() As IEnumerable(Of String)
Return data.Keys
End Function
Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder, ByRef result As Object) As Boolean
result = Me(binder.Name)
Return True
End Function
Public Overrides Function TrySetMember(ByVal binder As SetMemberBinder, ByVal value As Object) As Boolean
Me(binder.Name) = value
Return True
End Function
Default Public Property Item(ByVal columnName As String) As Object
Get
If data.ContainsKey(columnName) Then
Return data(columnName)
End If
Return Nothing
End Get
Set(ByVal value As Object)
If Not data.ContainsKey(columnName) Then
data.Add(columnName, value)
OnPropertyChanged(columnName)
Else
If data(columnName) <> value Then
data(columnName) = value
OnPropertyChanged(columnName)
End If
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(name As String)
Try
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
Catch
If Debugger.IsAttached Then Debugger.Break()
Throw ' rethrow exception
End Try
End Sub
Protected Sub OnPropertyChanged(event_args As PropertyChangedEventArgs)
RaiseEvent PropertyChanged(Me, event_args)
End Sub
End Class
In your VM, you need a public property :
Private _tableau As ObservableCollection(Of MyDataRow)
Public Property Tableau() As ObservableCollection(Of MyDataRow)
Get
Return _tableau
End Get
Set(ByVal value As ObservableCollection(Of MyDataRow))
_tableau = value
OnPropertyChanged("Tableau")
End Set
End Property
You need to create a method to load your table :
Private Function LoadTableau() As ObservableCollection(Of MyDataRow)
Dim taille As Integer = Me.GetTailleTableau()
If taille = 0 Then Return Nothing
Dim data As New ObservableCollection(Of MyDataRow)()
For i As Integer = 0 To (taille - 1)
Dim row = New MyDataRow()
For j As Integer = 0 To (taille - 1)
'row(String.Format("Column{0}", j)) = String.Format("Cell {0} {1}", i, j)
row(j) = ""
Next
data.Add(row)
Next
Return data
End Function
You need to load your table :
Me.Tableau = Me.LoadTableau()
And you need to bind your table :
<telerik:RadGridView x:Name="RadGridViewTableau" ItemsSource="{Binding Tableau}" >
I hope this help :)

WPF avoid adding a duplicate row

I'm using vb.net framework 4.5 and WPF project.
I have a button, the function adds a certain product info to a datagrid. In my vb code file I set a product class
Public Class MyProduct
Public Property ItemNumber As String
Public Property ItemDescription As String
Public Property ItemUnitPrice As Double
Public Property ItemQty As Integer
End Class
The button touchdown event
Private Sub Button_TouchDown(sender As Object, e As TouchEventArgs)
Dim dmb As New MyProduct
dmb.ItemNumber = "abc001"
dmb.ItemDescription = "bla bla bla"
dmb.ItemQty = 1
dmb.ItemUnitPrice = 123.45
MyDataGrid.Items.Add(dmb)
End Sub
Currently, if I tap multiple times of this button, the data grid will add multiple duplicated rows for same product. My goal is when multiple same product add to datagrid, only one row shows and each additional tap/click action on the same button will only increase the ItemQty number.
How can I do that? Thanks!
First, you need to prevent inserting twice :
Private Sub buttonAdd_Click(sender As Object, e As RoutedEventArgs) Handles buttonAdd.Click
Dim dmb As New MyProduct
dmb.ItemNumber = New Random().Next(5).ToString()
dmb.ItemDescription = "bla bla bla"
dmb.ItemQty = 1
dmb.ItemUnitPrice = 123.45
Dim dmbSearched As MyProduct = Nothing
For Each dmbs As MyProduct In MyDataGrid.Items
If dmbs.ItemNumber = dmb.ItemNumber Then
dmbSearched = dmbs
Exit For
End If
Next
If dmbSearched Is Nothing Then
MyDataGrid.Items.Add(dmb)
Else
dmbSearched.ItemQty += 1
End If
End Sub
Second the MyProduct class must raise an event when the quantity is changed, otherwise there is no visible change :
Public Class MyProduct : Implements INotifyPropertyChanged
Private Property m_ItemQty As Integer
Public Property ItemQty As Integer
Get
Return m_ItemQty
End Get
Set(value As Integer)
m_ItemQty = value
FirePropertyChanged()
End Set
End Property
Public Sub FirePropertyChanged(<CallerMemberName> Optional propName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
End Sub
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Public Property ItemNumber As String
Public Property ItemDescription As String
Public Property ItemUnitPrice As Double
End Class
Regards

How to create a new instance of a class when converting to it as a type vb.net

I have a Serializable class called SettingsForProgram this class contains a list of string called ServerList
I am using this class to save settings for myprogram (username , password , colors , etc..) but when i try to save a list the same way then add -or get- items from it i get object reference not set to instance of object so how can i create a new instance of the class when converting it
To understand what i mean here are some codes :
The class :
<Serializable()>
Public Class SettingsForProgram
Private Namev As String = ""
Private pwv As String = ""
Public LocationsList As New List(Of String)
Private Savev As New Boolean()
Public Property LoginName As String
Get
Return Namev
End Get
Set(value As String)
Namev = value
End Set
End Property
Public Property LoginPassword As String
Get
Return pwv
End Get
Set(value As String)
pwv = value
End Set
End Property
Public Property SaveLogin As Boolean
Get
Return Savev
End Get
Set(value As Boolean)
Savev = value
End Set
End Property
Public Sub New()
LocationsList = New List(Of String)
End Sub
End Class
To load settings:(where i want to initialize the new instance of the class)
public MySettings as new SettingsForProgram
Public Sub LoadSettings()
Dim formatter As New BinaryFormatter()
Dim data As Byte() = File.ReadAllBytes(savepath)
Dim ms As New MemoryStream(data)
MySettings = CType(formatter.Deserialize(ms), SettingsForProgram)
End Sub
To save settings :
Public Sub SaveSettings()
Dim bf As New BinaryFormatter()
Dim ms As New MemoryStream()
If MySettings.LoginName = Nothing Then
MySettings.LoginName = "name"
ElseIf MySettings.LoginPassword = Nothing Then
MySettings.LoginPassword = "password"
End If
bf.Serialize(ms, MySettings)
Dim mySaveState As Byte() = ms.ToArray()
File.WriteAllBytes(savepath, mySaveState)
End Sub
I made a quick test like this
button 1 : save
MySettings.LocationsList.AddRange({"test1", "test2", "test3"}) <<<< where i get the error
SaveSettings()
button 2 : load
LoadSettings()
MsgBox(MySettings.LocationsList(1))
thanks to #Steve i now know the problem
, the solution is to do like i did with name and password saving ,
just added this to the save Settings
If MySettings.LocationsList Is Nothing Then
MySettings.LocationsList = New List(Of String)
MySettings.LocationsList.Add("Location 1")
End If
and every thing worked
final code
Public Sub SaveSettings()
Dim bf As New BinaryFormatter()
Dim ms As New MemoryStream()
If MySettings.LoginName = Nothing Then
MySettings.LoginName = "name"
ElseIf MySettings.LoginPassword = Nothing Then
MySettings.LoginPassword = "password"
End If
If MySettings.LocationsList Is Nothing Then
MySettings.LocationsList = New List(Of String)
MySettings.LocationsList.Add("Location 1")
End If
bf.Serialize(ms, MySettings)
Dim mySaveState As Byte() = ms.ToArray()
File.WriteAllBytes(savepath, mySaveState)
End Sub

Resources