Graphic objects update without skip, vba - vba

I have a code, for example the class named MyClass and expect after each step the result is updated in the excel workbook, and some chart and graphic object is updated dynamically.
The delay of updating cells, charts and images is just ok but I have to add an additional pause because otherwise it will skip all the process and jumps graphic update to the end of code execution.
What should I do to getrid of unintended pause?
Public a As Double
Public b As Double
Public counter As Integer
Private Sub Class_Initialize()
a = Range("A1")
b = Range("B1")
counter = 1
End Sub
Public Sub some_calculation()
a = a + b
End Sub
Public Sub graphic_update()
counter = counter + 1
Cells(count, 1) = a
Sheet1.Shapes("Picture 1").Rotation = Sheet1.Shapes("Picture 1").Rotation + 45
End Sub
Public Function pause(pause_time As Double)
Start_Time = Timer
Do
DoEvents
Loop Until (Timer - Start_Time) >= pause_time
End Function
Sub test()
Dim obj1 As New MyClass
For i = 1 To 10
obj1.some_calculation
obj1.graphic_update ' if I had a kind of confirmation of previous line execution, which is cells update, to go to the next one, it would be just ok.
obj1.pause (0.001)
Next i
End Sub

Since you requested an example of DoEvents - I would try something like this:
Public Sub graphic_update()
counter = counter + 1
Cells(count, 1) = a
Sheet1.Shapes("Picture 1").Rotation = Sheet1.Shapes("Picture 1").Rotation + 45
DoEvents '<<<
End Sub

Related

Time Loop in VBA - Looping through Columns

There are two sheets (sheet1 and sheet2).
Need to refresh sheet1 every 4 minutes as long as the workbook is open.
Then I Need to copy values from Column C in sheet1 to column C in sheet2 every 4 minutes.
Since new values would come in every 4 mins in sheet1, I want the values to be copied to a new column in sheet2 everytime.
I'm using the following code. The problem with my code is that the variable i is getting initiated fresh everytime and I am not able to initiate it to a value outside the module using Public i as long.
Sub copyvalues()
Dim i As Long
i = 3
Sheets(2).Columns(i).Value = Sheets(1).Range("C11:C90").Value
i = i + 1
Application.OnTime Now + TimeValue("00:04:00"), "copyvalues"
End Sub
Although you mention you can't declare variable i as Public, this should work:
For example, the following works without issues:
Public i As Long
Sub test()
Debug.Print i
Application.OnTime Now + TimeValue("00:00:01"), "Module1.test"
i = i + 1
End Sub
Try the following:
Public i As Long
Public SubIsRunning As Boolean
Sub initiatesubs()
If Not SubIsRunning = True Then
i = 3
Call copyvalues
SubIsRunning = True
End If
End Sub
Sub copyvalues()
Workbooks(REF).Sheets(2).Columns(i).Value = Workbooks(REF).Sheets(1).Range("C11:C90").Value
i = i + 1
Application.OnTime Now + TimeValue("00:04:00"), "Module1.copyvalues" 'assuming the sub is in Module1
End Sub

A Sub Procedure holds a list, but only the first item is changed through SetCursorPosition

Module Module1
Dim MenuList As New List(Of String)
Function LineCount() As Integer
Return MenuList.Count
End Function
Sub CreateNewMenu(strings() As String)
For Each s In strings
MenuList.Add(s)
Next
End Sub
Sub PrintMenu(highlight As Integer)
For I = 0 To MenuList.Count - 1
If I = highlight Then
SwapColors()
Console.WriteLine(MenuList(I))
Console.ResetColor()
Else
Console.WriteLine(MenuList(I))
End If
Next
End Sub
Sub SwapColors()
Dim temp = Console.BackgroundColor
Console.BackgroundColor = Console.ForegroundColor
Console.ForegroundColor = temp
End Sub
Sub Main()
CreateNewMenu(
{
"Option A",
"Option B",
"Option C"
})
Dim CurrentItem As Integer = 0
Dim CurrentKey As ConsoleKey
While CurrentKey <> ConsoleKey.Enter
Console.Clear()
PrintMenu(CurrentItem)
'Console.SetCursorPosition(10, 10)
'Console.SetCursorPosition(10, 11)
'Console.SetCursorPosition(10, 12)
CurrentKey = Console.ReadKey(True).Key
Select Case CurrentKey
Case ConsoleKey.DownArrow
CurrentItem += 1
Case ConsoleKey.UpArrow
CurrentItem -= 1
End Select
CurrentItem = (CurrentItem + LineCount()) Mod LineCount()
End While
End Sub
End Module
This is a Code, A changed Post written 2014. The following link should be the source. How can you detect key presses in vb console mode?
The PrintMenu Procedure spotted in the Main Sub Procedure is the line i try to Change with a SetCursorPosition command.
I am able to Change the Output to be screened on row 10 line 10. But only for the Option A.
What has to be done, to manage every item that can be listed on Screen? I want to change the row and line for every item through the PrintMenu Procedure.
The problem is that the cursor position is set for the first item in the list but when WriteLine is called, the cursor position is reset to the next line on column = 0. I would change the PrintMenu method to accept the left and top cursor positiion. Something like this (untested):
Sub PrintMenu(highlight As Integer, left As Integer, top As Integer)
For I = 0 To MenuList.Count - 1
Console.CursorLeft = left
Console.CursorTop = top + I 'Use loop variable to adjust top position
If I = highlight Then
SwapColors()
Console.Write(MenuList(I)) 'Changed to use Write instead
Console.ResetColor()
Else
Console.Write(MenuList(I))
End If
Next
End Sub

