BlakeMcKenna
asked on
Waiting for value in DataGridView Cell before advancing?
I have a DGV in which certain cells are populated automatically by pressing the enter key in the specific cell and then reading a value from a Digital Multimeter. Retrieving this value may take up to 2 seconds, however, the cursor advances to the next cell before the previous cell is populated with the value from the read. Is is possible to wait for the cursor to advance to the next cell until the current cell has a value?
ASKER
On an empty cell, I hit the enter key and I got the following error. See attachment!
Screenshot.jpg
Screenshot.jpg
If cDGV(e.ColumnIndex, e.RowIndex).Value is nothing orelse (string statement) then
ASKER
I should have explained myself better. When a user hits the enter key, there is logic in the KeyPress event that retrieves a value to populate the cell. I am trying to minimize the number of events to use for this DGV. Your modified code works, however, that is the first event that is fired upon hitting the enter key. I need for the KeyPress event to fire to execute the retrieve value code.
So, it seems I need to have logic in the KeyPress event...
So, it seems I need to have logic in the KeyPress event...
How / where are you retrieving the value? I mean in the key press but where are you calling it from? Database, webservice?
ASKER
There is a function call in the KeyPress Event that makes a call to a Digital Multimeter and retrieves a value from this device. So it's not a DB or Webservice
Is that call done asynchronously or synchronously. Can you pass a reference to the cell so you know which cell you need to update? EG: don't prevent the user from changing cells, when it's done updating, go to this cell?
ASKER
The call is asynchronous I'm not passing a reference because I'm assigning the retrieved value directly to the cell in the KeyPress Event. I do capture the row/column indexes and store them in variables. I'm wondering if I can capture the value in a variable and assign it to the cell in a different event?
If the call is asynchronous you can pass a userstate as an object.
What I would do is pass a reference to the cell, and then when the value is returned, you can cast the reference back to the cell and update the value right in the process value event.
so for example:
Sub KeyPress
AddHandler GetValueComplted, MyHandler
GetValueAsync(params, MydgCell)
end sub
Sub GetValueCompleted (sender, GetValueCompletedEventArgs e)
DataGridCell cell = (DataGridCell) e.UserState
cell.Value = e.Value
end sub
Something like that. The above is just pseudo code to show the concept, not concrete or compilable.
What I would do is pass a reference to the cell, and then when the value is returned, you can cast the reference back to the cell and update the value right in the process value event.
so for example:
Sub KeyPress
AddHandler GetValueComplted, MyHandler
GetValueAsync(params, MydgCell)
end sub
Sub GetValueCompleted (sender, GetValueCompletedEventArgs
DataGridCell cell = (DataGridCell) e.UserState
cell.Value = e.Value
end sub
Something like that. The above is just pseudo code to show the concept, not concrete or compilable.
ASKER
I'm assuming I create this new event in the "KeyPress" event. What event should "MyHandler" be?
Can you show me this using VB.Net please?
Can you show me this using VB.Net please?
That is in VB.Net
Can you show me your code for processing the enter key, and retreiving the value from the digital Multireader?
Can you show me your code for processing the enter key, and retreiving the value from the digital Multireader?
ASKER
Look at your previous post. This code doesn't make sense to me!
Here is the KeyPress Event:
Sub GetValueCompleted (sender, GetValueCompletedEventArgs e)
DataGridCell cell = (DataGridCell) e.UserState
cell.Value = e.Value
end sub
Here is the KeyPress Event:
Private Sub cDGV_KeyUp(sender As Object, e As KeyEventArgs) Handles cDGV.KeyUp
Try
Dim blnLastCol As Boolean = False
Dim oCurrentCell As DataGridViewCell = cDGV.CurrentCell
If e.Handled = True Then
Exit Sub
End If
EH.ErrorMessage = ""
strCellData = ""
colIDX = cDGV.CurrentCell.ColumnIndex
rowIDX = cDGV.CurrentCell.RowIndex
prevColIdx = colIDX - 1
If prevColIdx > 0 Then
strCellData = cDGV.Rows(rowIDX).Cells(prevColIdx - 1).Value
End If
If blnRunAdded Then
startingCol = 5
Else
startingCol = 2
End If
If e.KeyCode = Keys.Return Or e.KeyCode = Keys.Tab Then
If gblnOEM Then
If IsNumeric(cDGV.Rows(rowIDX).Cells(1).Value) Then
iRunValue = cDGV.Rows(rowIDX).Cells(1).Value
Else
iRunValue = 0
End If
If cboNLHyst.Checked Then
tmpCol += 1
End If
'The IF logic determines where to move the cursor after a reading is taken
If rowIDX < cDGV.RowCount - 1 AndAlso (colIDX >= cDGV.ColumnCount - tmpCol And colIDX <= cDGV.ColumnCount) Then
If rowIDX = 0 Or rowIDX <> iTestRow Then
Dim i As Integer = 0
Else
If ValidateCellValues(cDGV.CurrentRow, colIDX) Then
If blnEnableFirstZero Then
iRowFinished += 1
Else
blnEnableFirstZero = True
End If
'If a new run has been added for a S/N, then the Rin, Rout and 1st Zero readings are skipped
If blnRunAdded Then
cDGV.Columns(2).ReadOnly = False
cDGV.Columns(3).ReadOnly = False
cDGV.Columns(4).ReadOnly = False
End If
If cDGV.Columns(colIDX).HeaderText.IndexOf("Final Zero") Then
cDGV.Rows(rowIDX).Cells(colIDX - 1).Value = GetDecimalPlaces(GetZeroOutput(RealReadMode, First0, oCurrentCell.Value, Me), gLoadDecimalPlaces) 'Gets Electronic Reading
iTestRow += 1
End If
End If
End If
blnRunAdded = False
blnAddRunButtonClicked = False
startingCol = 2
cDGV.Rows(rowIDX + 1).ReadOnly = False
cDGV.CurrentCell = cDGV(startingCol, rowIDX + 1)
Else 'The ELSE logic will take a reading and place the value in the current cell
If Not cboManual.Checked And rowIDX <> 0 Then
If rowIDX = iTestRow Then
Select Case colIDX
Case 0 To 2
'Protected Columns
Case 3
If Not IsNothing(strCellData) AndAlso Not strCellData.Length = 0 Then
cDGV.Rows(rowIDX).Cells(prevColIdx).Value = GetDecimalPlaces(ReadRin(1, 0), 0)
End If
Case 4
If Not IsNothing(strCellData) AndAlso Not strCellData.Length = 0 Then
cDGV.Rows(rowIDX).Cells(prevColIdx).Value = GetDecimalPlaces(ReadRout(1, 0), 0)
End If
Case 5 '1st Zero
If Not IsNothing(strCellData) AndAlso Not strCellData.Length = 0 Then
cDGV.Rows(rowIDX).Cells(prevColIdx).Value = GetDecimalPlaces(GetZeroOutput(RealReadMode, First0, oCurrentCell.Value, Me), gLoadDecimalPlaces) 'Gets Electronic Reading
End If
Case ((cDGV.ColumnCount + 1) - tmpCol) To cDGV.ColumnCount
'Protected Columns
Case colIDX
If Not IsNothing(strCellData) AndAlso Not strCellData.Length = 0 Then
sLoadAmt = GetLoadAmount(colIDX, 0)
cDGV.Rows(rowIDX).Cells(prevColIdx).Value = GetDecimalPlaces(getOutput(RealReadMode, cmbOutputUnit.Text, sLoadAmt), gLoadDecimalPlaces) 'Gets Electronic Reading
If colIDX = (cDGV.ColumnCount - tmpCol) And rowIDX = (cDGV.RowCount - 1) Then
blnLastCol = True
End If
End If
End Select
End If
End If
If colIDX = cDGV.ColumnCount - tmpCol Then
If rowIDX = cDGV.RowCount - 1 Then
oCurrentCell = cDGV(startingCol, 1)
Else
oCurrentCell = cDGV(startingCol, rowIDX + 1)
If Not blnEnableFirstZero Then
cDGV.Rows(rowIDX + 1).ReadOnly = False
iRowFinished += 1
Else
blnEnableFirstZero = True
End If
End If
blnAddRunButtonClicked = False
blnRunAdded = False
Else
oCurrentCell = cDGV(colIDX, rowIDX)
End If
cDGV.CurrentCell = oCurrentCell
End If
Else
'AddHandler DelayCellAdvancement.KeyPress, AddressOf DelayCellAdvancement_KeyPress
If colIDX = 2 Then
idx += 1
sLoadAmt = GetLoadAmount(prevColIdx, rowIDX - 1)
If idx = cDGV.RowCount Then
cDGV.Rows(rowIDX).Cells(colIDX).Value = GetDecimalPlaces(getOutput(RealReadMode, cmbOutputUnit.Text, sLoadAmt), gLoadDecimalPlaces) 'Gets Electronic Reading
cDGV.ClearSelection()
txtFinalZero.Enabled = True
txtFinalZero.Focus()
Else
strCellValue = GetDecimalPlaces(getOutput(RealReadMode, cmbOutputUnit.Text, sLoadAmt), gLoadDecimalPlaces) 'Gets Electronic Reading
cDGV.Rows(rowIDX - 1).Cells(colIDX).Value = GetDecimalPlaces(getOutput(RealReadMode, cmbOutputUnit.Text, sLoadAmt), gLoadDecimalPlaces) 'Gets Electronic Reading
cDGV.CurrentCell = cDGV(startingCol, rowIDX)
End If
End If
End If
ElseIf e.KeyCode = Keys.Delete Then
idx = rowIDX
cDGV.CurrentCell.Value = ""
End If
If blnLastCol Then
cDGV.ClearSelection()
btnTestingComplete.BackColor = Color.LightGreen
btnTestingComplete.Focus()
End If
Catch ex As Exception
EH.ErrorMessage = "cDGV_KeyUp() - " & ex.Message & "...Contact Engineering!" & "~E"
End Try
EH.ProcessMessages(Me, sbr, EH.ErrorMessage)
End Sub
I thought you said the readings were done asynchronously?
An asynchronous method you call the method specifying a call back method to invoke when the function is done. These seem more synchronous?
Do you control the code for ReadRin, ReadRout, GetZeroOutput, and getOutput?
If so can you pass oCurrentCell as a param, and then update it in each call?
An asynchronous method you call the method specifying a call back method to invoke when the function is done. These seem more synchronous?
Do you control the code for ReadRin, ReadRout, GetZeroOutput, and getOutput?
If so can you pass oCurrentCell as a param, and then update it in each call?
ASKER
I'm a self-taught OOP. I looked up the definition of asynchronous/synchronous and apparently I thought it was something else. Can you show me an example of an asynchronous method plz?
I do control the code for the other Functions...
I do control the code for the other Functions...
What you're programming currently is synchronous. Everything happens in lock step.
You call a function the processor jumps immediately to the function and then returns to the place when done.
Asynchronus programming happens in the background (may or may not be on a different thread).
Esssentially you setup the function to call a "Callback method". What you're saying to the program is hey, invoke this function, but I don't want to wait for it. When it's done, go to this function over here so I can process the results. The easiest way to tell is if you call a long running function in a synchronous mode your application will lock up while waiting for the results. If it doesn't lock up you're most likely using asynchronous.
Google for asynchronous calls as it's really outside the scope of this question.
Since you control all of the other functions, I would pass oCurrentCell into each function as needed. This way you don't care where the active cell is, you just run your code and when you're done update oCurrentCell.
You call a function the processor jumps immediately to the function and then returns to the place when done.
Asynchronus programming happens in the background (may or may not be on a different thread).
Esssentially you setup the function to call a "Callback method". What you're saying to the program is hey, invoke this function, but I don't want to wait for it. When it's done, go to this function over here so I can process the results. The easiest way to tell is if you call a long running function in a synchronous mode your application will lock up while waiting for the results. If it doesn't lock up you're most likely using asynchronous.
Google for asynchronous calls as it's really outside the scope of this question.
Since you control all of the other functions, I would pass oCurrentCell into each function as needed. This way you don't care where the active cell is, you just run your code and when you're done update oCurrentCell.
ASKER
Passing oCurrentCell into the function did not work...there was no change in the effect.
post your code for one of the functions.
ASKER
Here ya go:
Public Function getOutput(ByVal ReadMode As Short, ByVal thisOutUnit As String, ByVal Value As Single, ByVal currentCell As DataGridViewCell) As Single
Try
Dim OutputValue As Single = UnrealOutput
Dim thisExci As Double = CType(sglExci, Double)
If ReadMode = PureTestMode Then
Return Value
End If
If HPConnectedPort.IndexOf("Out") < 0 Then
ConnectToOutputPort(thisOutUnit)
End If
If gstrFaultyDeviceName <> "" Then
EH.ErrorMessage = "The " & gstrFaultyDeviceName & " is not functioning properly!" & "~I"
EH.ProcessMessages(frmCalibration1, frmCalibration1.sbr, EH.ErrorMessage)
Return UnrealOutput
End If
If thisOutUnit = "mV/V" Then
OutputValue = CType((CType(readOutput(ReadMode, Value), Double) / thisExci) * 1000, Single)
ElseIf thisOutUnit = "Vdc" Then
OutputValue = readOutput(ReadMode, Value)
ElseIf thisOutUnit = "mA" Then
OutputValue = ReadOutputCurrent(ReadMode, Value) * 1000
End If
Return OutputValue
Catch ex As Exception
EH.ErrorMessage = "getOutput() - " & ex.Message & "...Contact Engineering!" & "~E"
End Try
EH.ProcessMessages(frmCalibration1, frmCalibration1.sbr, EH.ErrorMessage)
End Function
You never used current cell -
Change it to a Sub as you will no longer need to return a value.
currentCell.Value = OutputValue
in place of the return.
Change it to a Sub as you will no longer need to return a value.
currentCell.Value = OutputValue
in place of the return.
ASKER
I actually figured it out. The CellValidating event is what I want to use! I just need to figure out where to put the rest of the logic. But that is what you suggested in the first place.
ASKER
I've requested that this question be closed as follows:
Accepted answer: 0 points for BlakeMcKenna's comment #a40037875
for the following reason:
I actually figured it out. The CellValidating event is what I want to use! I just need to figure out where to put the rest of the logic. But that is what you suggested in the first place.
Accepted answer: 0 points for BlakeMcKenna's comment #a40037875
for the following reason:
I actually figured it out. The CellValidating event is what I want to use! I just need to figure out where to put the rest of the logic. But that is what you suggested in the first place.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
My apologies...I forgot about this question. That worked for me!
Open in new window