How to get ProjectBeforeAssignmentChange to Communicate with Task Field - vba

I am currently trying to use a ProjectBeforeAssignmentChange application in a class module to pull resource and assignment costs into a task field. I am doing this because I want to group resource loads on a task into labor and material buckets. I have received some great guidance on how to use a ProjectBeforeAssignmentChange application to isolate an assignment change and react to it. I am now trying to make a link between this application and the ability to parse and sum data in a task.Cost# field.
Currently, the class module prints a lot of helpful information into the immediate window. Can someone please recommend how to sum Cost = Assgn.Cost as it loops and route it to tsk.Cost5 (for my purposes)? I have tested multiple approaches to this problem and all failed. It appears that the assignment change event does not acknowledge task objects.
ThisProject:
Private Sub Project_Open(ByVal pj As Project)
Call m_Events.StartEvents
End Sub
m_Event as a regular module:
Public oMSPEvents As New cm_Events
Public EnableEvents As Boolean
Sub StartEvents()
Set oMSPEvents.MyMSPApp = Application ' MSProject.Application
EnableEvents = True
End Sub
cm_Events Class Module:
Private Sub MyMSPApp_ProjectBeforeAssignmentChange(ByVal Assgn As Assignment, ByVal Field As PjAssignmentField, ByVal NewVal As Variant, Cancel As Boolean)
'
'Dim tsk As task
If Assgn.Resource.Text2 = "Labor" Then
Cost = Assgn.Cost
Debug.Print Now, Assgn.ResourceName, Assgn.Resource.Text2, NewVal, "$" & Cost, Assgn
End If
If EnableEvents Then
EnableEvents = False
' tsk.Cost5 = 10
'
' Dim Assgn As Assignment
' For Each Assgn In tsk.Assignments
' If Assgn.Resource.Text1 = "Labor" Then
' tsk.Cost5 = tsk.Cost5 + Assgn.Cost
' Assgn.Cost5 = Assgn.Cost5 + Assgn.Cost
' End If
' Next Assgn
'
EnableEvents = True
End If
End Sub

Goal: Capture changes to assignment-level cost changes and roll them into the task's Cost5 field
Issue: While the event, ProjectBeforeAssignmentChange, can be used to react to assignment-level changes, the Cost field is calculated so changes to it are done indirectly, for example, by changing the amount of work. Therefore, the event handler needs to react to changes to any field that would change the assignment cost.
Here is an example of how to use the ProjectBeforeAssignmentChange event to monitor changes to the assignment work to update the task's Cost5 field:
Private Sub MyMSPApp_ProjectBeforeAssignmentChange(ByVal asg As Assignment, ByVal Field As PjAssignmentField, ByVal NewVal As Variant, Cancel As Boolean)
If EnableEvents And Field = pjAssignmentWork Then
EnableEvents = False
Dim tsk As Task
Set tsk = asg.Task
Dim resRate As Double
resRate = CDbl(Mid(asg.Resource.StandardRate, 2, InStr(asg.Resource.StandardRate, "/") - 2))
Dim newAsnCost As Variant
newAsnCost = NewVal * resRate
If asg.Resource.Text1 = "Labor" Then
tsk.Cost5 = newAsnCost
Else
tsk.Cost5 = 0
End If
Dim Assgn As Assignment
For Each Assgn In tsk.Assignments
If Assgn.Resource.Text1 = "Labor" And Assgn <> asg Then
tsk.Cost5 = tsk.Cost5 + Assgn.Cost
End If
Next Assgn
EnableEvents = True
End If
End Sub
Note: the resRate calculation is based on the Standard Rate having format like "$123.45/hr"; adjust as necessary.

Take a look at this Wiki article. It will show you how to do it.
https://social.technet.microsoft.com/wiki/contents/articles/32051.ms-project-extra-fields-in-views.aspx

Related

VBA Run macro with arguments from Command Button click in UserForm

