MsgBox AFTER QueryTable refresh is done - vba

I have this code where I refresh a QueryTable:
Sub refreshCD()
ActiveWorkbook.Connections("CD").Refresh
End Sub
How can I display a MsgBox AFTER the refresh is complete? I've tried to place it right after the refresh, but obviously it ran before it was done since there's no type of callback or something.
I've read about DoEvents(), but I couldn't understand very well or apply that, and don't know if this is right method.
Any ideas?

you can create a class module and sink the events of the querytable, you have both before and after refresh available, like so
Private WithEvents qtCustom As QueryTable
Public Function Initialise(qtInput As QueryTable)
Set qtCustom = qtInput
End Function
Private Sub qtCustom_AfterRefresh(ByVal Success As Boolean)
' After Refresh
End Sub
Private Sub qtCustom_BeforeRefresh(Cancel As Boolean)
' Before Refresh
End Sub

Related

How to run vba macro after an access form has loaded?

When I call a sub in Form_Load, it gives me an error cause by Screen.ActiveForm. This is due to the form not being loaded yet.
What sub/function can I use to run a macro once the form has loaded.
I tried Form_Timer, it didn't do anything
Form_Activate produces same error
Form_after… they do not really seem to indicating anything after form load.
Here is my code for Form_Timer:
Private Sub Form_Timer()
call Module6.loadRecords
Me.TimerInterval = 500
End Sub
I was hoping that after 0.5 seconds that my form will be loaded and records will be display in the form controls.
Instead of depending on Screen.ActiveForm, you should simply pass the form reference to the function.
Private Sub Form_Load()
Call Module6.loadRecords(Me)
End Sub
and
Public Sub loadRecords(F As Access.Form)
If you really want to use Screen.ActiveForm, it works like this:
Private Sub Form_Load()
' 1 ms is enough to de-couple the events
Me.TimerInterval = 1
End Sub
Private Sub Form_Timer()
' Reset timer, always the first thing to do for single Timer events
Me.TimerInterval = 0
Call Module6.loadRecords
End Sub

An instance of an Excel-VBA form opens with an error, if it was closed from the top right red `X`

