Variable that depends on another variable VBA - vba

I have an interesting question about referencing another variable based on another variable in an array.
Below is my code:
Dim company, price, sdev, mean, random
Dim companies
companies = Array("mm", "tgt", "boog")
mm_price = 0
mm_mean = 0
mm_sdev = 0
tgt_price = 0
tgt_mean = 0
tgt_sdev = 0
boog_price = 0
boog_mean = 0
boog_sdev = 0
For i = 1 To 3
company = companies(i)
mean = company & "_mean"
sdev = company & "_sdev"
Next i
Now, the issue occurs when I attempt to define the "mean" and "sdev" variables, as they will not use the "0" value, but instead give it the string name "mm_mean" etc. mm_mean = 0, therefore, I want mean = 0 when i = 1. Clear?
Thanks, and let me know. It is a rather strange question, and the code is cut from many different functions, so if it doesn't make sense as to why I am doing this, my apologies. I tried to make it as simple as possible so it wouldn't confuse the answerer.

This is a good example of when custom data-structures (aka "record types", "structs") should be used to group related data together. In VBA the syntax is Type:
Type Company
Name As String
Price As Currency
Mean As Double
SDev As Double
End Type
Public Sub CalculateCompanyInfo()
Dim companies(3) As Company
companies(0).Name = "mm"
companies(0).Price = 123
companies(1).Name = "tgt"
companies(1).Price = 456
companies(2).Name = "boog"
companies(2).Price = 789
For i As Integer = 0 to UBound(companies)
companies(i).Mean = ...
companies(i).SDev = ...
Next i
End Sub
Where ... means whatever custom calculation you need to do to get that value.

Related

Global Class for SQL enumerated objects

I'm working on a tool crib VB.NET project, my first real one, so I'm struggling with a few things. In my SQL database I have a few tables that translate into enumerated fields. For example I have code and code type tables. I have a code type with CodeTypeID = 2 and description = Tool Status. These are used to track the location of a tool; Shop, In Transit, On Site, Out of Service, etc. I've set up enumerations for some of these.
Enum CodeType
User = 1
ToolStatus = 2
Repair = 3
OutOfService = 4
End Enum
ToolStatus
Shop = 5
Reserved = 6
InTransit = 7
OnSite = 9
ReturnTransit = 10
OutOfService = 11
Repair = 12
End Enum
The problem is if I change a code in SQL I then have to edit my enumerations as well. In the SQL tables I am only storing the CodeID and join to the code table to get the description. Other than an enumeration, I haven't found a good way to load these tables when the project opens that will allow me to code as easily, for example ToolStatus.OnSite to return the number 9 to my SQL table. It just seems inefficient to me. I always try to code my SQL procedures so if something changes I don't have to go and edit a bunch of procedures and would like to do the same here, especially as a simple addition of a new code could force a version update. Is there a better way to do this? I have searched the Internet and haven't found anything that really works as well as enumeration.
Example Code:
In the following code both OnSite and ReturnTransit are enumerations.
I'm moving items between two datagridviews, a Source and a Target DGV. Also updating the underlying shared datatable.
Dim cbo As ComboBox = sender
With cbo
Select Case .Name
Case "cboPeeps"
dvgFilter = "N"
Case "cboToolbox"
For i As Integer = 0 To dtTools.Rows.Count - 1
dtTools.Rows(i)("dgvFilter") = dtTools.Rows(i)("dbFilter")
Next
If cbo.Items.Count > 0 Then
filter = "ToolboxID=" & .SelectedValue.ToString & " And ToolStatus=" & OnSite
filter += " And UserID=" & .SelectedItem("UserID").ToString
End If
dvgFilter = "T"
End Select
End With
End If
Dim result() As DataRow = dtTools.Select(filter)
For i As Integer = 0 To result.Count - 1
result(i)("dgvFilter") = dvgFilter
If result(i)("dgvToolStatus") <> OutOfService Then result(i)("dgvToolStatus") = ReturnTransit
Next
I was, of course, trying to make this way too difficult. Simple solution along these lines.
Public Class Crepair
Public repairID As Integer
Public techID As Integer
Public repairCodeID As Integer
Public acceptedDate As Date
Public remarks As String
Public isComplete As Boolean
End Class
With nRepair
.repairID = row.Cells("RepairID").Value
.techID = row.Cells("TechID").Value
.repairCodeID = row.Cells("RepairCodeID").Value
.acceptedDate = row.Cells("AcceptedDate").Value
.remarks = row.Cells("Remarks").Value
.isComplete = row.Cells("IsComplete").Value
End With