I have a UserForm DataInput in the same VBA project as the module RunOB. These are in the Excel Project Benchmarking.xlsx. I have a command button on the DataInput form in which I would like the module RunOB to run when pressed. Additionally I am passing String arguments from the form to the module.
'Header of 'Run OB' Module
Sub OrganizeBenchmarks(bidNum As String, name As String, _
state As String, year As String, category As String)
The button click method is:
Private Sub CommandButton1_Click()
'...'
End Sub
If possible, it would also be helpful if I could set up a keyboard shortcut to open the DataInput UserForm while I was in the Excel project.
Seems pretty straightforward?
Private Sub CommandButton1_Click()
OrganizeBenchmarks "your", "arguments", "would", "go", "here"
End Sub
Something for you to help developing your own code .........
'Public enum type to add a set of particular vbKeys to the standard key set
Public Enum typePressKeys
vbNoKey = 0
vbExitTrigger = -1
vbAnswerKey = 100
vbLaunchKey = 102
vbPrevious = 104
vbNext = 106
vbSpecialAccessKey = 108
End Enum
Public Sub OrganizeBenchmarks()
Dim stopLoop As Boolean, listOfYears As Variant, bidNum As String, name As String, state As String, year As String, category As String
'For example
listOfYears = Array(2017, 2018, 2019, 2020, 2021, 2022)
stopLoop = False
Load DataInputForm 'Assuming this is the name of your UserForm
With DataInputForm 'Assuming that this name would be an input control in your UserForm
.yearCB.List = listOfYears 'comboboxList control
.yearCB.ListIndex = 2
.Tag = vbNoKey
End With
DataInputForm.Show
Do
If DataInputForm.Tag = vbOK Then
'I have assumed you would use these names for input controls on your UserForm
bidNum = DataInputForm.bidNum_TextBox.Value
name = DataInputForm.name_TextBox.Value
state = DataInputForm.state_TextBox.Value
year = CStr(DataInputForm.yearCB.Value)
category = DataInputForm.category_TextBox.Value
Unload DataInputForm
stopLoop = True
ElseIf DataInputForm.Tag = vbCancel Then
Unload DataInputForm
Exit Sub
Else
stopLoop = False
End If
Loop Until stopLoop = True
'Place additional code here to take whatever actions
End Sub

Passing Values in VBA

In the code I am posting, I am using a check box called "ACDS Test" and whenever it is checked it creates a sheet, then when it becomes unchecked it calls the upper function and deletes the sheet.
I am trying to add a message box that essentially works like a fail safe to ensure they want to delete the page. If they say they do not want to delete the page then I want the checkbox to stay checked.
For some reason I am getting this error message when I try to pass the value to make sure the checkbox stays checked and I cannot figure out why.
The error comes up on the line:
Sub ACDSTest_Click(CorrectValue As Integer)
And the specific error is: "Compile error: Procedure Declaration does not match description of event or procedure having the same name".
Any help is much appreciated! IF any more clarification is needed please feel free to ask!
Sub DeleteWorksheet(NameSheet As String)
Dim Ans As Long
Dim t As String
Dim CorrectValue As Integer
Dim i As Long, k As Long
k = Sheets.Count
Ans = MsgBox("Would you like to take this test off of the form?", vbYesNo)
Select Case Ans
Case vbYes
'Code reads through each page and finds one with corresponding name to string t
'Once it finds the correct page, it deletes it
For i = k To 1 Step -1
t = Sheets(i).Name
If t = NameSheet Then
Sheets(i).Delete
End If
Next i
CorrectValue = 0
Case vbNo
CorrectValue = 1
End Select
End Sub
Sub ACDSTest_Click(CorrectValue As Integer)
Dim NameSheet As String
Dim NameValue As String
NameSheet = "ACDS"
NameValue = "ACDS Test"
If ACDSTest.Value = True Then
CreateWorksheet (NameSheet), (NameValue)
Worksheets("Sheet1").Activate
Else
DeleteWorksheet (NameSheet)
If CorrectValue = 1 Then
ActiveSheet.Shapes("ACDS Test").ControlFormat.Value = 1
End If
End If
End Sub
The issue here is that the CorrectValue variable as you define it in DeleteWorksheet does not exist in the context of the
variable does not exist in context of the ACDSTest_Click subroutine. This is because variables defined within subroutines or functions are local to those functions. To correct this I would convert DeleteWorksheet to a function such as the below.
Further, the event that fires Private Sub ACDSTest_Click() cannot handle passing a value to that function, so changing it to Sub ACDSTest_Click(CorrectValue As Integer) causes an error.
Function DeleteWorksheet(ByVal SheetName As String) As Boolean
On Error GoTo SheetDNE
SheetName = Sheets(SheetName).Name 'Check if sheet exists w/o other objects
On Error GoTo 0
Select Case MsgBox("Would you like to take this test off of the form?", vbYesNo)
Case vbYes
Application.DisplayAlerts = False
Sheets(SheetName).Delete
Application.DisplayAlerts = True
DeleteWorksheet = True
Case Else: DeleteWorksheet = False
End Select
Exit Function 'Exit The Function w/o error
SheetDNE: 'Sheet Does Not Exist
MsgBox "The indicated sheet, " & SheetName & ", does not exist", vbOKOnly
End Function
And
Private Sub ACDSTest_Click()
Dim NameSheet As String
Dim NameValue As String
NameSheet = "ACDS"
NameValue = "ACDS Test"
If ACDSTest.Value = True Then
CreateWorksheet (NameSheet), (NameValue)
Worksheets("Sheet1").Activate
Else
If Not DeleteWorksheet(NameSheet) Then _
ActiveSheet.Shapes("ACDS Test").ControlFormat.Value = 1
End If
End Sub