Prehistory
I have read the best practises for creating a form, concerning the fact that one should always refer to an object of the form and not the form itself. Thus, I have decided to build a boiler-plate form for myself.
The problem
Everything ran smoothly, until the moment I have decided to close the form with the top right red X. It closes ok. But then, when I try to open the form again, I get this runtime error:
The error is on objPresenter.Show (see the code below). Obviously, it does not enter in the if above. But the problem is that the closing from the X does not work fine. When I close the form from the End button, anything works. And even, if I copy the code for the closing from the btnEnd to UserForm_QueryClose it still does not work the same.
The form
Thus, I have a modMain, frmMain and clsSummaryPresenter, which all take care of the form. I start the code from modMain
My form looks like this:
It has btnRun, btnExit, lblInfo. The name of the class is frmMain.
The code
In frmMain:
Option Explicit
Public Event OnRunReport()
Public Event OnExit()
Public Property Get InformationText() As String
InformationText = lblInfo.Caption
End Property
Public Property Let InformationText(ByVal value As String)
lblInfo.Caption = value
End Property
Public Property Get InformationCaption() As String
InformationCaption = Caption
End Property
Public Property Let InformationCaption(ByVal value As String)
Caption = value
End Property
Private Sub btnRun_Click()
RaiseEvent OnRunReport
End Sub
Private Sub btnExit_Click()
RaiseEvent OnExit
End Sub
Private Sub UserForm_QueryClose(CloseMode As Integer, Cancel As Integer)
If CloseMode = vbFormControlMenu Then
Cancel = True
Hide
'Even if I change the two lines above with this the error happens:
'RaiseEvent OnExit
'However, if I simply write END in stead of those two lines
'anything works quite ok...
'but that is a bit brutal.
End If
End Sub
In clsSummaryPresenter
Option Explicit
Private WithEvents objSummaryForm As frmMain
Private Sub Class_Initialize()
Set objSummaryForm = New frmMain
End Sub
Private Sub Class_Terminate()
Set objSummaryForm = Nothing
End Sub
Public Sub Show()
If Not objSummaryForm.Visible Then
objSummaryForm.Show vbModeless
Call ChangeLabelAndCaption("Press Run to Start", "Starting")
End If
With objSummaryForm
.Top = CLng((Application.Height / 2 + Application.Top) - .Height / 2)
.Left = CLng((Application.Width / 2 + Application.Left) - .Width / 2)
End With
End Sub
Public Sub Hide()
If objSummaryForm.Visible Then objSummaryForm.Hide
End Sub
Public Sub ChangeLabelAndCaption(strLabelInfo As String, strCaption As String)
objSummaryForm.InformationText = strLabelInfo
objSummaryForm.InformationCaption = strCaption
objSummaryForm.Repaint
End Sub
Private Sub objSummaryForm_OnRunReport()
MainGenerateReport
Refresh
End Sub
Private Sub objSummaryForm_OnExit()
Hide
End Sub
Public Sub Refresh()
With objSummaryForm
.lblInfo = "Ready"
.Caption = "Task performed"
End With
End Sub
In modMain
Option Explicit
Private objPresenter As clsSummaryPresenter
Public Sub MainGenerateReport()
objPresenter.ChangeLabelAndCaption "Starting and running...", "Running..."
GenerateNumbers
End Sub
Public Sub GenerateNumbers()
Dim lngLong As Long
Dim lngLong2 As Long
tblMain.Cells.Clear
For lngLong = 1 To 4
For lngLong2 = 1 To 1
tblMain.Cells(lngLong, lngLong2) = lngLong * lngLong2
Next lngLong2
Next lngLong
End Sub
Public Sub ShowMainForm()
If (objPresenter Is Nothing) Then
Set objPresenter = New clsSummaryPresenter
End If
objPresenter.Show
End Sub
The question
Once again, why I cannot close the form with the red X? I can substitute the code in UserForm_QueryClose with End but that is a bit brutal. Any ideas?
Changing the form's mode from vbModeless to vbModal gives you an earlier and more informative failure:
The problem seems to be because the Cancel = True assignment in the QueryClose handler, isn't working for some reason.
The signature for the QueryClose handler is as follows:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
Yours is:
Private Sub UserForm_QueryClose(CloseMode As Integer, Cancel As Integer)
You should never type these handler signatures manually yourself - instead, use the drop-down in the codepane's top-right corner, and have the VBE generate the handler stubs for you:
That way your handler signatures will always match the interface they're for.
VBA doesn't really care about parameter names in handlers: the way the runtime matches a handler signature is by matching the parameter indices and their types, against the expected ones. Since both QueryClose parameters are Integer values, inverting them compiles just fine - except when you set Cancel = True, what the runtime sees is that you've assigned CloseMode = -1 and left the Cancel parameter alone.
Which means your form doesn't cancel its close, and thus the object gets destroyed every time.
Invert the parameters in your QueryClose handler, and everything works perfectly fine and exactly as intended.
Calling the form like so works just fine for me:
Option Explicit
dim mfrmMain as ufMain
Sub ShowMainForm2()
If ufMain Is Nothing Then
Set ufMain = New mfrmMain
End If
mfrmMain.Show vbModeless
End Sub

form loading the last state it was before hide()

I have 2 forms. I want to open the second form in the last state it was left? I mean changes intact, textfields changed, choices made etc etc. I try using .show but it does load the form from its load sub which resets the form from the fresh state.
Can anyone guide me here? Thanks in advance
If you don't want the form to reload, don't destroy it.
In the main form, store a reference to the single instance of the second form.
Private m_Dialog As Form2
Private Sub Command1_Click()
If m_Dialog Is Nothing Then Set m_Dialog = New Form2
m_Dialog.Show
End Sub
Private Sub Form_Unload(Cancel As Integer)
If Not m_Dialog Is Nothing Then Unload m_Dialog
Set m_Dialog = Nothing
End Sub
In the second form, use Hide() to close it.
Private Sub OKButton_Click()
Me.Hide
End Sub

How to show userform 1 time only

