Need help shortening repetitive vb code - vb.net

I'm pretty new to programming, so this may be a pretty basic question. But, I need help to shorten this really repetitive code.
I'm working on a card game that has about 2 hundred different creatures, two decks (your and the enemy's), and 10 slots in each deck (meaning 10 creatures per deck).
In the deck builder part of my UI, when you actually add a creature to your deck, it does this:
If CritName = "Monarch" Then
YourCreature1PictureDB.Image = DHBattleSim.My.Resources.Monarch_Icon
YourCreature1Group.BackColor = Color.Transparent
End If
If the creature is "Ariel", then it'd be
If CritName = "Ariel" Then
YourCreature1PictureDB.Image = DHBattleSim.My.Resources.Ariel_Icon
YourCreature1Group.BackColor = Color.Transparent
End If
etc. Now, imagine there being two hundred of those statements, each for a different creature. THEN I need to copy that huge chunk of code, and change all of the 1's to 2's, and again, changing the 2's to 3's, etc. After I finish the 10's, I'd have to copy ALL OF THAT and change all of the "YourCreature" phrases to "EnemyCreature". So obviously this is extremely repetitive and tedious.
I know about the Find and Replace feature, but I'd rather shorten the code itself so that I don't have to resort to using that.

You can use code like this
Dim resources As Object = DHBattleSim.My.Resources.ResourceManager
YourCreature1PictureDB.Image = resources.GetObject(yourVariable & "_Icon")
This,
YourCreature1Group.BackColor = Color.Transparent
can follow; in your original code it doesn't need to appear in every if, if it is always set to Transparent.

create a sub for setting the image:
Public Sub SetCreatureImage(critName As String)
YourCreature1PictureDB.Image = DHBattleSim.My.Resources.ResourceManager.GetObject(critName + "_Icon")
YourCreature1Group.BackColor = Color.Transparent
End Sub

Related

Quicker method than looping through thousands of records