How to "Undo" after macro run?

I am running a code, that changes the format of the task's row, based on a value in Text1 field.
If I change the Duration, or Finish or some other values when I update the schedule, the value of Text1 (customized field) is modified as well. As a result of that value, I am formatting the background color and the font color.
Once I run this code, I can't use the regular "Undo" so I can't get the values to return to their previous state.
How do I create the "Custom Undo"?
ThisProject Code
Private Sub Project_Change(ByVal pj As Project)
' enable class to modify the Task format on Project change (when a task is changed)
StatusRYGFieldUpdate
End Sub
Regular Code Module
Option Explicit
Public StatusRYGView As New clsTskUpdate
Public UpdateViewFlag As Boolean
Public TskIDChanged As Long
Sub StatusRYGFieldUpdate()
' this Sub is triggered once a task is modified
' if the Field being modifed is related to "Text1"
Dim CurTskID As Long
Set StatusRYGView.ProjApp = Application
Application.Calculation = pjManual
Application.ScreenUpdating = False
If UpdateViewFlag Then
CurTskID = TskIDChanged ' save Row ID
FormatTask (TskIDChanged) ' call the Sub that formats the cell (send the taskId)
End If
Application.Calculation = pjAutomatic
Application.ScreenUpdating = False
End Sub
'===========================================================
Sub FormatTask(TskID)
Dim Tsk As Task
If UpdateViewFlag Then
SelectTaskField TskID, "Text1", False
Set Tsk = ActiveCell.Task ' set the Task to current cell's Task
SelectRow Row:=TskID, RowRelative:=False
' format entire row first
Select Case Tsk.Text1 ' Get the Field's used field, not name
Case "R"
FontEx CellColor:=7, Color:=0
FontEx Italic:=False
Case "Complete"
FontEx Italic:=True
FontEx CellColor:=15, Color:=14 ' Background Silver ; font Gray
End Select
' format "Status" field
SelectTaskField TskID, "Text1", False
Select Case Tsk.Text1 ' Get the Field's used field, not name
Case "R"
' Font Color:=pjWhite ' Font White
FontEx Italic:=False
FontEx CellColor:=1, Color:=7 ' Background Red ; font White
Case "Complete"
FontEx Italic:=True ' Font Italic
FontEx CellColor:=15, Color:=14 ' Background Silver ; font Gray
End Select
End If ' UpdateViewFlag is True
End Sub
clsTskUpdate Class Module
Option Explicit
Public WithEvents ProjApp As Application
Private Sub ProjApp_ProjectBeforeTaskChange(ByVal Tsk As Task, ByVal Field As PjField, ByVal NewVal As Variant, Cancel As Boolean)
' Sub (in "clsTskUpdate" Class) is triggered once a task is modified
' if the Field being modifed is related to "Text1"
' then the UpdateViewFlag is being raised, and the Tsk.ID (task's row) is saved to TskIDChanged variable
UpdateViewFlag = False
TskIDChanged = 0
Select Case Field
Case pjTaskActualFinish
If Not NewVal Like Format(Tsk.ActualFinish, myDateFormat) Then ' need to modify date format to "dd/mm/yy"
LastValue = Tsk.ActualFinish
UpdateViewFlag = True
TskIDChanged = Tsk.ID
End If
Case pjTaskStart
If Not NewVal Like Format(Tsk.Start, myDateFormat) Then ' need to modify date format to "dd/mm/yy"
LastValue = Tsk.Start
UpdateViewFlag = True
TskIDChanged = Tsk.ID
End If
Case pjTaskDuration
If Not NewVal Like (Tsk.Duration / 480) & "*" Then ' need to divide by 480 (in minutes) and add `*` wild-card for "days"
LastValue = Tsk.Duration / 480
UpdateViewFlag = True
TskIDChanged = Tsk.ID
End If
Case pjTaskPercentComplete
If Not NewVal Like Tsk.PercentComplete Then
LastValue = Tsk.PercentComplete
UpdateViewFlag = True
TskIDChanged = Tsk.ID
End If
' other possible Case Scenarios in the future
End Select
End Sub
Microsoft Project 2007 added a pair of methods, OpenUndoTransaction and CloseUndoTransaction, that create a single undo entry for the user to undo an entire macro.
Add these methods to procedure StatusRYGFieldUpdate like this:
Sub StatusRYGFieldUpdate()
Dim CurTskID As Long
Set StatusRYGView.ProjApp = Application
Application.OpenUndoTransaction "Status RYG Field Update"
Application.Calculation = pjManual
Application.ScreenUpdating = False
If UpdateViewFlag Then
CurTskID = TskIDChanged ' save Row ID
FormatTask (TskIDChanged) ' call the Sub that formats the cell (send the taskId)
End If
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True
Application.CloseUndoTransaction
End Sub