How can I use iteration to make this vbnet code more efficient?

Function set_bar_positions()
bar_x(0) = delocateX(bar1.Left)
bar_y(0) = delocateY(bar1.Top)
bar_x(1) = delocateX(bar2.Left)
bar_y(1) = delocateY(bar2.Top)
bar_x(2) = delocateX(bar3.Left)
bar_y(2) = delocateY(bar3.Top)
This snippet from one of my functions show what I'm trying to do. These lines repeat almost identical until the end of the function where this is called:
bar_x(29) = delocateX(bar30.Left)
bar_y(29) = delocateY(bar30.Top)
I have tried iterating this functions by doing stuff like this, but now I know I can't:
Dim num As Integer = 0
bar_x(num) = delocateX(bar(num)).Left)
I am trying to make this code more efficient and have less lines. Anyone have an idea I can implement?
You can use Controls.Find
For i = 0 To 29
Dim cs = Me.Controls.Find("bar" & i.ToString(), True)
If cs.Any() Then
Dim c = cs.First()
bar_x(i) = delocateX(c.Left)
bar_y(i) = delocateY(c.Top)
End If
Next

Parameter Form to Input Query Criteria with Multiple Fields Either With a Value or Null

I have built a form with unbound fields designed so a user can input a date range, a facility name (these come from a combobox), and a badge number to generate a query in Access. I want to be able to return results within the selected date range for all facilities if the field is left blank or just the ones for a particular facility if one is selected. I also want to be able to limit the results to those that match an person's badge number.
So the possibilities I want would be:
Date Range = defined by user | Facility - All if not selected | Badge # = All if not selected
Date Range = defined by user | Facility - All if not selected | Badge # = defined by user
Date Range = defined by user | Facility - defined by user | Badge # = All if not selected
Date Range = defined by user | Facility - defined by user | Badge # = defined by user
I originally built it with just the date range and facility name and it worked fine. When I try to add in the Badge # it doesn't really work correctly.
My SQL for the WHERE TO section is:
WHERE (((Diversion.Transaction_Date) Between [Forms]![Parameters]![FromDate] And [Forms]![Parameters]![ToDate])
AND ((Diversion.Employee_Badge_Number)=[Forms]![Parameters]![BadgeNumber])
AND ((Diversion.Facility)=[Forms]![Parameters]![FacilitySelect]))
OR (((Diversion.Transaction_Date) Between [Forms]![Parameters]![FromDate] And [Forms]![Parameters]![ToDate])
AND ((Diversion.Facility)=[Forms]![Parameters]![FacilitySelect])
AND ((([Diversion].[Employee_Badge_Number]) Like [Forms]![Parameters]![BadgeNumber]) Is Null))
OR (((Diversion.Transaction_Date) Between [Forms]![Parameters]![FromDate] And [Forms]![Parameters]![ToDate])
AND ((Diversion.Employee_Badge_Number)=[Forms]![Parameters]![BadgeNumber])
AND ((([Diversion].[Facility]) Like [Forms]![Parameters]![FacilitySelect]) Is Null))
OR (((Diversion.Transaction_Date) Between [Forms]![Parameters]![FromDate] And [Forms]![Parameters]![ToDate])
AND ((([Diversion].[Employee_Badge_Number]) Like [Forms]![Parameters]![BadgeNumber]) Is Null)
AND ((([Diversion].[Facility]) Like [Forms]![Parameters]![FacilitySelect]) Is Null))
OR (((([Diversion].[Facility]) Like [Forms]![Parameters]![FacilitySelect]) Is Null));
To me, it looks like it is including the four possible results that I want to get from the form, but it isn't working right. For instance, if I leave the facility field blank, and define the badge number, it is still giving me all of the results. If I define the facility and define the badge number it does give me the correct results.
Any ideas?
This might give you some ideas building a dynamic query with multiple criteria values. In this example the user can pick any number of the criteria. It is written in VB.Net. It works with Access. I check each field to see if any criteria was provided then append it to the query if there is a valid value.
I used Interpolated strings just because it is easier to see where the spaces go. The alternative is:
String.Format("RoasterId = {0} ", itgRoaster)
I also used a String builder which is an efficient way to alter strings without the overhead of creating and disposing of them with each append. You could just use &= if this is not available in VBA.
Dim bolNeedAnd As Boolean = False
Dim sb As New Text.StringBuilder
sb.Append("SELECT Coffees.ID, Coffees.[Name], Coffees.RoasterID, Roasters.[Name], Coffees.[Type],Coffees.Rating, Coffees.Comment, Coffees.Description, Coffees.Roast, Coffees.IsExtraBold, Coffees.IsFavorite
From Coffees Inner Join Roasters on Coffees.RoasterID = Roasters.ID Where ")
If itgRoaster <> 0 Then
sb.Append($"RoasterID = {itgRoaster} ")
bolNeedAnd = True
End If
If strRoast <> "" Then
If bolNeedAnd Then
sb.Append($"AND Roast = '{strRoast}' ")
Else
sb.Append($"Roast = '{strRoast}' ")
End If
bolNeedAnd = True
End If
If strType <> "" Then
If bolNeedAnd Then
sb.Append($"AND Type = '{strType}' ")
Else
sb.Append($"Type = '{strType}' ")
End If
bolNeedAnd = True
End If
If strRating <> "" Then
If bolNeedAnd Then
sb.Append($"AND Rating = '{strRating}' ")
Else
sb.Append($"Rating = '{strRating}' ")
End If
bolNeedAnd = True
End If
If bolBold Then
If bolNeedAnd Then
sb.Append("AND IsExtraBold = 1 ")
Else
sb.Append("IsExtraBold = 1 ")
End If
bolNeedAnd = True
End If
If bolFavorite Then
If bolNeedAnd Then
sb.Append("AND IsFavorite = 1 ")
Else
sb.Append("IsFavorite = 1 ")
End If
End If
sb.Append("Order By Coffees.[Name];")
Debug.Print(sb.ToString)
Dim cmd As New OleDbCommand With {
.Connection = cn,
.CommandType = CommandType.Text,
.CommandText = sb.ToString}

Variant Join in MS Access

I am converting a spreadsheet that had over 80 Sumifs for output.
For a query, I can join 2 tables, but they depend on 6-8 factors that aren't consistent, hence, the sumifs.
I've been successful with right joins in some instances, but most of the time I have to piece meal all of the conditions.
One of my approaches is to write a function like this:
Public Function DetermineCOB_ID(strFund As String, strType As String, strMDEP As String, _
strTier1 As String, strAirGround As Variant, strAcctType As String) As Integer
If strFund = "A82AB" And strType = "Base" And strTier1 = "2 BCT" And strMDEP = "W25D" And strAcctType = "Subordinate_Cmds" Then
DetermineCOB_ID = 1
ElseIf strFund = "A82AC" And strType = "Base" And strMDEP = "W25D" And strAcctType = "Subordinate_Cmds" Then
DetermineCOB_ID = 2
ElseIf strFund = "A82AD" And strType = "Base" And strMDEP = "W25D" And strAirGround = "Ground" And strAcctType = "Subordinate_Cmds" Then
DetermineCOB_ID = 3
Else
DetermineCOB_ID = 0
End If
End Function
But I will have to write 80 separate ElseIf statements.
I'd like to hook up my logic to a control table that the user can change the factors that determine the values.
This would somehow involve a 'variant' join everywhere there is a join where one is not required. I hope I am making sense.
Is there another approach to solving this problem?
Yes, I am working with a back end that leaves a lot to be desired. In the interim, I'm probing for alternate solutions. Do you know a way to make a join respond like a variant variable i.e so it can work with null values. I've tried outer joins but it doesn't really work.

Datagridview optimization VB.Net

I just want to ask if there is another alternative for filling up the cells in the datagridview. Currently I'm using this code :
For i = DataGridView1.CurrentCell.RowIndex To x - 1
DataGridView1.Rows(i).Cells("LastName").Value = empcoll.Item(i).LastName
DataGridView1.Rows(i).Cells("FirstName").Value = empcoll.Item(i).FirstName
DataGridView1.Rows(i).Cells("MiddleName").Value = empcoll.Item(i).MiddleName
DataGridView1.Rows(i).Cells("CreatedBy").Value = empcoll.Item(i).CreatedBy
DataGridView1.Rows(i).Cells("CreateDate").Value = empcoll.Item(i).CreateDate
DataGridView1.Rows(i).Cells("Status").Value = empcoll.Item(i).Status
DataGridView1.Rows(i).Cells("DailySalary").Value = empcoll.Item(i).DailySalary
DataGridView1.Rows(i).Cells("BirthDate").Value = empcoll.Item(i).BirthDate
Next i
but when I use it for databases with a large number of records, it tends to load slow and hangs up.
You can use the SQLDataSource control (http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.sqldatasource.aspx) and then bind the DataGridView to the SQLDataSource. It is described here: http://www.vkinfotek.com/gridview/bind-gridview-sqldatasource-control.html.
In general, you should be Data Binding the columns to the properties on the objects instead of the method you're currently using.
It would look something like this
LastNameColumn.DataPropertyname = "LastName"
FirstNameColumn.DataPropertyname = "FirstName"
....
DataGridView1.DataSource = MyListofEmployeeObjects
As far as the load speed, there are two options: pagination, and virtual mode.
With pagination, you can pull X of Y records from the database at a time and display them in the grid.
Virtual mode ( http://msdn.microsoft.com/en-us/library/ms171622.aspx ) enables the DataGridView to handle automatic pulling/pushing of the records from/to the database so that you do not have to load all records at once.
Well, I prefer to use the DatagridView.Rows.Add method for unbounded DataGridViews.
Something like this:
Dim loData(7) as object
DataGridView1.Rows.Clear()
For i = DataGridView1.CurrentCell.RowIndex To x - 1
loData(0) = empcoll.Item(i).LastName
loData(1) = empcoll.Item(i).FirstName
loData(2) = empcoll.Item(i).MiddleName
loData(3) = empcoll.Item(i).CreatedBy
loData(4) = empcoll.Item(i).CreateDate
loData(5) = empcoll.Item(i).Status
loData(6) = empcoll.Item(i).DailySalary
loData(7) = empcoll.Item(i).BirthDate
DataGridView1.Rows.Add(loData)
Next i
I usually declare en enum to give a meaning name to each array element and make the code cleaner:
private enum eCols
LastName
Firstname
MiddleName
CreatedBy
CreateDate
Status
DailySalary
BirthDate
end enum
Dim loData(7) as object
DataGridView1.Rows.Clear()
For i = DataGridView1.CurrentCell.RowIndex To x - 1
loData(eCol.LastName) = empcoll.Item(i).LastName
loData(eCol.FirstName) = empcoll.Item(i).FirstName
loData(eCol.MiddleName) = empcoll.Item(i).MiddleName
loData(eCol.CreatedBy) = empcoll.Item(i).CreatedBy
loData(eCol.CreateDate) = empcoll.Item(i).CreateDate
loData(eCol.Status) = empcoll.Item(i).Status
loData(eCol.DailySalary) = empcoll.Item(i).DailySalary
loData(eCol.BirthDate) = empcoll.Item(i).BirthDate
DataGridView1.Rows.Add(loData)
Next i