The function retrieveFixtureReport() returns a list of Report, each of which contains a PlayerID. I'm trying to set my labels to reflect the selected Report chosen from cmbSelectedPlayer. Each Report within the list contains a unique PlayerID. I've tried a variety of different ways to access the properties of the selected Report, including LINQ, but have been unsuccessful so far. A For Each loop doesn't seem to be the right option to select just one Report, it also prevents me from selecting other PlayerID's from cmbSelectedPlayer (only the last Report in the list is shown). The code is shown below:
Public Sub setFixtureReport()
'If UC_Menu_Scout1.cmbSelectedPlayer.SelectedItem IsNot Nothing Then
If UC_Menu_Scout1.cmbSelectedPlayer.Items.Count > 0 Then
Dim getPlayerReport = _
(From rpt As Report In retrieveFixtureReport() _
Where rpt.PlayerID = UC_Menu_Scout1.cmbSelectedPlayer.SelectedItem.PlayerID) '.AsEnumerable
'For Each rpt As Report In getPlayerReport()
'For Each rpt As Report In retrieveFixtureReport.Where(Function(x) x.PlayerID = UC_Menu_Scout1.cmbSelectedPlayer.SelectedItem.PlayerID)
'Dim rpt As Report = getPlayerReport 'retrieveFixtureReport(0)
'*****General Information
UC_Menu_Scout1.lblRptPosition.Text = rpt.PositionPlayed
UC_Menu_Scout1.lblFoot.Text = rpt.PreferredFoot
UC_Menu_Scout1.txtComments.Text = rpt.Comments
UC_Menu_Scout1.lblStatus.Text = rpt.MonitorStatus
'Next
setColours()
'End If
End If
End Sub
The combobox cmbSelectedPlayer is filled from playerList (this is added to in retrieveFixxtureReport as well):
UC_Menu_Scout1.cmbSelectedPlayer.DataSource = playerList
UC_Menu_Scout1.cmbSelectedPlayer.DisplayMember = "PlayerFullName"
UC_Menu_Scout1.cmbSelectedPlayer.ValueMember = "PlayerID"
Any suggestions would be appreciated.
If your combobox was bound to a List(Of Player) then all you would need to do is cast the SelectedItem to it's type:
Dim player = TryCast(UC_Menu_Scout1.cmbSelectedPlayer.SelectedItem, Player)
If Not player Is Nothing Then
Dim getPlayerReport = (From rpt As Report In retrieveFixtureReport()
Where rpt.PlayerID = player.PlayerID).FirstOrDefault()
If Not getPlayerReport Is Nothing Then
UC_Menu_Scout1.lblRptPosition.Text = getPlayerReport.PositionPlayed
UC_Menu_Scout1.lblFoot.Text = getPlayerReport.PreferredFoot
UC_Menu_Scout1.txtComments.Text = getPlayerReport.Comments
UC_Menu_Scout1.lblStatus.Text = getPlayerReport.MonitorStatus
End If
End If
Related
I'm trying to find a way to automate data entry into the raise invoice screen in Sage 50.
All of our order data is held in a different system and we could easily pull together the line items, customer data, etc. automatically but our accounts team currently have to manually select each row, enter the SKU and quantity which is very time consuming.
It appears that the clipboard isn't functional in the Product Code field either - which is really annoying!
Are there any reasonable ways to inject data like this into Sage 50?
as far as i know there is a Excel2Sage Tool or App which can handle mass-importing. i did not used the commercial software last year, but the year before.
i'm actual not know about a free solution for this without developing it.
As alternative you could use AutoIt or something.
best
Eric
Here is an example using VB.Net:
'Declare objects
Dim oSDO As SageDataObject230.SDOEngine
Dim oWS As SageDataObject230.WorkSpace
Dim oSOPRecord As SageDataObject230.SopRecord
Dim oSOPItem_Read As SageDataObject230.SopItem
Dim oSOPItem_Write As SageDataObject230.SopItem
Dim oSOPPost As SageDataObject230.SopPost
Dim oStockRecord As SageDataObject230.StockRecord
'Declare Variables
Dim szDataPath As String
'Create SDO Engine Object
oSDO = New SageDataObject230.SDOEngine
' Select company. The SelectCompany method takes the program install
' folder as a parameter
szDataPath = oSDO.SelectCompany("C:\Documents and Settings\All Users\Application Data\Sage\Accounts\2017\")
'Create Workspace
oWS = oSDO.Workspaces.Add("Example")
'Try to connect
If oWS.Connect(szDataPath, "manager", "", "Example") Then
'Create objects
oSOPRecord = oWS.CreateObject("SOPRecord")
oSOPPost = oWS.CreateObject("SOPPost")
oSOPItem_Read = oWS.CreateObject("SOPItem")
oStockRecord = oWS.CreateObject("StockRecord")
'Read an existing Sales Order
oSOPRecord.MoveLast()
'Populate the order header, copying fields from oSOPRecord to oSOPPost
oSOPPost.Header("INVOICE_NUMBER").Value = oSOPRecord.Fields.Item("INVOICE_NUMBER").Value
oSOPPost.Header("ACCOUNT_REF").Value = CStr(oSOPRecord.Fields.Item("ACCOUNT_REF").Value)
oSOPPost.Header("NAME").Value = CStr(oSOPRecord.Fields.Item("NAME").Value)
oSOPPost.Header("ADDRESS_1").Value = CStr(oSOPRecord.Fields.Item("ADDRESS_1").Value)
oSOPPost.Header("ADDRESS_2").Value = CStr(oSOPRecord.Fields.Item("ADDRESS_2").Value)
oSOPPost.Header("ADDRESS_3").Value = CStr(oSOPRecord.Fields.Item("ADDRESS_3").Value)
oSOPPost.Header("ADDRESS_4").Value = CStr(oSOPRecord.Fields.Item("ADDRESS_4").Value)
oSOPPost.Header("ADDRESS_5").Value = CStr(oSOPRecord.Fields.Item("ADDRESS_5").Value)
oSOPPost.Header("DEL_ADDRESS_1").Value = CStr(oSOPRecord.Fields.Item("DEL_ADDRESS_1").Value)
oSOPPost.Header("DEL_ADDRESS_2").Value = CStr(oSOPRecord.Fields.Item("DEL_ADDRESS_2").Value)
oSOPPost.Header("DEL_ADDRESS_3").Value = CStr(oSOPRecord.Fields.Item("DEL_ADDRESS_3").Value)
oSOPPost.Header("DEL_ADDRESS_4").Value = CStr(oSOPRecord.Fields.Item("DEL_ADDRESS_4").Value)
oSOPPost.Header("DEL_ADDRESS_5").Value = CStr(oSOPRecord.Fields.Item("DEL_ADDRESS_5").Value)
oSOPPost.Header("CUST_TEL_NUMBER").Value = CStr(oSOPRecord.Fields.Item("CUST_TEL_NUMBER").Value)
oSOPPost.Header("CONTACT_NAME").Value = CStr(oSOPRecord.Fields.Item("CONTACT_NAME").Value)
oSOPPost.Header("GLOBAL_TAX_CODE").Value = CShort(oSOPRecord.Fields.Item("GLOBAL_TAX_CODE").Value)
oSOPPost.Header("ORDER_DATE").Value = CDate(oSOPRecord.Fields.Item("ORDER_DATE").Value)
oSOPPost.Header("NOTES_1").Value = CStr(oSOPRecord.Fields.Item("NOTES_1").Value)
oSOPPost.Header("NOTES_2").Value = CStr(oSOPRecord.Fields.Item("NOTES_1").Value)
oSOPPost.Header("NOTES_3").Value = CStr(oSOPRecord.Fields.Item("NOTES_3").Value)
oSOPPost.Header("TAKEN_BY").Value = CStr(oSOPRecord.Fields.Item("TAKEN_BY").Value)
oSOPPost.Header("ORDER_NUMBER").Value = CStr(oSOPRecord.Fields.Item("ORDER_NUMBER").Value)
oSOPPost.Header("CUST_ORDER_NUMBER").Value = CStr(oSOPRecord.Fields.Item("CUST_ORDER_NUMBER").Value)
oSOPPost.Header("PAYMENT_REF").Value = CStr(oSOPRecord.Fields.Item("PAYMENT_REF").Value)
oSOPPost.Header("GLOBAL_NOM_CODE").Value = CStr(oSOPRecord.Fields.Item("GLOBAL_NOM_CODE").Value)
oSOPPost.Header("GLOBAL_DETAILS").Value = CStr(oSOPRecord.Fields.Item("GLOBAL_DETAILS").Value)
oSOPPost.Header("ORDER_TYPE").Value = oSOPRecord.Fields.Item("ORDER_TYPE").Value
oSOPPost.Header("FOREIGN_RATE").Value = CDbl(oSOPRecord.Fields.Item("FOREIGN_RATE").Value)
oSOPPost.Header("CURRENCY").Value = oSOPRecord.Fields.Item("CURRENCY").Value
oSOPPost.Header("CURRENCY_USED").Value = oSOPRecord.Fields.Item("CURRENCY_USED").Value
' Link header to items
oSOPItem_Read = oSOPRecord.Link
'Find the First Record
oSOPItem_Read.MoveFirst()
Do
'Add the existing items to the order
oSOPItem_Write = oSOPPost.Items.Add
'Populate the Fields, copying the data from the existing records
oSOPItem_Write.Fields.Item("STOCK_CODE").Value = CStr(oSOPItem_Read.Fields.Item("STOCK_CODE").Value)
oSOPItem_Write.Fields.Item("DESCRIPTION").Value = CStr(oSOPItem_Read.Fields.Item("DESCRIPTION").Value)
oSOPItem_Write.Fields.Item("NOMINAL_CODE").Value = CStr(oSOPItem_Read.Fields.Item("NOMINAL_CODE").Value)
oSOPItem_Write.Fields.Item("TAX_CODE").Value = CShort(oSOPItem_Read.Fields.Item("TAX_CODE").Value)
oSOPItem_Write.Fields.Item("QTY_ORDER").Value = CDbl(oSOPItem_Read.Fields.Item("QTY_ORDER").Value)
oSOPItem_Write.Fields.Item("UNIT_PRICE").Value = CDbl(oSOPItem_Read.Fields.Item("UNIT_PRICE").Value)
oSOPItem_Write.Fields.Item("NET_AMOUNT").Value = CDbl(oSOPItem_Read.Fields.Item("NET_AMOUNT").Value)
oSOPItem_Write.Fields.Item("TAX_AMOUNT").Value = CDbl(oSOPItem_Read.Fields.Item("TAX_AMOUNT").Value)
oSOPItem_Write.Fields.Item("COMMENT_1").Value = CStr(oSOPItem_Read.Fields.Item("COMMENT_1").Value)
oSOPItem_Write.Fields.Item("COMMENT_2").Value = CStr(oSOPItem_Read.Fields.Item("COMMENT_2").Value)
oSOPItem_Write.Fields.Item("UNIT_OF_SALE").Value = CStr(oSOPItem_Read.Fields.Item("UNIT_OF_SALE").Value)
oSOPItem_Write.Fields.Item("FULL_NET_AMOUNT").Value = CDbl(oSOPItem_Read.Fields.Item("FULL_NET_AMOUNT").Value)
oSOPItem_Write.Fields.Item("TAX_RATE").Value = CDbl(oSOPItem_Read.Fields.Item("TAX_RATE").Value)
'We now need to ensure that the TAX_FLAG is set the same as the item being read otherwise it will be re calculated
oSOPItem_Write.Fields.Item("TAX_FLAG").Value = CInt(oSOPItem_Read.Fields.Item("TAX_FLAG").Value)
'Loop until there are no more existing items
Loop Until oSOPItem_Read.MoveNext = False
'destroy the oSOPItem_Write object
oSOPItem_Write = Nothing
'write a new item
oStockRecord.MoveLast()
oSOPItem_Write = oSOPPost.Items.Add
' Populate other fields required for Invoice Item
' From 2015 the update method now wraps internal business logic
' that calculates the vat amount if a net amount is given.
' If you wish to calculate your own Tax values you will need
' to ensure that you set the TAX_FLAG to 1 and set the TAX_AMOUNT value on the item line
' ***Note if a NVD is set the item line values will be recalculated
' regardless of the Tax_Flag being set to 1***
oSOPItem_Write.Fields.Item("STOCK_CODE").Value = oStockRecord.Fields.Item("STOCK_CODE").Value
oSOPItem_Write.Fields.Item("DESCRIPTION").Value = CStr(oStockRecord.Fields.Item("DESCRIPTION").Value)
oSOPItem_Write.Fields.Item("NOMINAL_CODE").Value = CStr(oStockRecord.Fields.Item("NOMINAL_CODE").Value)
oSOPItem_Write.Fields.Item("TAX_CODE").Value = CShort(oStockRecord.Fields.Item("TAX_CODE").Value)
oSOPItem_Write.Fields.Item("QTY_ORDER").Value = CDbl(2)
oSOPItem_Write.Fields.Item("UNIT_PRICE").Value = CDbl(50)
oSOPItem_Write.Fields.Item("NET_AMOUNT").Value = CDbl(100)
oSOPItem_Write.Fields.Item("FULL_NET_AMOUNT").Value = CDbl(100)
oSOPItem_Write.Fields.Item("COMMENT_1").Value = CStr("")
oSOPItem_Write.Fields.Item("COMMENT_2").Value = CStr("")
oSOPItem_Write.Fields.Item("UNIT_OF_SALE").Value = CStr("")
oSOPItem_Write.Fields.Item("TAX_RATE").Value = CDbl(20)
'Destroy the oSOPItem_Write object
oSOPItem_Write = Nothing
'Post the order
If oSOPPost.Update() Then
MsgBox("Order Updated Successfully")
Else
MsgBox("Order Update Failed")
End If
'Disconnect and destroy the objects
oWS.Disconnect()
oSDO = Nothing
oWS = Nothing
oSOPRecord = Nothing
oSOPItem_Read = Nothing
oSOPItem_Write = Nothing
oSOPPost = Nothing
oStockRecord = Nothing
End If
Exit Sub
All of the current commercial products will require you to put your data into a specific format (column order and file type) anyway, so if you can do that, then bring everything into Excel, and then adapt the code listed above for VB.Net into VBA. It's fairly straightforward, mainly passing data to an array and then looping through.
If you want specific assistance, show us the structure of your Order data, and then we can do something
Cheers
Paul
Update 5/21/17. Thank you for the suggestion of using a Table. That was helpful. I actually figured it out. I made myinputable a global variable by declaring the Dim statement at the top and making it a Datagridview type. Now I can turn it off in the other event that I needed to do it.
I am a novice. I have created a Datagridview in VB 2015 to capture a bunch of data from the use. When the user is finished with the data entry, I want to store the cell values in my variables. I do not know how to capture any event from my dynamically created datagridview "myinputable." My code is below. Please help.
Private Sub inputmodel()
Dim prompt As String
Dim k As Integer
'
' first get the problem title and the number of objectives and alternatives
'
prompt = "Enter problem title: "
title = InputBox(prompt)
prompt = "Enter number of criteria: "
nobj = InputBox(prompt)
prompt = "Enter number of alternatives: "
nalt = InputBox(prompt)
'
' now create the table
'
Dim Myinputable As New DataGridView
Dim combocol As New DataGridViewComboBoxColumn
combocol.Items.AddRange("Increasing", "Decreaing", "Threashold")
For k = 1 To 6
If k <> 2 Then
Dim nc As New DataGridViewTextBoxColumn
nc.Name = ""
Myinputable.Columns.Add(nc)
Else
Myinputable.Columns.AddRange(combocol)
End If
Next k
' now add the rows and place the spreadsheet on the form
Myinputable.Rows.Add(nobj - 1)
Myinputable.Location = New Point(25, 50)
Myinputable.AutoSize = True
Me.Controls.Add(Myinputable)
FlowLayoutPanel1.Visible = True
Myinputable.Columns(0).Name = "Name"
Myinputable.Columns(0).HeaderText = "Name"
Myinputable.Columns(1).Name = "Type"
Myinputable.Columns(1).HeaderText = "Type"
Myinputable.Columns(2).Name = "LThresh"
Myinputable.Columns(2).HeaderText = "Lower Threshold"
'Myinputable.Columns(2).ValueType = co
Myinputable.Columns(3).Name = "UThresh"
Myinputable.Columns(3).HeaderText = "Upper Threshold"
Myinputable.Columns(4).Name = "ABMin"
Myinputable.Columns(4).HeaderText = "Abs. Minimum"
Myinputable.Columns(5).Name = "ABMax"
Myinputable.Columns(5).HeaderText = "Abs. Maximum "
Myinputable.Rows(0).Cells(0).Value = "Help"
If Myinputable.Capture = True Then
MsgBox(" damn ")
End If
End Sub
As #Plutonix suggests, you should start by creating a DataTable. Add columns of the appropriate types to the DataTable and then bind it to the grid, i.e. assign it to the DataSource of your grid, e.g.
Dim table As New DataTable
With table.Columns
.Add("ID", GetType(Integer))
.Add("Name", GetType(String))
End With
DataGridView1.DataSource = table
That will automatically add the appropriate columns to the grid if it doesn't already have any, or you can add the columns in the designer and set their DataPropertyName to tell them which DataColumn to bind to. As the user makes changes in the grid, the data will be automatically pushed to the underlying DataTable.
When you're done, you can access the data via the DataTable and even save the lot to a database with a single call to the Update method of a data adapter if you wish.
I tried reading other posted questions about this error but I am still having trouble understanding it.
I was having errors after renaming variables and posted this question, I use option explicit and debug my code and it fixed my naming problems, But I still am having one problem in that 2 out of my 21 comboboxes in the operations row are still getting this same error. How can they get an error when they are using the same functions as the other 19. I checked their names and they are correct how could this be??
I have a form that I have build that is set up to look and function similar to and excel spreadsheet. This is the layout, Column Names first and then Control type
Operation Time/Min DecTime LaborRate Cost MarkUp Total
ComboBox TextBox Label Label Label TextBox Label
So all together I have 7 columns with 21 rows (not including column names) in which I can input data. Any where there is a label there is no user input its value is simply derived from the other fields.
I used the following naming convention for my form controls cb_op1, cb_op2, cb_op3 ... I just increment it by one to mimic the row number (control name plus row number). Operation = cb_op, Time = tb_time, DecTime = tb_Dectime, LaborRate = tb_LbrRate, Cost = tb_Cost, MarkUp = tb_MU, Total = tb_Total.
I wrote functions for the AfterUpdate events for my controls. This first function gets the value of the control based on its row number and then allows me to assign it to a variable this fuction is used for multiple controls update events.
Private Function GetControlValue(name As String) As Object
Dim obj As Object
For Each obj In Me.Controls
If obj.name = name Then
Set GetControlValue = obj
Exit Function
End If
Next
End Function
The next function is used to actually update the other field in the row based on the after update event. I use the GetControlValue Function listed above in this function. This is just for the operations column.
Private Function OperationsUpdate(RowNumber As Integer)
Dim Operations As Object
Dim Time As Object
Dim DecTime As Object
Dim LaborRate As Object
Set Operations = GetControlValue("cb_op" & RowNumber)
Set Time = GetControlValue("tb_time" & RowNumber)
Set DecTime = GetControlValue("tb_DecTime" & RowNumber)
Set LaborRate = GetControlValue("tb_LbrRate" & RowNumber)
Set Cost = GetControlValue("tb_Cost" & RowNumber)
If IsNull(Time.Value) Then
If IsNull(Operation.Value) Then
Cost.Value = ""
LaborRate.Value = ""
Else
LaborRate.Value = DLookup("LaborRate", "OperationsType", "[operationsID] = " & Operation.Value)
End If
Else
If IsNull(Operation.Value) Then
Cost.Value = ""
LaborRate.Value = ""
Else
LaborRate.Value = DLookup("LaborRate", "OperationsType", "[operationsID] = " & Operation.Value)
Cost.Value = DecTime.Value * LaborRate.Value
End If
End If
FormObjectsUpdate2 (RowNumber)
Set Operations = Nothing
Set Time = Nothing
Set DecTime = Nothing
Set LaborRate = Nothing
Set Cost = Nothing
End Function
Finally this is how my After update events look per control. I post just the first three not all 21 to save space.
Private Sub cb_op1_AfterUpdate()
OperationsUpdate (1)
End Sub
Private Sub cb_op2_AfterUpdate()
OperationsUpdate (2)
End Sub
Private Sub cb_op3_AfterUpdate()
OperationsUpdate (3)
End Sub
I just posted the function and the code for the first column operations all the other after update events are now having the same issues too.
I have a Part class with the fields list in the code below. I have a DataGridView control, which I am filtering with the Advanced DGV (ADGV) DLL from NUGET. I must include the ADGV in my winform. I currently have a DataGridView, a search box on the form, and a button to run the following function. I need to go through all of the visible rows, collect a unique list of part numbers with their most recent revisions, and then color the rows in DataGridView which are out of date by checking the part number and rev on each row against the mostuptodate list. For 45,000 entries displayed in DataGridView, this take ~17 secs. For ~50 entries, it take ~1.2 seconds. This is extremely inefficient, but I can't see a way to cut the time down.
Sub highlightOutdatedParts()
'Purpose: use the results in the datagridview control, find the most recent revision of each part, and
' highlight all outdated parts relative to their respective most recent revisions
'SORT BY PART NUMBER AND THEN BY REV
If resultsGrid.ColumnCount = 0 Or resultsGrid.RowCount = 0 Then Exit Sub
Dim stopwatch As New Stopwatch
stopwatch.Start()
resultsGrid.Sort(resultsGrid.Columns("PartNumber"), ListSortDirection.Ascending)
Dim iBag As New ConcurrentBag(Of Part)
Dim sortedList As Generic.List(Of Part)
For Each row As DataGridViewRow In resultsGrid.Rows
If row.Visible = True Then
Dim iPart As New Part()
Try
iPart.Row = row.Cells(0).Value
iPart.Workbook = CStr(row.Cells(1).Value)
iPart.Worksheet = CStr(row.Cells(2).Value)
iPart.Product = CStr(row.Cells(3).Value)
iPart.PartNumber = CStr(row.Cells(4).Value)
iPart.ItemNo = CStr(row.Cells(5).Value)
iPart.Rev = CStr(row.Cells(6).Value)
iPart.Description = CStr(row.Cells(7).Value)
iPart.Units = CStr(row.Cells(8).Value)
iPart.Type = CStr(row.Cells(9).Value)
iPart.PurchCtgy = CStr(row.Cells(10).Value)
iPart.Qty = CDbl(row.Cells(11).Value)
iPart.TtlPerProd = CDbl(row.Cells(12).Value)
iPart.Hierarchy = CStr(row.Cells(13).Value)
iBag.Add(iPart)
Catch ice As InvalidCastException
Catch nre As NullReferenceException
End Try
End If
Next
sortedList = (From c In iBag Order By c.PartNumber, c.Rev).ToList() ' sort and convert to list
Dim mostUTDRevList As New Generic.List(Of Part) ' list of most up to date parts, by Rev letter
For sl As Integer = sortedList.Count - 1 To 0 Step -1 'start at end of list and work to beginning
Dim query = From entry In mostUTDRevList ' check if part number already exists in most up to date list
Where entry.PartNumber = sortedList(sl).PartNumber
Select entry
If query.Count = 0 Then ' if this part does not already exist in the list, add.
mostUTDRevList.Add(sortedList(sl))
End If
Next
'HIGHLIGHT DATAGRIDVIEW ROWS WHERE PART NUMBERS ARE OUT OF DATE
For Each row As DataGridViewRow In resultsGrid.Rows
' if that part with that Rev does not exist in the list, it must be out of date
Try
Dim rowPN As String = CStr(row.Cells(4).Value).ToUpper ' get part number
Dim rowR As String = CStr(row.Cells(6).Value).ToUpper ' get Rev
Dim query = From entry In mostUTDRevList ' check if that part number with that Rev is in the list.
Where entry.PartNumber.ToUpper.Equals(rowPN) AndAlso
entry.Rev.ToUpper.Equals(rowR)
Select entry
If query.Count = 0 Then ' if the part is out of date highlight its' row
row.DefaultCellStyle.BackColor = Color.Chocolate
End If
Catch ex As NullReferenceException
Catch ice As InvalidCastException
End Try
Next
resultsGrid.Select()
stopwatch.Stop()
If Not BackgroundWorker1.IsBusy() Then timertextbox.Text = stopwatch.Elapsed.TotalSeconds.ToString & " secs"
MessageBox.Show("Highlighting completed successfully.")
End Sub
It is almost always faster to work with the data than the control. The control is simply the means to present a view of the data (in a grid) to the users. Working with the data from there requires too much converting to be effieicent. Then, use the DGV events to highlight the rows
Its hard to tell all the details of what you are doing, but it looks like you are comparing the data to itself (as opposed to some concrete table where the lastest revision codes are defined). Nor is it clear why the datasources are collections, ConcurrentBags etc. The key would be to use collections optimized for the job.
To demonstrate, I have a table with 75,000 rows; the product codes are randomly selected from a pool of 25,000 and a revision code is a random integer (1-9). After the DGV datasource is built (a DataTable) a LookUp is created from the ProductCode-Revision pair. This is done once and once only:
' form level declaration
Private PRCodes As ILookup(Of String, Int32)
' go thru table
' group by the product code
' create an anon Name-Value object for each,
' storing the code and highest rev number
' convert result to a LookUp
PRCodes = dtSample.AsEnumerable.
GroupBy(Function(g) g.Item("ProductCode"),
Function(key, values) New With {.Name = key.ToString(), .Value = values.
Max(Of Int32)(Function(j) j.Field(Of Int32)("RevCode"))
}).
ToLookup(Of String, Int32)(Function(k) k.Name, Function(v) v.Value)
Elapsed time via stopwatch: 81 milliseconds to create the collection of 23731 items. The code uses an anonymous type to store a Max Revision code for each product code. A concrete class could also be used. If you're worried about mixed casing, use .ToLowerInvariant() when creating the LookUp (not ToUpper -- see What's Wrong With Turkey?) and again later when looking up the max rev.
Then rather than looping thru the DGV rows use the RowPrePaint event:
If e.RowIndex = -1 Then Return
If dgv1.Rows(e.RowIndex).IsNewRow Then Return
' .ToLowerInvariant() if the casing can vary row to row
Dim pc = dgv1.Rows(e.RowIndex).Cells("ProductCode").Value.ToString()
Dim rv = Convert.ToInt32(dgv1.Rows(e.RowIndex).Cells("RevCode").Value)
Dim item = PRCodes(pc)(0)
If item > rv Then
dgv1.Rows(e.RowIndex).DefaultCellStyle.BackColor = Color.MistyRose
End If
Notes
It takes some time to create the DataSource, but 75,000 rows is a lot to throw at a user
The time to create the LookUp is minimal - barely measurable
There is no noticeable wait in displaying them because a) the LookUp is made for this sort of thing, b) rows are done as needed when they are displayed. Row # 19,999 may never be processed if the user never scrolls that far.
This is all geared to just color a row. If you needed to save the Current/NotCurrent state for each row, add a Boolean column to the DataTable and loop on that. The column can be invisible if to hide it from the user.
The random data results in 47,000 out of date RevCodes. Processing 75k rows in the DataTable to set the flag takes 591 milliseconds. You would want to do this before you set the DataTable as the DataSource to prevent changes to the data resulting in various events in the control.
In general, the time to harvest the max RevCode flag and even tag the out of date rows is a trivial increment to creating the datasource.
The Result:
The data view is sorted by ProductCode so that the coloring of lower RevCodes is apparent.
We surely cant grok all the details and constraints of the system from a small snippet - even the data types and original datasource are a guess for us. However, this should provide some help with better look-up methods, and the concept of working with the data rather than the user's view.
One thing is the revision code - yours is treating them as a string. If this is alphanumeric, it may well not compare correctly - "9" sorts/compares higher than "834" or "1JW".
See also:
Lookup(Of TKey, TElement) Class
Anonymous Types
The solution was spurred in part by #Plutonix.
Sub highlightOutdatedParts()
If resultsGrid.ColumnCount = 0 Or resultsGrid.RowCount = 0 Then Exit Sub
Dim stopwatch As New Stopwatch
stopwatch.Start()
resultsGrid.DataSource.DefaultView.Sort = "PartNumber ASC, Rev DESC"
resultsGrid.Update()
'HIGHLIGHT DATAGRIDVIEW ROWS WHERE PART NUMBERS ARE OUT OF DATE
Dim irow As Long = 0
Do While irow <= resultsGrid.RowCount - 2
' if that part with that Rev does not exist in the list, it must be out of date
Dim utdPN As String = resultsGrid.Rows(irow).Cells(4).Value.ToString().ToUpper()
Dim utdRev As String = resultsGrid.Rows(irow).Cells(6).Value.ToString().ToUpper()
Dim iirow As Long = irow + 1
'If iirow > resultsGrid.RowCount - 1 Then Exit Do
Dim activePN As String = Nothing
Dim activeRev As String = Nothing
Try
activePN = resultsGrid.Rows(iirow).Cells(4).Value.ToString().ToUpper()
activeRev = resultsGrid.Rows(iirow).Cells(6).Value.ToString().ToUpper()
Catch ex As NullReferenceException
End Try
Do While activePN = utdPN
If iirow > resultsGrid.RowCount - 1 Then Exit Do
If activeRev <> utdRev Then
resultsGrid.Rows(iirow).DefaultCellStyle.BackColor = Color.Chocolate
End If
iirow += 1
Try
activePN = resultsGrid.Rows(iirow).Cells(4).Value.ToString().ToUpper()
activeRev = resultsGrid.Rows(iirow).Cells(6).Value.ToString().ToUpper()
Catch nre As NullReferenceException
Catch aoore As ArgumentOutOfRangeException
End Try
Loop
irow = iirow
Loop
resultsGrid.Select()
stopwatch.Stop()
If Not BackgroundWorker1.IsBusy() Then
timertextbox.Text = stopwatch.Elapsed.TotalSeconds.ToString & " secs"
resultcounttextbox.Text = resultsGrid.RowCount - 1 & " results"
End If
MessageBox.Show("Highlighting completed successfully.")
End Sub
I've checked for hours but I can't seem to find anything to help.
I want to loop through tables and columns from a dataset and use the column name in a combobox.items.add() line, however the eventual goal is to fill the combobox from the dataset itself possibly in a combobox.datasource line.
The first problem is that I can't get the code correct to setup the combobox control where it allows me to use .items.add("") and in extension .datasource
Error Message = "Object reference not set to an instance of an object"
dstcopt is the dataset from a oledbDataAdapter .fill(dstcopt,"table") line (which returns correct values)
tc_opt is a tab name on a tab control where the comboboxes are
For Each dstable In dstcopt.Tables
For Each dscolumn In dstable.Columns
Dim colName As String = dscolumn.ToString
MsgBox(colName) 'This retuns "aantigen"
Dim cb As ComboBox = Me.tc_opt.Controls("cb_" & colName)
cb.Items.Add(colName)
'cb_aantigen.DataSource = dstcopt.Tables(dstable.ToString)
'cb_aantigen.DisplayMember = "aantigen"
'cb_atarget.DataSource = dstcopt.Tables(dstable.ToString)
'cb_atarget.DisplayMember = "atarget"
Next
Next
The second problem comes when I do it manually (which works) using the exact combobox names cb_aantigen and cb_atarget as seen in the comments.
The problem is that once the form is loaded and the cb's are filled with the correct values, I can't change the value in any single cb individually, when I change one value it changes them all (there is 15 comboboxes in total) I know this is down to using a dataset, but I don't know away to 'unlink them from each other or the dataset'
Not sure if I need to split this into 2 questions, but help on either problem would be appreciated.
EDIT:
After looking at only this section of code for a day. This is what I have come up with to tackle both the problems at once.
The combobox control not working was down to using a tab tc_opt instead of a groupbox gp_anti
The issue with splitting the dataset up into individual comboboxes, I've worked around by taking the value of each cell in the database and adding it separately, probably a better way to do it though
For Each dstable As DataTable In dstcopt.Tables
For Each dscolumn As DataColumn In dstable.Columns
Dim colName As String = dscolumn.ToString
Dim cb(2) As ComboBox
cb(0) = CType(Me.gp_anti.Controls("cb_" & colName), ComboBox)
cb(1) = CType(Me.gp_rec.Controls("cb_" & colName), ComboBox)
cb(2) = CType(Me.gp_nat.Controls("cb_" & colName), ComboBox)
For icb = 0 To cb.Count - 1
If Not (IsNothing(cb(icb))) Then
For irow = 0 To dstable.Rows.Count - 1
If dstable.Rows(irow)(colName).ToString <> Nothing Then
Dim icbitemdupe As Boolean = False
If cb(icb).Items.Contains(dstable.Rows(irow)(colName).ToString) Then
icbitemdupe = True
End If
If icbitemdupe = False Then
cb(icb).Items.Add(dstable.Rows(irow)(colName).ToString)
End If
End If
Next
End If
Next
Next
Next