Every 5 seconds record the value in 2 cells in another worksheet VBA

Problem:
I have searched extensively for this and cannot seem to get it to work. I have a timer running when the "StartBtn" is pressed:
Dim StopTimer As Boolean
Dim SchdTime As Date
Dim Etime As Date
Dim currentcost As Integer
Const OneSec As Date = 1 / 86400#
Private Sub ResetBtn_Click()
StopTimer = True
Etime = 0
[TextBox21].Value = "00:00:00"
End Sub
Private Sub StartBtn_Click()
StopTimer = False
SchdTime = Now()
[TextBox21].Value = Format(Etime, "hh:mm:ss")
Application.OnTime SchdTime + OneSec, "Sheet1.NextTick"
End Sub
Private Sub StopBtn_Click()
StopTimer = True
Beep
End Sub
Sub NextTick()
If StopTimer Then
'Don't reschedule update
Else
[TextBox21].Value = Format(Etime, "hh:mm:ss")
SchdTime = SchdTime + OneSec
Application.OnTime SchdTime, "Sheet1.NextTick"
Etime = Etime + OneSec
End If
End Sub
Then in another cell (say, C16) I have a manually entered value which is the hourly cost rate. I have a third cell that is calculating total cost by C16*current timer value.
What I want to do is record every 5 seconds after the "StartBtn" is clicked the current time and current calculated cost in another sheet. This is what I have started:
Sub increment()
Dim x As String
Dim n As Integer
Dim Recordnext As Date
n = 0
Record = [TextBox21].Value
Recordnext = [TextBox21].Value + OneSec
Range("B13").Value = Recordnext
Do Until IsEmpty(B4)
If [TextBox21].Value = Recordnext Then ActiveCell.Copy
Application.Goto(ActiveWorkbook.Sheets("Sheet2").Range("A1").Offset(1, 0))
ActiveSheet.Paste
Application.CutCopyMode = False
n = n + 1
Recordnext = [TextBox21].Value + 5 * (OneSec)
Exit Do
End If
ActiveCell.Offset(1, 0).Select
Loop
End Sub
But it doesnt work. Any help would be appreciated.
I have tried to simplify your timer method down to what is actually needed.
Sheet1 code sheet
Option Explicit
Private Sub ResetBtn_Click()
bRun_Timer = False
'use the following if you want to remove the last time cycle
TextBox21.Value = Format(0, "hh:mm:ss")
End Sub
Private Sub StartBtn_Click()
bRun_Timer = True
dTime_Start = Now
TextBox21.Value = Format(Now - dTime_Start, "hh:mm:ss")
Range("D16").ClearContents
Call next_Increment
End Sub
Module1 code sheet
Option Explicit
Public bRun_Timer As Boolean
Public Const iSecs As Integer = 3 'three seconds
Public dTime_Start As Date
Sub next_Increment()
With Worksheets("Sheet1")
.TextBox21.Value = Format(Now - dTime_Start, "hh:mm:ss")
.Range("D16") = Sheet1.Range("C16") / 3600 * _
Second(TimeValue(Sheet1.TextBox21.Value)) '# of secs × rate/sec
Worksheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Resize(1, 2).Offset(1, 0) = _
Array(.TextBox21.Value, .Range("D16").Value)
End With
If bRun_Timer Then _
Application.OnTime Now + TimeSerial(0, 0, iSecs), "next_Increment"
End Sub
Note that the operation of transferring the data to Sheet2 is a direct value transfer¹ with no .GoTo, ActiveCell or Select.
It was not entirely clear to me what you were trying to do with the value transfer. I have stacked them one after another on Sheet1.
        
