sort results (find method) not working - vb.net

I am using ArcGIS. I am trying to sort the data manually after its found. I created a property class and loop through a Tlist to scrub unwanted data. However right before it binds to the data grid, I receive a cast error. I assume something is coming back null. Am I missing something??
Public Class temp
Public Sub New()
End Sub
Public Property DisplayFieldName As String
Public Property Feature As ESRI.ArcGIS.Client.Graphic
Public Property FoundFieldName As String
Public Property LayerId As Integer
Public Property LayerName As String
Public Property Value As Object
End Class
Public Class templst
Public Sub New()
Dim findresult = New List(Of temp)
End Sub
Private _findresult = findresult
Public Property findresult() As List(Of temp)
Get
Return _findresult
End Get
Set(ByVal value As List(Of temp))
_findresult = value
End Set
End Property
End Class
Private Sub FindTask_Complete(ByVal sender As Object, ByVal args As FindEventArgs)
Dim newargs As New templst() 'puts Tlist (temp) into property
Dim templistNUMONLY As New List(Of temp) 'Tlist of temp
For Each r In args.FindResults 'class in compiled dll.
If Regex.Match(r.Value.ToString, "[0-9]").Success Then
templistNUMONLY.Add(New temp() With {.LayerId = r.LayerId,
.LayerName = r.LayerName,
.Value = r.Value,
.FoundFieldName = r.FoundFieldName,
.DisplayFieldName = r.DisplayFieldName,
.Feature = r.Feature})
End If
Next
newargs.findresult = templistNUMONLY
Dim sortableView As New PagedCollectionView(newargs.findresult)
FindDetailsDataGrid.ItemsSource = sortableView 'populate lists here
End Sub
BIND TO GRID HERE: (Error here)
Private Sub FindDetails_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
' Highlight the graphic feature associated with the selected row
Dim dataGrid As DataGrid = TryCast(sender, DataGrid)
Dim selectedIndex As Integer = dataGrid.SelectedIndex
If selectedIndex > -1 Then
'''''''''''''''''CAST ERROR HERE:
Dim findResult As FindResult = CType(FindDetailsDataGrid.SelectedItem, FindResult)

You populate FindDetailsDataGrid with objects of type temp, but you try to cast it to type FindResult instead. You should do:
Dim selectedTemp As temp = CType(FindDetailsDataGrid.SelectedItem, temp)
'*Create find result from selected temp object here*

Related

How to check elements from an array in a CheckListBox

i have a checklisbox with some value, let's say
"Apple"
"Peach"
"Lemon"
These values came from a dataset.
I have an array with Apple and Lemon: {"Apple", "Lemon"}.
How to check in the checklistbox each value read in this array?
EDIT: In my case, the checklistbox was populate using a dataset provided by a SQL query
In the following code sample, data from SQL-Server (database doesn't matter but this is what I used, what is important is the container the data is loaded into is loaded into a list.
Container to hold data
Public Class Category
Public Property Id() As Integer
Public Property Name() As String
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Class to read data
Imports System.Data.SqlClient
Public Class SqlOperations
Private Shared ConnectionString As String =
"Data Source=.\SQLEXPRESS;Initial Catalog=NorthWind2020;Integrated Security=True"
Public Shared Function Categories() As List(Of Category)
Dim categoriesList = New List(Of Category)
Dim selectStatement = "SELECT CategoryID, CategoryName FROM Categories;"
Using cn As New SqlConnection With {.ConnectionString = ConnectionString}
Using cmd As New SqlCommand With {.Connection = cn}
cmd.CommandText = selectStatement
cn.Open()
Dim reader = cmd.ExecuteReader()
While reader.Read()
categoriesList.Add(New Category() With {.Id = reader.GetInt32(0), .Name = reader.GetString(1)})
End While
End Using
End Using
Return categoriesList
End Function
End Class
Extension method
Which can check or uncheck a value if found in the CheckedListBox and is case insensitive.
Public Module Extensions
<Runtime.CompilerServices.Extension>
Public Function SetCategory(sender As CheckedListBox, text As String, Optional checkedValue As Boolean = True) As Boolean
If String.IsNullOrWhiteSpace(text) Then
Return False
End If
Dim result = CType(sender.DataSource, List(Of Category)).
Select(Function(item, index) New With
{
Key .Column = item,
Key .Index = index
}).FirstOrDefault(Function(this) _
String.Equals(this.Column.Name, text, StringComparison.OrdinalIgnoreCase))
If result IsNot Nothing Then
sender.SetItemChecked(result.Index, checkedValue)
Return True
Else
Return False
End If
End Function
End Module
Form code
Public Class ExampleForm
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CheckedListBox1.DataSource = SqlOperations.Categories
End Sub
Private Sub CheckCategoryButton_Click(sender As Object, e As EventArgs) Handles CheckCategoryButton.Click
CheckedListBox1.SetCategory(CategoryToCheckTextBox.Text, StateCheckBox.Checked)
End Sub
End Class
To check all at once
Private Sub CheckAllButton_Click(sender As Object, e As EventArgs) Handles CheckAllButton.Click
CType(CheckedListBox1.DataSource, List(Of Category)).
ForEach(Sub(cat) CheckedListBox1.SetCategory(cat.Name, True))
End Sub
You haven't mentioned exactly how the CheckedListBox gets populated by the DataSet, I've made the assumption that you add Strings directly to the Items collection.
This code will simply loop through the CheckedListBox and compare the values with the array, whatever the match result, the Checkbox is either ticked or cleared.
Dim theArray() As String = {"Apple", "Lemon"}
For counter As Integer = 0 To CheckedListBox1.Items.Count - 1
Dim currentItem As String = CheckedListBox1.Items(counter).ToString
Dim match As Boolean = theArray.Contains(currentItem.ToString)
CheckedListBox1.SetItemChecked(counter, match)
Next
Use the SetItemChecked method like this:
CheckedListBox1.SetItemChecked(CheckedListBox1.Items.IndexOf("your item goes here"), True)
Note that if the item does not exist, an exception will be thrown, so be sure to check for the item before calling the SetItemChecked() method. To do that, you can check for the return value of IndexOf(). It will be -1 if the item does not exist.

Problem populating DataGridView with Structured Array

When I run my code, populating the array with data from a csv logfile, the DataGridView populates with empty rows.
I have tried just using a 2 Dimensional Array, I get the same results. I suspect I need to "map" the data in some fashion... maybe?
Here is what I am currently attempting:
This first section is in a Module..., the sub in the form code
Public Structure DataBlock
Public Data As String()
Public Property xData As String()
Get
Return Data
End Get
Set(ByVal value As String())
Data = value
End Set
End Property
End Structure
Public DataBlocks(1) As DataBlock
' end of module code
' start of form code
Public Sub test(path)
Dim i As Integer
LogData = IO.File.ReadAllLines(path)
ReDim DataBlocks(UBound(LogData))
For i = 0 To UBound(LogData)
DataBlocks(i) = New DataBlock With {.xData = Split(LogData(i), ",")}
Next
DataGridView1.DataSource = DataBlocks
End Sub
I was expecting the table to be populated I see other examples which work, but these do not use a structured array with an array in the structure. I could just break the array down in to 8 parts (that's my column count for the file), but I'm not willing to secede just yet.
Well, it's a bit kludgy but it works. I changed the structure to a class. It seems the DataGridView can not handle an array as a column type so I changed the array to a string for display.
Public Class DataBlock
Private Data As String()
Public ReadOnly Property xDataOut As String
Get
Return String.Join(", ", Data)
End Get
End Property
Public WriteOnly Property xDataIn As String()
Set(value As String())
Data = value
End Set
End Property
Public Sub New(sArray As String())
xDataIn = sArray
End Sub
End Class
Public DataBlocks As New List(Of DataBlock)
Public Sub test(path As String)
Dim Lines = IO.File.ReadAllLines(path)
For Each line In Lines
DataBlocks.Add(New DataBlock(line.Split(","c)))
Next
DataGridView1.DataSource = DataBlocks
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
test("LogData.txt")
End Sub

How to filter an object list based on an unknown property

I created a user control consisting of a text box and data grid.
(code not included on the sample below).
I need to filter the content of the data grid by a specific column.
The name of the column depends on the name of the object properties.
The object will vary, however each control will be bound to a specific object.
How I am using the control example:
Public Class Form1
Private ObjectList As List(Of Object)
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
ObjectList = GenerateRandomData()
AddHandler Me.MyUserControl.SelectionStatusChanged, AddressOf UpdateForm
AddHandler Me.MyUserControl.TextChanged, AddressOf UpdateList
Me.MyUserControl.DataSource = ObjectList
End Sub
Private Sub UpdateList()
Dim FilteredList As IEnumerable(Of Object) = From obj As Item In ObjectList
Where obj.Prop_1.StartsWith(Me.MyUserControl.Text)
Select obj
Me.MyUserControl.DataSource = FilteredList.ToList
End Sub
Private Sub UpdateForm()
If Me.MyUserControl.HasItemLoaded Then
Dim NewItem As Item = CType(Me.MyUserControl.SelectedItem, Item)
TextBox1.Text = NewItem.Prop_1
TextBox2.Text = NewItem.Prop_2
TextBox3.Text = NewItem.Prop_3
Return
End If
TextBox1.Text = ""
TextBox2.Text = ""
TextBox3.Text = ""
End Sub
Private Function GenerateRandomData() As List(Of Object)
Randomize()
Dim lst As New List(Of Object)
For i = 0 To 10000
Dim itm As New Item
Dim value As Integer = CInt(Int((999999 * Rnd()) + 1))
Dim value2 As Integer = CInt(Int((999999 * Rnd()) + 1))
itm.Prop_1 = value
itm.Prop_2 = "abc " & value & value2
itm.Prop_3 = value2 & value & " abc"
lst.Add(itm)
Next
Return lst
End Function
End Class
I would like to handle the work of UpdateList() inside the User control (move the code or equivalent result) but the problem is that the control is unaware of the object type so I cannot directly call Prop_1 or whatever is the name of the property from inside the user control.
So far I am accomplishing this by listening for events outside the user control but I will like to remove as much responsibility from the programmer.
Any ideas are welcome.
The following is designed around its SetList method that is used to set the source list (IEnumerable(Of T)) and the name of the property to filter on in the UpDateList method. It use Reflection to retrieve the desired property.
Public Class UserControl1
Private list As IEnumerable
Private filterPropInfo As Reflection.PropertyInfo
Public Sub SetList(Of T)(list As IEnumerable(Of T), filterPropertyName As String)
filterPropInfo = GetType(T).GetProperty(filterPropertyName, GetType(String))
If filterPropInfo Is Nothing Then
Throw New ArgumentException(String.Format("{0} is not a Public String Property on Type: {1}", filterPropertyName, GetType(T).FullName))
End If
Me.list = list
End Sub
Public Sub UpdateList()
Dim FilteredList As IEnumerable(Of Object) = From obj In list
Where CStr(filterPropInfo.GetValue(obj)).StartsWith(Me.Text)
Select obj
Me.DataGridView1.DataSource = FilteredList.ToList
End Sub
End Class
Example usage by calling the test method.
Public Class Form1
Sub test()
Dim l As New List(Of Item)
l.Add(New Item("fred"))
l.Add(New Item("freddy"))
l.Add(New Item("fredrick"))
l.Add(New Item("joe"))
UserControl11.[Text] = "fred"
UserControl11.SetList(l, "Field1")
UserControl11.UpdateList()
End Sub
End Class
Class Item
Public Property Field1 As String
Public Property Field2 As Int32
Public Sub New(f1 As String)
Me.Field1 = f1
End Sub
End Class

Extracting property values from a dictionary

I am attempting to write a subroutine that will deserialize a dictionary from a .ser file (this bit works fine) and then repopulate several lists from this dictionary (this is the bit I cannot do).
The dictionary contains objects (I think) of a custom class I wrote called "Photo Job" which has properties such as ETA, notes, medium etc. (Declared as such)
Dim photoJobs As New Dictionary(Of String, PhotoJob)
In short, I want to be able to extract every entry of each specific property into an separate arrays (one for each property) and I can go from there.
Any help would be appreciated, I may be going about this completely the wrong way, I'm new to VB. The relevant code is below:
Photo Job Class:
<Serializable()> _Public Class PhotoJob
Private intStage As Integer 'Declare all local private variables
Private ID As String
Private timeLeft As Integer
Private material As String '
Private note As String
Private path As String
Private finished As Boolean = False
'Declare and define properties and methods of the class
Public Property productionStage() As Integer
Get
Return intStage
End Get
Set(ByVal Value As Integer)
intStage = Value
End Set
End Property
Public Property photoID() As String
Get
Return ID
End Get
Set(ByVal Value As String)
ID = Value
End Set
End Property
Public Property ETA() As Integer
Get
Return timeLeft
End Get
Set(ByVal Value As Integer)
timeLeft = Value
End Set
End Property
Public Property medium() As String
Get
Return material
End Get
Set(ByVal Value As String)
material = Value
End Set
End Property
Public Property notes() As String
Get
Return note
End Get
Set(ByVal Value As String)
note = Value
End Set
End Property
Public Property imagePath() As String
Get
Return path
End Get
Set(ByVal Value As String)
path = Value
End Set
End Property
Public Property complete() As Boolean
Get
Return finished
End Get
Set(value As Boolean)
finished = value
End Set
End Property
Public Sub nextStage()
If intStage < 4 Then
intStage += 1
ElseIf intStage = 4 Then
intStage += 1
finished = True
End If
End Sub
End Class
Subroutines involved in de/serialisation:
Private Sub BackupAllToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles BackupAllToolStripMenuItem.Click
Dim formatter As New BinaryFormatter
Dim backupFile As New FileStream(Strings.Replace(Strings.Replace(Now, ":", "_"), "/", ".") & ".ser", FileMode.Create, FileAccess.Write, FileShare.None)
formatter.Serialize(backupFile, photoJobs)
backupFile.Close()
MsgBox("Collection saved to file")
End Sub
Private Sub RestoreFromFileToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles RestoreFromFileToolStripMenuItem.Click
With OpenFileDialog 'Executes the following sets/gets/methods of the OpenFileDialog
.FileName = ""
.Title = "Open Image File"
.InitialDirectory = "c:\"
.Filter = "Serial Files(*.ser)|*ser"
.ShowDialog()
End With
Dim backupPathStr As String = OpenFileDialog.FileName
Dim deSerializer As New BinaryFormatter
Dim backupFile As New FileStream(backupPathStr, FileMode.Open)
photoJobs = deSerializer.Deserialize(backupFile)
backupFile.Close()
End Sub
From what I can see using the autos menu, the saving/restoring of the dictionary works just fine.
First, if you are using VS2010+, you can greatly reduce boilerplate code using autoimplemented properties:
<Serializable()>
Public Class PhotoJob
Public Property productionStage() As Integer
Public Property photoID() As String
Public Property ETA() As Integer
etc
End Class
That is all that is needed, all the boilerplate code is handled for you. Second, with this line:
photoJobs = deSerializer.Deserialize(backupFile)
Your deserialized photojobs will be a generic Object, not a Dictionary. You should turn on Option Strict so VS will enforce these kinds of errors. This is how to deserialize to Type:
Using fs As New FileStream(myFileName, FileMode.Open)
Dim bf As New BinaryFormatter
PhotoJobs= CType(bf.Deserialize(fs), Dictionary(Of String, PhotoJob))
End Using
Using closes and disposes of the stream, CType converts the Object returned by BF to an actual dictionary
To work with the Dictionary (this has nothing to do with Serialization) you need to iterate the collection to get at the data:
For Each kvp As KeyValuePair(Of String, PhotoJob) In PhotoJobs
listbox1.items.Add(kvp.value.productionStage)
listbox2.items.Add(kvp.value.ETA)
etc
Next
The collection is a made of (String, PhotoJob) pairs as in your declaration, and when you add them to the collection. They comeback the same way. kvp.Key will be the string key used to identify this job in the Dictionary, kvp.Value will be a reference to a PhotoJobs object.
As long as VS/VB knows it is a Dictionary(of String, PhotoJob), kvp.Value will act like an instance of PhotoJob (which it is).

How to assign class property as display data member in datagridview

I am trying to display my data in datagridview. I created a class with different property and used its list as the datasource. it worked fine. but I got confused how to do that in case we have nested class.
My Classes are as follows:
class Category
property UIN as integer
property Name as string
end class
class item
property uin as integer
property name as string
property mycategory as category
end class
my data list as follows:
dim myDataList as list(of Item) = new List(of Item)
myDataList.Add(new Item(1,"item1",new category(1,"cat1")))
myDataList.Add(new Item(2,"item2",new category(1,"cat1")))
myDataList.Add(new Item(3,"item3",new category(1,"cat1")))
myDataList.Add(new Item(4,"item4",new category(2,"cat2")))
myDataList.Add(new Item(5,"item5",new category(2,"cat2")))
myDataList.Add(new Item(6,"item6",new category(2,"cat2")))
Now I binded the datagridview control like:
DGVMain.AutoGenerateColumns = False
DGVMain.ColumnCount = 3
DGVMain.Columns(0).DataPropertyName = "UIN"
DGVMain.Columns(0).HeaderText = "ID"
DGVMain.Columns(1).DataPropertyName = "Name"
DGVMain.Columns(1).HeaderText = "Name"
DGVMain.Columns(2).DataPropertyName = "" **'here i want my category name**
DGVMain.Columns(2).HeaderText = "category"
DGVMain.datasource = myDataList
DGVMain.refresh()
I have tried using mycategory.name but it didn't worked. What can be done to get expected result? Is there any better idea other than this to accomplish the same task?
Edited My question as per comment:
I have checked the link given by u. It was nice n very usefull. Since the code was in c# i tried to convert it in vb. Everything went good but failed at a point of case sensitive and next one is that i had my nested class name itemcategory and my property name was category. there it arouse the problem. it didn't searched for category but it searched for itemcategory. so confused on it. My Code as follows:
Private Sub DGVMain_CellFormatting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellFormattingEventArgs) Handles DGVMain.CellFormatting
Dim DGVMain As DataGridView = CType(sender, DataGridView)
e.Value = EvaluateValue(DGVMain.Rows(e.RowIndex).DataBoundItem, DGVMain.Columns(e.ColumnIndex).DataPropertyName)
End Sub
Private Function EvaluateValue(ByRef myObj As Object, ByRef myProp As String) As String
Dim Ret As String = ""
Dim Props As System.Reflection.PropertyInfo()
Dim PropA As System.Reflection.PropertyInfo
Dim ObjA As Object
If myProp.Contains(".") Then
myProp = myProp.Substring(0, myProp.IndexOf("."))
Props = myObj.GetType().GetProperties()
For Each PropA In Props
ObjA = PropA.GetValue(myObj, New Object() {})
If ObjA.GetType().Name = myProp Then
Ret = EvaluateValue(ObjA, myProp.Substring(myProp.IndexOf(".") + 1))
Exit For
End If
Next
Else
PropA = myObj.GetType().GetProperty(myProp)
Ret = PropA.GetValue(myObj, New Object() {}).ToString()
End If
Return Ret
End Function
I'd just add a property to item called CategoryName that returns Category.Name and use that.
The Category is part of the Item, but that doesn't mean that you always have to give access to the whole Category to outside classes, just give access to the bits that they need access to at that time as to maximize encapsulation and minimize interdependencies.
Edit: In response to your comment
If you don't want to create the properties as mentioned in my answer above I don't think there is any real solution but you can use the 'CellFormatting' event to sort of get it to work where you set the DataPropertyName to a special identifier and then in the CellFormatting event handler you look up the real value to display. You can find an example of that here, look for tkrasinger's post (or AlexHinton's if you want to use reflection).
Public Class Category
Dim uni As Integer
Dim name As String
Public Sub New(ByVal i As Integer, ByVal n As String)
Me.UIN = i
Me.name = n
End Sub
Public Sub New()
End Sub
Property UIN() As Integer
Get
Return uni
End Get
Set(ByVal value As Integer)
uni = value
End Set
End Property
Property Names() As String
Get
Return Me.name
End Get
Set(ByVal value As String)
Me.name = value
End Set
End Property
**Public Overrides Function ToString() As String
Return name.ToString()
End Function**
End Class
Public Class item
Dim uni As Integer
Dim name As String
Dim category As New Category()
Property UIN() As Integer
Get
Return uni
End Get
Set(ByVal value As Integer)
uni = value
End Set
End Property
Property Names() As String
Get
Return Me.name
End Get
Set(ByVal value As String)
Me.name = value
End Set
End Property
Property mycategory() As Category
Get
Return Me.category
End Get
Set(ByVal value As Category)
Me.category = value
End Set
End Property
Public Sub New(ByVal i As Integer, ByVal nm As String, ByVal ct As Category)
Me.UIN = i
Me.Names = nm
Me.mycategory = ct
End Sub
End Class
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim myDataList As List(Of item) = New List(Of item)
myDataList.Add(New item(1, "item1", New Category(1, "cat1")))
myDataList.Add(New item(2, "item2", New Category(1, "cat1")))
myDataList.Add(New item(3, "item3", New Category(1, "cat1")))
myDataList.Add(New item(4, "item4", New Category(2, "cat2")))
myDataList.Add(New item(5, "item5", New Category(2, "cat2")))
myDataList.Add(New item(6, "item6", New Category(2, "cat2")))
DGVMain.AutoGenerateColumns = False
DGVMain.ColumnCount = 3
DGVMain.Columns(0).DataPropertyName = "UIN"
DGVMain.Columns(0).HeaderText = "ID"
DGVMain.Columns(1).DataPropertyName = "Names"
DGVMain.Columns(1).HeaderText = "Name"
**DGVMain.Columns(2).DataPropertyName = "mycategory"**
DGVMain.Columns(2).HeaderText = "Category"
DGVMain.datasource = myDataList
DGVMain.refresh()
End Sub