Is there a quicker way than looping through thousands of records (around 24k)?
Code:
For n = 0 To oCHStockItems.Count - 1
Dim itemSellingPrice As New CH.CH_ItemSellingPrice
With itemSellingPrice
.StockItem = oCHStockItems(n).StockItem
.Code = oCHStockItems(n).StockItem.Code
.Name = oCHStockItems(n).StockItem.Name
.ProductGroupCode = oCHStockItems(n).StockItem.ProductGroup.Code
.CurrentSellingPrice = oCHStockItems(n).StockItem.StockItemPrices(0).Price
.NewSellingPrice = 0D
.LastSellingPriceDate = oCHStockItems(n).LastSellingPriceDate
.OriginalPrice = oCHStockItems(n).OriginalPrice
End With
_itemSellingPrices.Add(itemSellingPrice)
Next
Originally I was assigning oCHStockItems to a Grid (it's actually a Sage 200 Grid) however I can't seem to find a way to reference to the field oCHStockItems(n).StockItem.StockItemPrices(0).Price.
Normally the above kind of syntax works. For example if I want to reference to the Stock Code it would be StockItem.Code.
StockItem.StockItemPrices(0).Price does produce a value however it doesn't show on the Grid. I've logged a ticket with Sage to see if they can help.
However I'm thinking they will come back and say that it can't be done and on that assumption I'm kind of left with looping through oStockItems and assigning the properties to the properties of my predefined class. So with that in mind has anyone any suggestions of speeding this kind of process up?
Sage got back to me with a solution:
First:
AddHandler dgItems.Items.ItemAdded, AddressOf Items_ItemAdded
Then:
Private Sub Items_ItemAdded(ByVal sender As Object, ByVal args As Sage.Common.Controls.ListItemArgs)
args.Item.SubItems(4).Value = oCHStockItems(args.Item.Index).StockItem.StockItemPrices(0).Price
End Sub
This quickly populates the item on the Grid.
All I have done is assign the original collection oCHStockItems to the .DataSource of the grid and the method Items_ItemAdded sorts the rest out.

Recalculate a variable on change event without duplicate code

I'm making a small app that handles pricing and quotes for cloud services.
On one of my forms, I work out some pricing info in the 'me.shown' bit.
The prices for various items are read in from a mysql database on app launch, so i've already got all that info.
My price working out code is as follows:
calc_vmCPUprice = vmcpuprice * cpuslider.Value
calc_vmHDDprice = vmhddprice * hddslider.Value
calc_vmRAMprice = vmRAMprice * memoryslider.Value
If requirebackup.Checked = True Then
calc_backupsetupfee = vmbackupsetupfeecost
calc_backupfee = vmbackupcostperGB * sliderbackup.Value
End If
If supportcheck.Checked = True Then
calc_supportfee = vmsupportfee
End If
vmmonthlycost = calc_vmCPUprice + calc_vmHDDprice + calc_vmRAMprice + calc_backupfee + calc_backupsetupfee + calc_supportfee
vm_monthlycost.Text = "£" & vmmonthlycost
I need to make my program re-calculate the 'vmmonthlycost' variable each time an item is changed.
So for example if you change the slider for cpu (cpuslider.value) then the price will obviously change as the 'calc_vmCPUprice' variable will now be different.
I dont want to copy and paste the calculation code into the change event of each item, as this will be messy and any future changes will need to be mirrored across all items etc.
What would you guys recommend as the best way of achieving this recalculation of price?
I would ideally like to take that bit of code and seperate it then call it from each change event, but as I tinker with vb as a hobbyist more than anything else, my knowledge is still limited.
Thanks in advance!!

How can I use value of a string to control another control in VB.net?

I have been playing around with some code, and I have made easily 50+ controls that all are labeled: PictureBox[XCoordinate]_[YCorrdinate] (Replacing the brackets and contents with the coordinates of them on a little grid I made.)
The problem with this is it is a real pain to use a control when doing loops to update all the picture boxes. I want to know how to do code like:
'This code assumes that the picture boxes are all initialized.
Dim XCoordiante As Integer = 5
Dim YCorrdinate As Integer = 2
PictureBox[XCoordinate]_[YCoordiante].Image = [Put Image Here]
I am going to put this within a loop. Is there a way that I can do this without manually typing it all and risking missing something within a case statement? And also, I would have to retype it for every different kind of change I want to make (ex: tag or error image).
Would a pointer somehow help? I don't really know how to do this, but it would be really helpful if possible.
When you create them, save them to a List:
Private pList As New List(Of PictureBox)
Dim pic As New PictureBox
With Pic
.Location = ...
' etc
End With
Me.Controls.Add(pic)
pList.Add(pic)
Assuming they are created in some sort of order:
For n As integer = 0 To pList.Count = 1
' add code to look at Plist(n).X and .Y to determine what to do (?)
Plist(n).Image = ...
Next n
If there is more info to capture, create a custom class of a PicBox and the other info, and make the list a List(Of myPicClass).

Creating VB.Net forms via code instead of in design mode

I'm using netzero hardware to manage the contents of a number of monitors. My present solution creates a form in VB.Net that has a pixel offset corresponding to where I've placed the Monitors in display management in the control panel. Each monitor has a dedicated form, and in each form are various objects.
The annoyance is that each form must be individually created (so far as I know) at design time. I can't make an array of forms, coupled with an array of offsets and assign all the properties through code.
There ought to be a way to do this...it would simplify my coding and project management.
What I see on MSDN is either over my head or not helpful.
I haven't tested this in hardware yet, but it does compile w/o error:
Public Sub makeform()
Dim MonitorForm(21) As Form
Dim MPictureBoxes(21) As PictureBox
Dim a As Integer
For i As Integer = 0 To n 'up to 21
MonitorForm(i) = New Form
MonitorForm(i).Name = "Form" & (i + 1)
MonitorForm(i).Text = "Form" & (i + 1)
MonitorForm(i).Controls.Add(MPictureBoxes(i))
MonitorForm(i).Location= new Point (x(i), y(i))
With MPictureBoxes(i)
.Name = "Picture Box " & Convert.ToString(i)
.Image = Image.FromFile(CurrentPic(i))
.Location = New System.Drawing.Point(0, 0)
.Size = New Size(1920, 1080)
' Note you can set more of the PicBox's Properties here
End With
Next
End Sub
Where I had gone wrong in my attempts at this was trying to do it this way
Dim Monitor(21) as New Form
That doesn't work, and the difference between Dim Monitor(21) as Form followed by monitor(i)= new Form
was simply too subtle for my present understand of classes, namespaces etc.
.
Well, I've had to give up on this approach and go back to creating n forms at design time (which means that they have names of form2...form22, putting each of them at manual start positions in design mode. There just doesn't seem to be a way to do this with an array of forms. So the code I have built around the messiness of forms2...forms22 works just fine, it's just going to be messy to maintain and elaborate on.
The solution to this may lie in system.screen classes, but the documentation on this is too advanced for me and I'm not finding good code examples for anything other than extracting data about how many screens there are - nothing about writing to them.
This is very easy in code. You want to make many instances of the same form. In this case, I have created a form in the designer called frmTest and I create many instances in code called frmNew:
Public Sub Main()
For x = 100 To 400 Step 100
For y = 100 To 700 Step 200
Dim frmNew As New frmTest
frmNew.Show()
frmNew.Top = x
frmNew.Left = y
frmNew.Height = 100
frmNew.Width = 200
Next
Next
End Sub
I have just used two loops to increment x and y values, but you could do this from a database or config file easily enough.

McAfee deletes code from VBA module

I am trying to program an Excel module where it dynamically inserts code in new objects in a form that is created at design time.
I am using this code where "Code" contains a string with the actual code that should go into the DstrFiles object.
Dim DstrFiles As Object
Set DstrFiles = ThisWorkbook.VBProject.VBComponents("DistributeFiles")
With DstrFiles.CodeModule
.InsertLines .CountOfLines + 1, Code
End With
My problem is that when I use the .InsertLines, McAfee removes the entire Code from my module, is there a way to work around this?
First I create the label with:
Form1.Controls.Add("Forms.Label.1", "Label1", True)
Then I use the .InsertLines to create some code to go with the Label.
For instance, I want the background color of the label to turn red when someone clicks on it. This has been very easy to accomplish with the ".InsertLines".
An ugly way to work around this is to just create a bunch of code beforehand that is ready in the background and then limit the amount of labels that may be created on the fly. - I hope it won't come to that.
I have been googeling around, and this seems to be a known problem with McAfee.
Do anyone know a way to create a dynamic user form that can add code to new labels or button that are added with the Contrls.Add method?
You should not be generating new labels by writing code that creates the controls.
You should be using the .Add method on the Controls collection to create new labels.
For example:
UserForm1.Controls.Add("Forms.Label.1", "foo", True)
You can use WithEvents to get the events.
For example, in UserForm1,
Public WithEvents a As MSForms.Label
Private Sub a_Click()
MsgBox "label clicked"
End Sub
Private Sub CommandButton1_Click()
Set a = UserForm1.Controls.Add("Forms.Label.1", "foo", True)
a.Visible = True
a.Caption = "Hi There"
End Sub
If you want to make a dynamic array of newly added controls, you'll need to create a little wrapper class. Sample code for that is here.
If possible I would recommend against dynamic generation of code (smells like a self-modifying program?).
It's maybe hard to say without knowing your specific problem but I bet there is a better solution using a function with the necessary parameters.
You might be able to workaround this version of McAfee. But the next version of the data-files, or another malware blocker might block you anyhow.
So you can create code like this to run on you development machine, but it will never (or only temporary) work when distributed to customers.