You would benefit by adding Option Explicit² to the top of all your code sheets. This requires variable declaration and if you misplace a public variable, you will quickly know.
¹ See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.
² Setting Require Variable Declaration within the VBE's Tools ► Options ► Editor property page will put the Option Explicit statement at the top of each newly created code sheet. This will avoid silly coding mistakes like misspellings as well as influencing you to use the correct variable type in the variable declaration. Variables created on-the-fly without declaration are all of the variant/object type. Using Option Explicit is widely considered 'best practice'.

Public subs don't work in this scenario

I have a form named FrmDrvouchers. It contains some public subprocedures.
I call these subs from another form which opens from a form which is opened from FrmDrVouchers as a dialog.
When I open FrmDrvouchers directly, everything works. But when I call FrmDrvouchers from another project which is also part of this solution, its public subs don't work when I call them from another (dialog) form.
Here is the code of the Button Click from which I open FrmDrvouchers:
Dim FrmDrv As FrmDrVouchers = New FrmDrVouchers()
FrmDrv.Show()
This works, but those public subs don't. Why?
Thanks For ur response,
It just skip what i want to do, but not throws any Exceptions,
Now I m posting my Code,,, plz have a look on that,,
Here is my Main form's MenuStrip Button Click code :
Private Sub CashPaymentToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CashPaymentToolStripMenuItem.Click
Dim DrVouchers As Transactions.FrmDebitVouchers = Transactions.New FrmDebitVouchers()
DrVouchers.Show()
End Sub
Here is the FrmDebitVouchers's Public Subs which are not working,,, in the sense that they skip the function which i have written but not throwing any Exception,,
Public Sub DrVoucherOPen(ByVal VoucherNo As Integer)
'Filling the Dataset with selected Voucher No to OPen the record
DebitVouchersTableAdapter.FillByVoucher(Flr12131DataSet.DebitVouchers, VoucherNo)
VoucherDateDateTimePicker.Enabled = False
End Sub
Public Sub DrVoucherBodyOPen(ByVal VoucherNo As Integer)
'---------Procedure to Open DrVouchersBody And to OPen it in Datagridview-------------'
'Getting the User No, and if it is admin then load fill dataset and allow user to edit the record
If GetUserNumber() = 1 Then
'Filling the dataset
DebitVouchersBodyTableAdapter.FillByVoucher(Flr12131DataSet.DebitVouchersBody, VoucherNo)
DrBodyDGV.DataSource = Nothing
Dim Sum As New Decimal
'Initializing the SerialNumbers variable
SerialNumbers = New List(Of Integer)
'Setting datagridview to opend record
For i = 0 To Flr12131DataSet.DebitVouchersBody.Rows.Count - 1
DrBodyDGV.Rows.Add()
DrBodyDGV.Rows(i).Cells(0).Value = Flr12131DataSet.DebitVouchersBody.Rows(i).Item("SerialNo")
DrBodyDGV.Rows(i).Cells(3).Value = Flr12131DataSet.DebitVouchersBody.Rows(i).Item("AccountNo")
DrBodyDGV.Rows(i).Cells(6).Value = Flr12131DataSet.DebitVouchersBody.Rows(i).Item("Debit")
DrBodyDGV.Rows(i).Cells(7).Value = Flr12131DataSet.DebitVouchersBody.Rows(i).Item("Narration")
'Getting serial No into List
SerialNumbers.Add(Flr12131DataSet.DebitVouchersBody.Rows(i).Item("SerialNo"))
'Getting Account Name into Datagridview
If Not Not IsNumeric(DrBodyDGV.Rows(i).Cells(3).Value) Then
Dim Qa As New Flr12131DataSetTableAdapters.QueriesTableAdapter
Dim StrAccountName = Qa.GetAccountName(DrBodyDGV.Rows(i).Cells(3).Value)
DrBodyDGV.Rows(i).Cells(5).Value = StrAccountName
Else
End If
Sum += DrBodyDGV.Rows(i).Cells(6).Value
Next
TxtTotal.Text = Sum
'Setting the controls properties for admin
DrBodyDGV.AllowUserToAddRows = True
DrBodyDGV.AllowUserToDeleteRows = True
DrBodyDGV.ReadOnly = False
BtnSave.Enabled = True
BtnDelete.Enabled = True
BtnPrint.Enabled = True
ToUpdate = True
Else
'If user is not admin then load all record and not allow user to modify it or delete
' Bounding the datagridview
Dim Sum As Decimal = 0
BtnSave.Enabled = False
DebitVouchersBodyTableAdapter.FillByVoucher(Flr12131DataSet.DebitVouchersBody, VoucherNo)
DrBodyDGV.DataSource = Flr12131DataSet.DebitVouchersBody
For i = 0 To DrBodyDGV.Rows.Count - 1
If Not Not IsNumeric(DrBodyDGV.Rows(i).Cells(3).Value) Then
Dim Qa As New Flr12131DataSetTableAdapters.QueriesTableAdapter
Dim StrAccountName = Qa.GetAccountName(DrBodyDGV.Rows(i).Cells(3).Value)
DrBodyDGV.Rows(i).Cells(5).Value = StrAccountName
Else
End If
Sum += DrBodyDGV.Rows(i).Cells(6).Value
Next
TxtTotal.Text = Sum
DrBodyDGV.AllowUserToAddRows = False
DrBodyDGV.AllowUserToDeleteRows = False
' DrBodyDGV.edit()
DrBodyDGV.ReadOnly = True
BtnSave.Enabled = False
BtnDelete.Enabled = False
BtnPrint.Enabled = True
End If
End Sub
Here is another the DrVouchersRecord form from which I call Public Subs:
Private Sub DrVouchersRecordDataGridView_CellDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DrVouchersRecordDataGridView.CellDoubleClick
Dim FrmDrVouchers As FrmDebitVouchers = New FrmDebitVouchers()
If FrmDrVouchers.DrBodyDGV.Rows.Count > 1 Then
Dim Ans As Integer
Ans = MsgBox("Unsaved changes will be lost, want to proceed", vbYesNo + vbInformation, "Alert")
If Ans = vbYes Then
Dim VoucherNo As New Integer
VoucherNo = DrVouchersRecordDataGridView.CurrentRow.Cells(0).Value
FrmDrVouchers.DrVoucherOPen(VoucherNo)
FrmDrVouchers.DrVoucherBodyOPen(VoucherNo)
Me.Close()
End If
Else
Dim VoucherNo As New Integer
VoucherNo = DrVouchersRecordDataGridView.CurrentRow.Cells(0).Value
FrmDrVouchers.DrVoucherOPen(VoucherNo)
FrmDrVouchers.DrVoucherBodyOPen(VoucherNo)
Me.Close()
End If
End Sub
My forms sequence is:
FrmMain is my main startUp form
After that FrmDebitVouchers Open on menustripButtonclick
After that DebitVouchersRecord is Open as Dialog from DrmDebitVouchers,,, and from these Public Subs are called
I Have added Refrence also
Waiting for ur answers,
This problem has hit many programmers coming from VB6 to VB.NET.
In your DataGridView_DoubleCellClick event you create a NEW instance of FrmDebitVouchers.
Then your code refers to property/methods/objects of this new INSTANCE, not of the original one created via CashPaymentToolStripMenuItem_Click. (Also note that this new instance is never showed on the screen, so you are sending/requesting changes to an hidden form instance)
Of course, the DrBodyDGV.Rows.Count is zero on this INSTANCE (referenced as FrmDrVouchers) because probably this INSTANCE has never been intialized like the first one.
Try to add a FrmDrVouchers.Show() after the creation and you will see the hidden SECOND INSTANCE of the form class FrmDebitVouchers.
To solve your problem, you need to pass the reference of the first FrmDebitVouchers instance to the DrVouchersRecord form (for example in the constructor or via a public property) and then use that reference instead of creating FrmDrVouchers