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
Related
This question already has answers here:
Argument passed ByVal to VB.NET Function and manipulated there
(2 answers)
Closed 4 years ago.
The list value changeed here when passed ByVal why
,it must be not changed.
Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button6.Click
Dim value As Integer = 1
Dim value2 As New List(Of Decimal)
value2.Add(1)
value2.Add(2)
' The integer value doesn't change here when passed ByVal.
Example1(value)
Console.WriteLine(value)
' The list value changeed here when passed ByVal.
Example3(value2)
Console.WriteLine(value)
End Sub
Sub Example1(ByVal test As Integer)
test = 10
End Sub
Sub Example3(ByVal test As List(Of Decimal))
test.Add(3)
End Sub
its solved ,the solution is making new copy:
Sub Example3(ByVal test As List(Of Decimal))
Dim testnew As New List(Of Decimal)
testnew.AddRange(test)
testnew.Add(3)
End Sub
You need to do some reading on value types and reference types and passing method arguments by value and by reference. They are related but not the same thing. When you pass a method argument by value, you create a copy of the variable being passed. If the variable is a value type, i.e. a structure, then that means creating a copy of the value. If the variable is a reference type, i.e. a class, then that means creating a copy of the reference. The thing is, the original reference and the copy still both refer to the same object.
The reason that reference types exist is that you wouldn't want to create copies of large objects every time you assigned them somewhere. In the case of passing a collection to a method, it's almost always the case that any change you make inside the method you will want to be reflected outside. In the rare case that you don't, it's up to you to create a copy of the collection first and pass that in.
When you pass a value type by value, you create a copy of the value. That means that no changes you make inside the method can affect the original variable. You can assign a new value to the parameter or you can set a property of the value and the change will not be reflected outside the method. Of course, value types should generally be immutable and so setting a property should not be possible, but there are times that that "rule" gets broken.
When you pass a reference type by value, you create a copy of the reference. That means that assigning a different object to the parameter inside the method will not affect the original variable. There is still only one object though, referred to by the original variable and the parameter. As such, if you set a property of that object via the parameter then that change will be reflected in the original variable, because it's the same object.
When you pass a value type by reference, you create a new reference to the value. That means that any changes you make inside the method will affect the original variable. You can assign a new value to the parameter or you can set a property of the value and the change will be reflected outside the method.
When you pass a reference type by reference, you create a new reference to the original reference. That means that assigning a different object to the parameter inside the method will affect the original variable. There's still just one object, so setting a property on the parameter will still affect the original variable too.
Those are the only four possibilities: value type by value, reference type by value, value type by reference and reference type by reference. In none of those scenarios is a copy of a reference type object made so in none of those scenarios can you set a property of a reference type object via a method parameter and have that change not be reflected in the original variable.
If you want a copy of the original object then it's up to you to create one explicitly. Whether you do that inside the method or outside really depends on the specific circumstances. That means that you need to change your code to this:
Sub Example3(ByVal test As List(Of Decimal))
Dim copy = test.ToList()
copy.Add(3)
End Sub
or this:
Dim copy = value2.ToList()
Example3(copy)
Let me just repeat the important point here: there is NO WAY to pass a reference type object to a method, modify the object via the parameter inside the method and have that not affect the original variable (assigning a different object to the parameter is NOT modifying the object). If you want a modification inside the method to not affect the original variable then you need a copy of the object and the ONLY way that will happen is if YOU do it explicitly.
I've hit a snag, and my searching hasn't helped so far.
I have a variant being passed into a function which I then intend to copy, perform some calculations, take another copy, perform some other calculations then compare the results of the two copies...
However, when I perform the calculations on one copy, the original variant is also manipulated... so after two copies, and two calculations I end up with 3 variants that are equal to each other and different to the original... Not what I intended.
I expect this is happening because when I use NewVar = OldVar I'm actually taking a reference to the original object. What I actually want, is to make an independent duplicate of the original object - i.e. copy the value of the variable similar to byval in a function delcaration.
My code is linked here: https://1drv.ms/x/s!AiPgb0BH-YZ_ga956eMmJbSdihGjyg.
If you put a break on line 67 of modMain, then watch CutList(1).QTY (the original variable), and CutTrial.RemainingCuts(1).QTY you'll see that both the QTY values decrement when you step through line 67... I want CutList(1).QTY to remain unchanged, and CutTrial.RemainingCuts(1).QTY to decement only.
Any suggestions?
Make sure the functions definition is as follows
Public function DoMagic(ByVal variable as something)
Don't use ByRef or as you found it will modify the reference.
If you are using an object, array or collection you will need to first copy it before using it.
Eg something like the following:
Public Function Clone() As Class1
Set Clone = New Class1
Clone.prop1 = prop1
Clone.PrivateThing = PrivateThing
End Function
I am trying to check if an output column of my script component is NULL.
I tried to use the Row.Column_IsNull, but when I try to do the following:
If Row.Column_IsNull = True Then
// do something
End If
I get an error " Property Row.Column_IsNull is WriteOnly".
What the problem is
The key error in the above was is WriteOnly. When you are referencing columns in Script Components as Transformation, you can specify whether they are ReadOnly, ReadWrite.
When acting as Source, you don't have that option. It's WriteOnly (logically) and they don't even give you the option of the above dialog. So, when you're in your Source and attempt to access write only properties like the following code demonstrates, it breaks.
Public Overrides Sub CreateNewOutputRows()
Output0Buffer.AddRow()
' this is logically wrong
If Output0Buffer.Column_IsNull Then
End If
End Sub
The resolution is that you need to inspect whatever you are assigning into OutputBuffer0.Column prior to making the assignment (or create a separate boolean flag) to keep track of whether the current value was populated.
What the problem isn't
Keeping this here since I already ran down this rabbit hole
Since _IsNull is boolean, you can skip the explicit test and simply use
If Row.Column_IsNull Then
Originally, I had thought this was the classic C-like language issue of assignment (=) vs equality (==) but as #John Saunders was kind enough to point out, this was VB.
That said, the supplied code should work (it does for me).
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Dim x As String
If Row.Src_IsNull = True Then
x = "" ' do nothing
End If
End Sub
I'm surprised at how hard this has been to do but I imagine it's a quick fix so I will ask here (searched google and documentation but neither helped). I have some code that adds items to a collection using keys. When I come across a key that already exists in the collection, I simply want to set it by adding a number to the current value.
Here is the code:
If CollectionItemExists(aKey, aColl) Then 'If key already has a value
'add value to existing item
aColl(aKey).Item = aColl(aKey) + someValue
Else
'add a new item to the collection (aka a new key/value pair)
mwTable_ISO_DA.Add someValue, aKey
End If
The first time I add the key/value pair into the collection, I am adding an integer as the value. When I come across the key again, I try to add another integer to the value, but this doesn't work. I don't think the problem lies in any kind of object mis-match or something similar. The error message I currently get is
Runtime Error 424: Object Required
You can't edit values once they've been added to a collection. So this is not possible:
aColl.Item(aKey) = aColl.Item(aKey) + someValue
Instead, you can take the object out of the collection, edit its value, and add it back.
temp = aColl.Item(aKey)
aColl.Remove aKey
aColl.Add temp + someValue, aKey
This is a bit tedious, but place these three lines in a Sub and you're all set.
Collections are more friendly when they are used as containers for objects (as opposed to containers for "primitive" variables like integer, double, etc.). You can't change the object reference contained in the collection, but you can manipulate the object attached to that reference.
On a side note, I think you've misunderstood the syntax related to Item. You can't say: aColl(aKey).Item. The right syntax is aColl.Item(aKey), or, for short, aColl(aKey) since Item is the default method of the Collection object. However, I prefer to use the full, explicit form...
Dictionaries are more versatile and more time efficient than Collections. If you went this route you could run an simple Exists test on the Dictionary directly below, and then update the key value
Patrick Matthews has written an excellent article on dictionaries v collections
Sub Test()
Dim MyDict
Set MyDict = CreateObject("scripting.dictionary")
MyDict.Add "apples", 10
If MyDict.exists("apples") Then MyDict.Item("apples") = MyDict.Item("apples") + 20
MsgBox MyDict.Item("apples")
End Sub
I think you need to remove the existing key-value pair and then add the key to the collection again but with the new value
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.