In VBA for excel, I have a userform then I want this to show only for 1 instance. Even if the user re-open it, it won't open again. Is there any code for it? well, I'm also using this code for my login:
Private Sub UserForm_Initialize()
User.Caption = Environ("Username")
End Sub
I'm thinking if i can use this code in my problem. Hoping for a quick response. thanks guys, you're awesome!
Yes, it's possible.
You have to add new sheet. In a cell A1 type 0 (zero), then hide it. In a code which calls UserForm, use this:
Sub ShowMyForm()
If ThisWorkbook.Worksheets("HiddenSheet").Range("A1")=0 then MyUserForm.Show
End Sub
In a form:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
ThisWorkbook.Worksheets("HiddenSheet").Range("A1")=1
ThisWorkbook.Save()
DoEvents
End Sub
If you don't want to add an extra sheet just to store one bool, you can set a custom document property like this:
Private Sub Workbook_Open()
On Error Resume Next
Dim test As Boolean
test = Me.CustomDocumentProperties("UserFormShown").Value
If Err.Number = 0 Then Exit Sub
UserForm1.Show
Me.CustomDocumentProperties.Add "UserFormShown", False, msoPropertyTypeBoolean, True
End Sub
If the property hasn't been set yet it will throw an error, so trapping an error lets you know if you've set the property (and shown the form).

Excel VBA Timer Keeps Stopping

I currently have a macro that when i click anywhere on the userform, a picturebox is moved to the left. i have added a timer into this so that it will always keep going left after the first form click. The problem is that the picturebox does move to the left, but only once. After, nothing happens. This is my code so far:
Private Sub UserForm_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Call PlayerMoving
End Sub
Public Sub PlayerMoving()
Player1.Left = Player1.Left + 5
Call StartTimer
End Sub
Sub StartTimer()
Application.OnTime Now + TimeValue("00:00:01"), "PlayerMoving"
End Sub
Like i mentioned before, after the first move, nothing else happens. I don't know why. i have also tried a do while loop like this:
Public Sub PlayerMoving()
do while SOME_STATEMENT_HERE
Player1.Left = Player1.Left + 5
Call StartTimer
loop
End Sub
Great question! The answer for this lies with the Application.OnTime function. It is designed to call procedures that are in regular modules, not class objects like forms. In other words the OnTime function can't find your PlayerMoving sub because it is in your form's class instead of in a regular module.
To correct this, you can simply add the following wrapper function in a regular VBA Module:
Public Sub MoveMyPlayer()
UserForm1.PlayerMoving
End Sub
Then change your OnTime call to schedule the MoveMyPlayer function that resides in the regular module:
Public Sub StartTimer()
Application.OnTime Now + TimeValue("00:00:01"), "MoveMyPlayer"
End Sub
Also keep in mind that your code should have a way to stop the timer when it is finished. You probably want to add another function to your form, and call it when you are ready to stop moving the image:
Public Sub CancelTimer()
Application.OnTime Now, "MoveMyPlayer", , False
End Sub
Hope that helps!
Adam
Make sure that these are in an ordinary module (not the User Form module). Modify to use your form's Name in case it differs from UserForm1:
Public timerOn As Boolean
Public Sub PlayerMoving()
UserForm1.Player1.Left = UserForm1.Player1.Left + 5
Call StartTimer
End Sub
Sub StartTimer()
If timerOn Then
Application.OnTime Now + TimeValue("00:00:01"), "PlayerMoving"
End If
End Sub
In your UserForm module:
Sub UserForm_Activate()
Player1.Left = 0 'Set the initial position if desired
Module1.timerOn = False '## Modify to the module name
End Sub
Private Sub UserForm_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Module1.timerOn = True
Call PlayerMoving
End Sub
Sub UserForm_Terminate()
Module1.timerOn = False
End Sub
There may be a better/more refined way to handle this, but it is what I came up with relatively quickly.
So we create a boolean variable which determines whether to keep the "timer" looping. We set it to false when the form unloads and also make sure to reset the Player1.Left when you re-activate the form, otherwise it may "disappear".
Then, we can simply toggle this switch as needed.