Vb.net Gridview "pointer"? - vb.net

I have code that interacts with a gridview, and the code is exactly the same for multiple gridviews. So can I do something like this:
Dim gridViewPointer As GridView
If (gridViewNumber = 1) Then
gridViewPointer = GridView1
ElseIf (gridViewNumber = 8) Then
gridViewPointer = GridView8
...
and then
If (gridViewPointer.DataSourceID = SQLDatasourcetemp.ID) Then
...
Will this work or is there another way to do this?
Edit:
I'm checking to make sure that the data the user is inputting into the gridview is correct. It could be one of 4 gridviews, and the checks are exactly the same, the only parameter that changes in the code is gridview1/gridview2/etc. So if I can use a "pointer" to the correct gridview then I can elimninate all the duplicate code.

Yes that is not a problem at all.
Whenever you assign an object to a variable you are actually assigning a memory reference to the variable. Using that reference you can read, write, and call all properties and methods of the object as if it were there original.
You might want to read up on the differences between value and reference types. This is primarily a concern when passing data through function calls.
http://msdn.microsoft.com/en-us/library/t63sy5hs%28VS.80%29.aspx
In fact I would probably create a new function to call on the gridview...
Private Sub GridOperations(ByVal grid as GridView)
//Do work here.
End Sub
If (gridViewNumber = 1) Then
GridOperations(GridView1)
ElseIf (gridViewNumber =8) Then
GridOperations(GridView8)
...

What you are asking is correct. When you set gridViewPointer = GridView1, you are actually only storing the pointer to the GridView1 object, not copying the object, so any action you perform on gridViewPointer after the set will directly control GridView1.

Related

Saving Custom Document Properties in a Loop

I'm trying to save the values of data that have been input into my form. There are a total of about 50 different fields to save across 5 different agents, so I loaded the data into arrays.
I've tried saving the fields in a loop, but it doesn't seem to work in a loop, only if each field has a separate line, which is a lot of code and messy. The Ag1Name, Ag2Name and Ag3Name are the names of my textboxes that the user enters to populate the form.
Sub LoadAndSaveData()
NumberofAgents = 3
Dim AgentName(3) as String
AgentName(1) = Ag1Name.Value
AgentName(2) = Ag2Name.Value
AgentName(3) = Ag3Name.Value
For Count = 1 To NumberOfAgents
With ActiveDocument.CustomDocumentProperties
.Add Name:="AgentName" & Count, LinkToContent:=False, Value:=AgentName(Count), Type:=msoPropertyTypeString
End With
Next Count
End Sub
The data doesn't get saved to the Custom Document Properties when the code is set up in a loop like the above. Since there are so many values to save and all the data is already in arrays, I would much prefer to use a loop rather than write out a separate line of code for all ~50 of the values. It does seem to work when each field is saved in a separate line of code.
I think this would probably get what you want. You don't really need to count the document properties first, only increment with the ones you want to update. Hopefully the only document properties you want contain the name AgentName in it.
ReDim AgentName(0) As String
Dim P As Long
For Each c In ThisDocument.CustomDocumentProperties
If InStr(1, c.Name, "AgentName", vbTextCompare) > 0 Then
ReDim Preserve AgentName(P)
AgentName(P) = c.Value
P = P + 1
End If
Next c
As a guest I cannot post a comment here, but the code you gave works OK here.
However, there is a problem with creating legacy custom document properties programmatically, because doing that does not mark the document as "changed". When you close the document, Word does not necessarily save it and you lose the Properties and their values.
However, if you actually open up the Custom Document Property dialog, Word does then mark the document as "changed" and the Properties are saved.
So it is possible that the difference between your two scenarios is not the code, but that in one scenario you have actually opened the dialog box to check the values before closing the document and in the other you have not.
If that is the case, here, I was able to change this behaviour by adding the line
ActiveDocument.Saved = False
after setting the property values.
If you do not actually need the values to be Document Properties, it might be better either to use Document Variables, which are slightly easier to use since you can add them and modify them with exactly the same code, or perhaps by storing them in A Custom XML Part, which is harder work but can be useful if you need to extract the values somewhere where Word is not available.
You can make this even easier by looping the controls on the UserForm, testing whether the control name contains "Ag" and, if it does, create the Custom Document Property with the control's value - all in one step.
For example, the following code sample loops the controls in the UserForm. It tests whether the controls Name starts with "Ag". If it does, the CustomDocumentProperty is added with that control's value.
Sub LoadAndSaveData()
Dim ctl As MSForms.control
Dim controlName As String
For Each ctl In Me.Controls
controlName = ctl.Name
If Left(controlName, 2) = "Ag" Then
With ActiveDocument.CustomDocumentProperties
.Add Name:=controlName, LinkToContent:=False, value:=ctl.value, Type:=msoPropertyTypeString
End With
End If
Next
End Sub
I feel a little stupid... I just realized that the reason that the code wasn't working was that the variable NumberofAgents was not being calculated correctly elsewhere in my code. I've got it working now. Thanks for your thoughts!