How do I prevent ListBox - Multiselect Change Event from firing twice on the first selection?

I have created a UserForm that has two ListBoxes, one populated from a dictionary that contains excess items from a company report, and the other from a dictionary that contains excess items from a bank report. The first ListBox is a fmMultiSelectMulti, allowing the user to select multiple items to get the sum of the selected items (which change the value of a TextBox).
My issue is when I select the first item in the ListBox, the ListBox_Change() event fires twice. The sum variable is public since it is referenced in other methods, but upon the double-fire, it doubles the real value.
Here's the code for the change event:
Private Sub GPListBox_Change()
For lItem = 0 To GPListBox.ListCount - 1
If GPListBox.Selected(lItem) = True Then
gpTotal = gpTotal + GPListBox.List(lItem, 1)
Debug.Print GPListBox.List(lItem, 1)
End If
Next
GPTotalTextBox.Value = Format(gpTotal, "$#,##0.00")
End Sub
The other method that removes the (multiple) selected variables, and references the sum variable:
Private Sub RemoveButton1_Click()
For lItem = GPListBox.ListCount - 1 To 0 Step -1
If GPListBox.Selected(lItem) Then
gpTotal = gpTotal - GPListBox.List(lItem, 1)
'GPListBox.RemoveItem GPListBox.ListIndex
GPListBox.RemoveItem lItem
GPTotalTextBox.Value = gpTotal
End If
Next
End Sub
This is the UserForm after I selected the first item, which automatically deselected and left the sum present:
.
My Question: How do I prevent this from double-firing every time the first selection occurs?
I have got around it like this in the past. Something like this.
Use a global boolean at the top of you code. Above all subs and functions.
Dim bFire as Boolean
A boolean is false by default so you will have to set the boolean to true somewhere outside of you subs such as form UserForm_Initialize event or something. If you don't have a place to do that, switch the true/false usage in the subs (Benno Grimm Elaborated on this below in comments).
Private Sub UserForm_Initialize()
bFire = True
End Sub
Then use the boolean in the subs.
Private Sub GPListBox_Change()
'Check the status and get out if you have set it to not fire.
If bFire = false then
Exit Sub
End If
For lItem = 0 To GPListBox.ListCount - 1
If GPListBox.Selected(lItem) = True Then
gpTotal = gpTotal + GPListBox.List(lItem, 1)
Debug.Print GPListBox.List(lItem, 1)
End If
Next
GPTotalTextBox.Value = Format(gpTotal, "$#,##0.00")
End Sub
In the button that modifies it, set the boolean false at the start and true at the end.
Private Sub RemoveButton1_Click()
'Set it false
bFire = false
For lItem = GPListBox.ListCount - 1 To 0 Step -1
If GPListBox.Selected(lItem) Then
gpTotal = gpTotal - GPListBox.List(lItem, 1)
'GPListBox.RemoveItem GPListBox.ListIndex
GPListBox.RemoveItem lItem
GPTotalTextBox.Value = gpTotal
End If
Next
'After you have modified the control set it to true
bFire = True
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'.

Getting 424 Runtime error, object required

Please help with the code error 424 below
Public counter As String
Private Sub Workbook_WindowActivate(ByVal Wn As Window)
If ActiveSheet1.Name = Sheet2.Name Then
If counter = 0 Or counter = Null Then
Call LLP_Hide
End If
End If
End Sub
Remove 1 from ActiveSheet1.
If counter is meant to count it should be declared as Integer type instead of String.
Anyway, this part of code will also cause error: counter = 0. Replace it with counter = "0" or change the type of counter.
Public counter As Integer
Private Sub Workbook_WindowActivate(ByVal Wn As Window)
If ActiveSheet.Name = Sheet2.Name Then
If counter = 0 Then
Call LLP_Hide
End If
End If
End Sub