getting syntax error using microsoft access we have built - vba

syntax error on the area of code E at the start 6th line
Private Sub Combo21_AfterUpdate()
' Update the row source of the cboProducts combo box
' when the user makes a selection in the cboCategories
' combo box.
Dim mybuyprice, myproduct, myunits
**E If DLookup("[Buy Price]", "GoodsIn_Buy_Price") = Null Then mybuyprice = "0" Else mybuyprice = DLookup("[Buy Price]", "GoodsIn_Buy_Price")
Me.Buy_Price = mybuyprice**
If DLookup("[Product]", "GoodsIn_Buy_Price") = Null Then myproduct = "Null" Else myproduct = DLookup("[Product]", "GoodsIn_Buy_Price")
Me.Product = myproduct
If DLookup("[Unit of Measure]", "Product_Unit_Check") = Null Then myunits = "0" Else myunits = DLookup("[Unit of Measure]", "Product_Unit_Check")
Me.Unit = myunits
Me.Refresh

You have a bunch of repeated logic which would be better factored out into a separate function:
Private Sub Combo21_AfterUpdate()
' Update the row source of the cboProducts combo box when the
' user makes a selection in the cboCategories combo box.
Me.Buy_Price = IfNull(DLookup("[Buy Price]", "GoodsIn_Buy_Price"), "0")
Me.Product = IfNull(DLookup("[Product]", "GoodsIn_Buy_Price"), "Null")
Me.Unit = IfNull(DLookup("[Unit of Measure]", "Product_Unit_Check"), "0")
Me.Refresh
End Sub
'return `exp` if it's not Null, otherwise return `default`
Function IfNull(exp, default)
If IsNull(exp) Then
IfNull = default
Else
IfNull = exp
End If
End Function
See also https://codekabinett.com/rdumps.php?Lang=2&targetDoc=coalesce-function-paramarray-keyword-vba-argument for a more flexible version.

Related

Custom function like msgbox function