VBA- UserForm.Lable.Caption gets Error 91 Object Not Set

System Description: I have a userform that takes input on an item that is being returned. A user clicks the row of the item that needs to be returned and then clicks a "Check-In button"
My Attempt: I created a button checkin_cmdbutton on the spreadsheet that measures which item is selected by which cell is selected Application.ActiveCell.Row, writes the info into a userform Checkin_Form, the user finishes the rest of the check-in info, and clicks submit.
This code is the event for the button checkin_cmdbutton on the spreadsheet:
Private Sub checkin_cmdbutton_Click()
Set ItemID = Cells(Application.ActiveCell.Row, 1)
Set ItemDescription = Cells(Application.ActiveCell.Row, 2)
If ItemID Is Nothing Then
MsgBox ("ID is null, ending...")
Exit Sub
End If
Checkin_Form.UserForm_Initialize
Checkin_Form.itemid_dynamiclabel.Caption = ItemID.Value
Checkin_Form.description_dynamiclabel.Caption = ItemDescription.Value
Checkin_Form.checkin_datepicker.Value = Date
Checkin_Form.Show
End Sub
Problem: The code throws an error 91 "Object variable or with block variable not set" at Checkin_Form.itemid_dynamiclabel.caption and the following 2 lines. Why is an object on a form throwing this error? I can't declare these, can I?
You shouldn't be explicitly calling UserForm_Initialize - that's an event handler, and there's a reason handlers are Private by default: they're invoked by the event provider, when the event provider deems it necessary - in this case, when the object instance is getting initialized.
The best way to ensure the form gets initialized properly, is to treat it like the object it is, instead of storing global state on its default instance.
A UserForm class is little more than a class module with a designer and a VB_PredeclaredId module attribute. This attribute makes VBA create a global-scope object variable named after the class, and that is how this code is legal:
UserForm1.Show
Except, it shouldn't be.
You DON'T want to store global state in the default instance: that's the very last thing you want, especially if your form involves dynamic controls.
New it up instead.
With New UserForm1
.Show
'what follows only executes when the form is closed:
'...
End With
For this to work, you must handle the form's QueryClose event, to prevent the object instance from self-destructing itself when the user clicks the [X] button.
For this to work, you must also avoid destroying the form yourself, e.g. with Unload Me (or worse, Unload UserForm1) calls - say, when the user clicks the [Ok] button. Instead, you Hide (or Me.Hide) the form, so that the caller (the code that New'd it up) can still access the object's state.
From the look of your code - i.e. with the .Show call being the very last thing your macro does, I can tell that you're having the form run the show: this is an anti-pattern that will keep creating problems every time you do that.
Forms don't implement application logic: forms present and collect data. Nothing more, nothing less. It's not the form's job to write to any spreadsheet, or even to know anything about worksheets.
Read this recent article of mine if you want more information about doing forms right.
Now, the actual problem.
Checkin_Form.itemid_dynamiclabel.Caption = ItemID.Value
If that label is dynamic (i.e. created at run-time), then I'm surprised accessing it like this even compiles. First, remove the underscore in the form's name: underscores have a special meaning in VBA - I'm sure you've noticed the pattern by now, of how VBA generates event handlers for a given object:
Private Sub ObjectName_EventName()
End Sub
If ObjectName or EventName has an underscore, you're asking for compile errors at one point or another - one day you'll want to use an Implements statement and discover that your code can't be compiled anymore, if you kept that underscore habit: better lose it now.
If the control is dynamic, you can't do what you're trying to do the way you're doing it.
Dynamic controls need to be accessed through the form's Controls collection:
Dim myLabel As MSForms.Label
Set myLabel = Me.Controls("NameOfTheLabelControl")
Otherwise, you need to keep a reference to the dynamic contols at module-level, in the form's code-behind - you could expose it via a property:
Option Explicit
Dim myLabel As MSForms.Label
Private Sub UserForm_Initialize()
Set myLabel = Me.Controls.Add(...)
End Sub
Public Property Get ThatLabel() As MSForms.Label
Set ThatLabel = myLabel
End Property
Or better, use an actual model class, and let the calling code not be bothered with controls at all - see the previously linked article for details.
TL;DR:
You're getting that error because your label object instance isn't initialized, i.e. it's Nothing. Since you aren't showing your form's code-behind, we can't really point out why that is the case, but my money is on the form's default instance making you yet another victim of the "hey look how easy it is!" VBA tutorials that teach things wrong.
Implement the worksheet-handling code outside the form, make the form collect data, make the calling code read this data after the form is hidden, and then make the calling code create and destroy the form instance.
Now, with all that said, I've no idea why you think you need a dynamic control for this.
Just shooting in the dark, as far as I really do not know the names of your variables and what they are (a few screenshots will be helpful). Try like this, if your code is in a form (as far as you have _Click I assume it is):
Private Sub checkin_cmdbutton_Click()
Set ItemID = Cells(Application.ActiveCell.Row, 1)
Set ItemDescription = Cells(Application.ActiveCell.Row, 2)
Me.itemid_dynamiclabel.Caption = ItemID.Value
Me.description_dynamiclabel.Caption = ItemDescription.Value
Me.checkin_datepicker.Value = Date
Me.Show
End Sub
And try at least declaring the variables (e.g. ItemID etc) and using Option Explicit on top.

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.

Scope issue in Access VBA Dictionary?

I'm having a strange problem. I'm new to Access and VBA, so it may be a stupid mistake.
Private backColorCycle As Integer
Private doneRows As New Dictionary
Private Sub AlternateGroupColor()
If Not doneRows.Exists(Me.JCH_Shape) Then
'... some stuff that assigns a value to backColorCycle
Else
'... some stuff that assigns a value to backColorCycle
End If
doneRows.Item(Me.JCH_Shape) = backColorCycle
Detail.BackColor = QBColor(doneRows.Item(Me.JCH_Shape))
GroupHeader0.BackColor = QBColor(doneRows.Item(Me.JCH_Shape))
End Sub
AlternateGroupColor() is an event handler that is called repeatedly (by the OnFormat event in Access). Me.JCH_Shape, a string, cycles through a set of values twice: it might be A, B, C, A, B, C as the function is called, so I want to know when a value has been encountered already. I hoped to determine this by storing the value in doneRows and checking to see if the value already exists. However, even though I've checked that Me.JCH_Shape does indeed have different values and doneRows.Item(Me.JCH_Shape) does return the value I expect at the end of the function, doneRows.Count is never greater than 1. It seems as if the changes I make to the dictionary in the sub are reset every time it is called, and I'm not sure why. It feels like a scope problem, but I can't understand why this would happen to a variable that is a member of the class and not just the function.
Any help would be appreciated. Thanks.
A Dictionary allows objects to serve as its key. Me.JCH_Shape is an object on your report, so when you do
doneRows.Item(Me.JCH_Shape) = backColorCycle
you are repeatedly re-assigning the value to the Dictionary entry whose key is the Me.JCH_Shape object itself. If you want to store Dictionary items based on the current value of the Me.JCH_Shape object you need to use
doneRows.Item(Me.JCH_Shape.Value) = backColorCycle

How can I change a toolbox item property by referring to the item through a variable?

Sorry for the confusing title - here is the code I'm using.
Example code -
If bolCorrect = False Then
intIncorrect += 1
temp3 = "picture" + CStr(intIncorrect)
temp3.Visible = True
I've got several images all, with names of picture[number-from-0-to-10], and I want them to show depending on the count of a variable.
The error it throws up is that 'Visible' is not part of 'String'. How can I get the interpreter to look at 'temp3' in this instance, and refer to the toolbox item rather than the type of the variable (e.g. string)?
You need to refer to the actual name property you have set for the picturebox control (if you are using the picturebox control)
So if your picture box control is named pb1
pb1.Image = System.Drawing.Image.FromFile("picture" + counter + ".jpg")
pb1.Visible = True
You should generally try to avoid addressing controls via strings, that’s usually just a hack around a proper solution. Instead, maintain a variable to that control, or, in your case, maintain an array of the relevant controls and access them via an index.
That said, it is possible to get a control given its name via the Form.Controls collection:
Dim ctl = Me.Controls("picture" + CStr(intIncorrect))