I use ms access vba to create my program and I don't like default msgbox design , so I create a form named "frm_msg"
look like this >>>
and I make a simple function with arguments to call this form
Public Function custom_msg(msg_title, msg_txt, msg_icon, msg_button As String)
DoCmd.OpenForm "frm_msg"
Form_frm_msg.lbl_msg_title.Caption = msg_title
Form_frm_msg.lbl_msg_txt.Caption = msg_txt
If msg_icon = "success_icon" Then
Form_frm_msg.success_icon.Visible = 1
Form_frm_msg.error_icon.Visible = 0
Form_frm_msg.warning_icon.Visible = 0
Form_frm_msg.question_icon.Visible = 0
ElseIf msg_icon = "error_icon" Then
Form_frm_msg.success_icon.Visible = 0
Form_frm_msg.error_icon.Visible = 1
Form_frm_msg.warning_icon.Visible = 0
Form_frm_msg.question_icon.Visible = 0
ElseIf msg_icon = "warning_icon" Then
Form_frm_msg.success_icon.Visible = 0
Form_frm_msg.error_icon.Visible = 0
Form_frm_msg.warning_icon.Visible = 1
Form_frm_msg.question_icon.Visible = 0
ElseIf msg_icon = "question_icon" Then
Form_frm_msg.success_icon.Visible = 0
Form_frm_msg.error_icon.Visible = 0
Form_frm_msg.warning_icon.Visible = 0
Form_frm_msg.question_icon.Visible = 1
End If
If msg_button = "ok" Then
Form_frm_msg.btn_ok.Visible = 1
Form_frm_msg.btn_yes.Visible = 0
Form_frm_msg.btn_no.Visible = 0
ElseIf msg_button = "yes_no" Then
Form_frm_msg.btn_ok.Visible = 0
Form_frm_msg.btn_yes.Visible = 1
Form_frm_msg.btn_no.Visible = 1
End If
End Function
now my problem is how do I make it return a value depending on the button that I click?
and use it like default msgbox in if statement
if msgbox("Hallo world",vbInformation+ vbYesNo ,"Hallo") = vbYes Then
'do something ...
end if
The easiest way I found to do this reliably is to place a hidden text box on the form to hold your 'return' value. (I found this more reliable as you can control default value, type etc and draw conclusions from NULL etc which you can't from using a TempVar)
Now write yourself a simple wrapper function which opens the form in dialog mode. In the OnClick event of your buttons, set the hidden text control value to the return value you want, and then hide (don't close) the form. Because it was dialog, it will still be open but not visible, and control flow returns to your wrapper function.
Obtain the value from the hidden text field on the form using a fully qualified reference to the text control, store it in a variable in the wrapper function and do something with it if required, or just return it as is (like the example), then close the form programmatically using DoCmd.Close.
Something simple like this;
Form_Name is the full name of your form
NameOfTextControl is the name of the hidden control set by your onClick event
Function customBox(msg_title, msg_txt, msg_icon, msg_button) as string
'I would usually pass a delimited string of values in the OpenArgs
'or set TempVars if necessary, then use your function code inside
'the form Open event to configure the form dynamically before displaying
DoCmd.OpenForm "Form_Name", acNormal, , , , acDialog
' When we get back here, the form is invisible, not closed
customBox = Forms!Form_Name!NameOfTextControl
DoCmd.Close acForm, "Form_Name", acSaveNo
End Function
So all you then do is replace msgbox with your customBox function and adjust the vbYes / vbNo constant to check for whatever value you set in the form
if customBox(msg_title, msg_txt, msg_icon, msg_button) = "Yes" Then
'do something ...
end if
You can use a module and global/public variables to interact with the form.
Here is a snippet from my project:
' Opens a message box, using form ModernBox, similar to VBA.MsgBox.
'
' Syntax. As for MsgBox with an added parameter, TimeOut:
' MsgMox(Prompt, [Buttons As VbMsgBoxStyle = vbOKOnly], [Title], [HelpFile], [Context], [TimeOut]) As VbMsgBoxResult
'
' If TimeOut is negative, zero, or missing:
' MsgMox waits forever as MsgBox.
' If TimeOut is positive:
' MsgMox exits after TimeOut milliseconds, returning the result of the current default button.
'
' 2018-04-26. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function MsgMox( _
Prompt As String, _
Optional Buttons As VbMsgBoxStyle = vbOkOnly, _
Optional Title As Variant = Null, _
Optional HelpFile As String, _
Optional Context As Long, _
Optional TimeOut As Long) _
As VbMsgBoxResult
' Set global variables to be read by form ModernBox.
mbButtons = Buttons
mbPrompt = Prompt
mbTitle = Title
mbHelpFile = HelpFile
mbContext = Context
Call OpenFormDialog(ModernBoxName, TimeOut)
' Return result value set by form ModernBoxName.
MsgMox = mbResult
End Function
The full code is way too much to post here, so feel free to browse VBA.ModernBox for all the details.

user-defined mixed listing query in sql

suppose that,
I have a db which is included USR_NR field (integer type). and I know all datas in field. no any surprises nr.
USR_NR : 1,3,4,7,9,12,44,13,78
I need to listed this field as mixed like 1,78,44,9,7,3,12,4,13
there is no rule for listing. I want to sort the way I wanted.
I tried with ORDER BY but how can I advance ?
I can not use ASC , DESC
SELECT * FROM DB ORDER BY ?
could you help me for this ?
What you need is a form where you can set a priority number for each record using a function like this:
' Set the priority order of a record relative to the other records of a form.
'
' The table/query bound to the form must have an updatable numeric field for
' storing the priority of the record. Default value of this should be Null.
'
' Requires:
' A numeric, primary key, typical an AutoNumber field.
'
' 2018-08-31. Gustav Brock, Cactus Data ApS, CPH.
'
Public Sub RowPriority( _
ByRef TextBox As Access.TextBox, _
Optional ByVal IdControlName As String = "Id")
' Error codes.
' This action is not supported in transactions.
Const NotSupported As Long = 3246
Dim Form As Access.Form
Dim Records As DAO.Recordset
Dim RecordId As Long
Dim NewPriority As Long
Dim PriorityFix As Long
Dim FieldName As String
Dim IdFieldName As String
Dim Prompt As String
Dim Buttons As VbMsgBoxStyle
Dim Title As String
On Error GoTo Err_RowPriority
Set Form = TextBox.Parent
If Form.NewRecord Then
' Will happen if the last record of the form is deleted.
Exit Sub
Else
' Save record.
Form.Dirty = False
End If
' Priority control can have any Name.
FieldName = TextBox.ControlSource
' Id (primary key) control can have any name.
IdFieldName = Form.Controls(IdControlName).ControlSource
' Prepare form.
DoCmd.Hourglass True
Form.Repaint
Form.Painting = False
' Current Id and priority.
RecordId = Form.Controls(IdControlName).Value
PriorityFix = Nz(TextBox.Value, 0)
If PriorityFix <= 0 Then
PriorityFix = 1
TextBox.Value = PriorityFix
Form.Dirty = False
End If
' Disable a filter.
' If a filter is applied, only the filtered records
' will be reordered, and duplicates might be created.
Form.FilterOn = False
' Rebuild priority list.
Set Records = Form.RecordsetClone
Records.MoveFirst
While Not Records.EOF
If Records.Fields(IdFieldName).Value <> RecordId Then
NewPriority = NewPriority + 1
If NewPriority = PriorityFix Then
' Move this record to next lower priority.
NewPriority = NewPriority + 1
End If
If Nz(Records.Fields(FieldName).Value, 0) = NewPriority Then
' Priority hasn't changed for this record.
Else
' Assign new priority.
Records.Edit
Records.Fields(FieldName).Value = NewPriority
Records.Update
End If
End If
Records.MoveNext
Wend
' Reorder form and relocate record position.
' Will fail if more than one record is pasted in.
Form.Requery
Set Records = Form.RecordsetClone
Records.FindFirst "[" & IdFieldName & "] = " & RecordId & ""
Form.Bookmark = Records.Bookmark
PreExit_RowPriority:
' Enable a filter.
Form.FilterOn = True
' Present form.
Form.Painting = True
DoCmd.Hourglass False
Set Records = Nothing
Set Form = Nothing
Exit_RowPriority:
Exit Sub
Err_RowPriority:
Select Case Err.Number
Case NotSupported
' Will happen if more than one record is pasted in.
Resume PreExit_RowPriority
Case Else
' Unexpected error.
Prompt = "Error " & Err.Number & ": " & Err.Description
Buttons = vbCritical + vbOKOnly
Title = Form.Name
MsgBox Prompt, Buttons, Title
' Restore form.
Form.Painting = True
DoCmd.Hourglass False
Resume Exit_RowPriority
End Select
End Sub
It is explained in detail in my article which includes a demo as well:
Sequential Rows in Microsoft Access
If you don't have an account, browse for the link: Read the full article.
Code is also on GitHub: VBA.RowNumbers
You can use instr():
order by instr(",1,78,44,9,7,3,12,4,13,", "," & USR_NR & ",")
Or, somewhat more verbosely, use switch:
order by switch(USR_NR = 1, 1,
USR_NR = 78, 2,
USR_NR = 44, 3,
. . .
)

Access Public Function Form and Control Parameters

I set up a public function. It is meant to lock and gray a date control, unless its corresponding combo box control is set to a (bound) value of 2. I made this a public function, so I can call it on load of the form, on current, which fires when the form is navigated back or forth, and when the combo changes. I also have to do this for 2 sets of paired combos and date controls, so having it in a function makes it easier to call for any form/control pairing, when I need to.
This is the public function:
Public Function DateFieldStatus(frm As Form, fkctl As Control, dtctrl As Control)
'Combo Status Values
'ID Combo
'1 = not ok
'2 = ok
'3 = not ok
Select Case frm.fkctl
Case 2
frm.dtctrl.Locked = False
frm.dtctrl.ForeColor = RGB(255, 255, 255)
Case Else
frm.dtctrl.Locked = True
frm.dtctrl.ForeColor = RGB(214, 214, 214)
End Select
frm.dtctrl.Requery
End Function
I have it set to call on load of the main form like this:
Private Sub Form_Load()
If DCount("ID", "tblMainTable") = 0 Then
DoCmd.GoToRecord , , acNewRec
Else
DoCmd.GoToRecord , , acFirst
End If
Me.FirstControl.SetFocus
DateFieldStatus Me.Form, Me.FKDropDown1, Me.dtDateControl1
End Sub
When Launch the main form, I get the following error:
Run-time error '2465':
Application-defined or object-defined error
When I debug, it hightlights this line of the public function:
Select Case frm.fkctl
I'm assuming it doesn't like me combining a form parameter with a control parameter.
Is that not something you can do?
Is there a better way to do this?
Thanks for any help!
frm.fkctl is looking for a control named "fkctl" on the form, and not finding it obviously.
You don't need to pass frm to the function at all - the control object fkctl is sufficient to address the control.
Simply do:
Select Case fkctl.Value
and it will work.
You could do
frm(fkctl.Name)
but it would be kinda silly.
Thanks, I realized I didn't need to declare the form. I declare the control, and when I call the function, the form is . Here is the final solution:
Public Function -
Public Function SmokerStatus(fkctl As Control, dtctrl As Control)
Dim Msg, Style, Title, Response
'ID Combo
'1 = not ok
'2 = ok
'3 = not ok
Msg = "You have changed the option status" & vbCrLf & _
"to a status that doesn't require an option date." & vbCrLf & _
"The corresponding option date has a value. " & vbCrLf & _
"If you would like to delete that date, click yes, otherwise click no, and we will undo."
Style = vbYesNo
Title = "Option Date Only Applies to PAST Option Status"
Select Case fkctl
Case 2
dtctrl.Locked = False
dtctrl.BackColor = RGB(255, 255, 255)
Case Else
If Nz(dtctrl, "") = "" Then
dtctrl.Locked = True
dtctrl.BackColor = RGB(214, 214, 214)
Else
Response = MsgBox(Msg, Style, Title)
If Response = vbYes Then
dtctrl.Value = ""
dtctrl.Locked = True
dtctrl.BackColor = RGB(214, 214, 214)
Else
fkctl.Undo
End If
End If
End Select
dtctrl.Requery
End Function
Here is an example of calling it -
OptionStatus Me.FKOptionCombo, Me.dtOptionDate
Hope this helps others.

Static Variables in VBA

I have an excel workbook where the user imports text files into a "Data Importation Sheet". The number of files imported is dependent on how files the user wants to import. So far my workbook works great but I have hit one bump. When the user imports a file an identifier (i.e. 1, 2, 3, etc) gets assigned to that data set. Then the user selects an option from a dropdown box and calculations and plots will automatically be produced. The user also has the option to "Clear all data" where when this is selected all worksheets are cleared and if the user imports new files (after clicking the "clear all data" button) the identifier value restarts at 1. Here is my code for the identifier/counting how many files have been imported..
Public Sub Macro(Optional reset As Boolean = False)
Static i As Integer
If reset Then
i = -1
i = i + 1
Exit Sub
End If
i = i + 1
Worksheets("Hidden").Cells(i + 1, 1).FormulaR1C1 = "=" & i
Worksheets("Hidden").Cells(2, 2).FormulaR1C1 = "=" & i
End Sub
The problem I have ran into now is data will need to be imported into this sheet at a later date so when I save this file and reopen it then import more files the identifier/count for file imports restarts at 1 which I do not want to happen. I want to be able to just keep adding more files and have the code continue, I do not want to have to clear all the imported data and restart. Any ideas as to how I can do this? TIA.
I'd create a standalone function to manage the sequence. Store the value in a Workbook Name entry.
Note - if you had to manage multiple sequences you could promote the name of the sequence to a parameter instead of using a Constant within the Function.
Function NextSequence(Optional reset As Boolean = False)
Const COUNTER_NAME As String = "NM_COUNTER"
Dim nm As Name, i
On Error Resume Next
'is the name already created?
Set nm = ThisWorkbook.Names(COUNTER_NAME)
On Error GoTo 0
If nm Is Nothing Then
'not there yest - create it...
Set nm = ThisWorkbook.Names.Add(COUNTER_NAME, 0)
End If
If Not reset Then
i = Evaluate(nm.RefersTo)
i = i + 1
nm.RefersTo = i
Else
nm.RefersTo = 0
i = 0 '<< or 1 if you want NextSequence(True) to
' return the first sequence value
End If
NextSequence = i
End Function
Usage:
Public Sub Macro(Optional reset As Boolean = False)
Dim i
i = NextSequence(reset)
If reset Then Exit Sub
With Worksheets("Hidden")
.Cells(i + 1, 1).Value = i
.Cells(2, 2).Value = i
End With
End Sub
A quick fix for this would be to store the value of the identifier/count inside a cell and hide/lock the cell. The value inside the cell won't change upon restart yet you still can manipulate it inside VBA.
Very quick feel of how it should look like (probably innacurate as I don't have every info I need.)
Public Sub Macro(Optional reset As Boolean = False)
Static i As Integer
i = ActiveWorkBook.Sheets("Ressource").Range("A1").Value
If reset Then
i = -1
i = i + 1
Exit Sub
End If
i = i + 1
Worksheets("Hidden").Cells(i + 1, 1).FormulaR1C1 = "=" & i
Worksheets("Hidden").Cells(2, 2).FormulaR1C1 = "=" & i
End Sub
You could also create a CustomDocumentProperty to save the sequence number. You can pass a boolean to the method to reset:
Lastly, a helper function will check if the property exists, in order to be added if not.
Public Sub SequenceNumber(Optional ByVal Reset As Boolean = False)
If Not PropertyExists("Identifier") Then
ThisWorkbook.CustomDocumentProperties.Add Name:="Identifier", _
LinkToContent:=False, _
Type:=msoPropertyTypeNumber, _
Value:=0
End If
Dim p As Object
Set p = ThisWorkbook.CustomDocumentProperties("Identifier")
If Reset Then p.Value = 0 Else p.Value = p.Value + 1
End Sub
'Property Exists?
Private Function PropertyExists(ByVal propertyName As String) As Boolean
Dim p As Object
For Each p In ThisWorkbook.CustomDocumentProperties
If p.Name = propertyName Then
PropertyExists = True
Exit Function
End If
Next p
End Function
To call it:
SequenceNumber
SequenceNumber Reset:=True

Remove warning BC42108 during rebuild

This is the first bit of my VB.NET code:
Private Function CreateEventMWB(pWeek As XmlNode, sEventSettings As GoogleEventSettings, strEventType As String, ByRef rbCreated As Boolean) As My_GoogleEvent
Dim sEvent As My_GoogleEvent
sEvent.strTitle = pWeek.Attributes(kEventTitle).Value()
sEvent.strDate = pWeek.Attributes(kDate).Value()
sEvent.strTime = pWeek.Attributes(kStartTime).Value()
sEvent.strLocation = GetXmlNodeText(pWeek, kSpecialEventLocation)
sEvent.iMeetingDuration = 105 ' 1h 45m
sEvent.bSpecialEvent = GetXmlNodeBoolean(pWeek, kSpecialEvent)
sEvent.bNoMeeting = GetXmlNodeBoolean(pWeek, kNoMeeting)
sEvent.bFirstWeekOfMonth = GetXmlNodeBoolean(pWeek, kFirstWeek)
sEvent.strTag1 = strEventType
sEvent.strTag2 = ""
sEvent.strEventDetails = ""
rbCreated = False
If (Not sEvent.bNoMeeting) Then
' It does not matter if this is a special event (like a circuit visit meeting)
' as it is still a meeting
rbCreated = True
ElseIf (Not sEventSettings.bExcludeSpecialEvents) Then
' We already know there is no meeting for this date.
' Therefore it is going to be a special event. But are we including special events? - Yes!
rbCreated = True
End If
If (rbCreated) Then
' We have one of two scenariors
' 1. It is a normal meeting or a special meeting
' 2. It is a special event (if they were not excluded)
Dim bCreateAsMeeting = True
If (sEvent.bSpecialEvent And sEvent.bNoMeeting) Then
bCreateAsMeeting = False
End If
If (bCreateAsMeeting) Then
AddToEventDetails(sEvent, "IDS_TPL_MWB_GOOGLE_NOTE", sEventSettings.dictLabels, GetXmlNodeText(pWeek, kNote))
The variable sEvent gets correctly filled. The program works - always has done. But the last call in the code AddToEventDetails raises a warning when I compile:
I am not sure exactly how I can prevent the warning.
Thank you.