Disabling multi-line fields in MS Access - ms-access

Is there a way to disable entering multi-line entries in a Text Box (i.e., I'd like to stop my users from doing ctrl-enter to get a newline)?

I was able to do it on using KeyPress event.
Here's the code example:
Private Sub SingleLineTextBox_ KeyPress(ByRef KeyAscii As Integer)
If KeyAscii = 10 _
or KeyAscii = 13 Then
'10 -> Ctrl-Enter. AKA ^J or ctrl-j
'13 -> Enter. AKA ^M or ctrl-m
KeyAscii = 0 'clear the the KeyPress
End If
End Sub

The way I did it before (and the last time I worked in Access was around '97 so my memory is not so hot) was raising a key-up event and executing a VBA function. It's a similar method to what you do with an AJAX suggest text box in a modern webform application, but as I recall it could get tripped up if your Access form has other events which tend to occur frequently such a onMouseMove over the entire form object.

Using the KeyPress event means that your code will fire every time the user types. This can lead to screen flickering and other problems (the OnChange event would be the same).
It seems to me that you should use a single event to strip out the CrLf's, and the correct event would be AfterUpdate. You'd simply do this:
If InStr(Me!MyMemoControl, vbCrLf) Then
Me!MyMemoControl = Replace(Me!MyMemoControl, vbCrLf, vbNullString)
End If
Note the use of the Access global constants, vbCrLf (for Chr(10) & Chr(13)) and vbNullString (for zero-length string).
Using a validation rule means that you're going to pop up an ugly error message to your user, but provide them with little in the way of tools to correct the problem. The AfterUpdate approach is much cleaner and easier for the users, seems to me.

Thanks Ian and BIBD. I created a public sub based on your answer that is reusable.
Public Sub PreventNewlines(ByRef KeyAscii As Integer)
If KeyAscii = 10 Or KeyAscii = 13 Then KeyAscii = 0
End Sub
Private Sub textbox_KeyPress(KeyAscii As Integer)
Call PreventNewlines(KeyAscii)
End Sub
Screen flicker should never be an issue, as these are handled events, not constant polling (and it's per control further limiting the scope). Seems to me like an invalid argument, as every text editor is executing some code per keystroke.
Thanks

not entirely sure about that one, you should be able to remove the line breaks when you render the content though, or even run a vbscript to clear it out, you just need to check for chr(13) or vbCrLf.

If you don't want an event interfering, you can set up the Validation Rule property for the textbox to be
NOT LIKE "*"+Chr(10)+"*" OR "*"+Chr(13)+"*"
You will probably also want to set the Validation Text to explain specifically why Access is throwing up an error box.

Jason's response works well. Just to add to it..
If you want to allow the user to leave the text box blank, you could use this:
Not Like ""+Chr(10)+"" Or ""+Chr(13)+"" Or Is Null

Related

MS Access 2010 VBA: mysterious compile error on custom LostFocus sub

In a data validation form, I have a subroutine checking previously-entered data on a LostFocus event by ensuring that the release time (TimeReleased in table; Me.txtTimeReleased on form) is after the capture time (ObservationTime in table; Me.txtObservationTime on form). I'm using LostFocus rather than BeforeUpdate because the data were bulk-imported into the db and are now being error-checked.
My users keep getting a compile error (Compile Error: method or data member not found) upon tabbing out of the field this sub is attached to but I cannot reproduce the problem locally. The error occurs on this line:
If (Me.txtTimeReleased) <= (Me.ObservationTime) Then
and the part highlighted is '.txtTimeReleased'
Full code block:
Private Sub txtTimeReleased_LostFocus()
Dim badData As Variant
Dim resp As Variant
'Also check that time released is after time captured
If Not IsNull(Me.txtObservationTime) And Not IsNull(Me.txtTimeReleased) Then
If (Me.txtTimeReleased) <= (Me.ObservationTime) Then
resp = MsgBox("Release time must be after capture time." & vbCrLf & "Please double check this field's value: is it correct?", _
vbYesNo + vbExclamation + vbDefaultButton2, "Release Time Before Capture Time")
If resp <> vbYes Then badData = True
End If
End If
If badData = True Then
Me.cmbTaxonId.SetFocus 'set focus away so can set focus back
With Me.txtTimeReleased
.SetFocus
.SelStart = 0
.SelLength = 10
End With
End If
End Sub
Other things to note:
Both the table field and form control are formatted as 'Short Time' (24-hour time)
There is an input mask on that form control for 24-hour time; I use input masks very rarely and thus aren't familiar with them--perhaps the input mask could be causing the problem?
There are similar LostFocus subs on most of the other controls which do not produce this (or any other) error
Things I've tried:
Checking spelling
Fully decompling and recompiling the code: starting with shift, compact and repair with shift, open with /decompile flag while holding shift, compact and repair with shift, re-open with shift, and finally compile (without error)
Replacing the form in their database with one that works fine for me on the same data
Google
Things that seem odd to me:
I can't reproduce the error locally.
The error is triggering on the second instance of
Me.txtTimeReleased rather than the first: it has already passed a Not
IsNull(Me.txtTimeReleased) check.
The fact that it's a compile error: could that be masking something else?
Thanks for your time, and please let me know if there's any additional information that would be useful. Any thoughts are most welcome!
You checked for Null txtObservationTime and txtTimeReleased, but compare then txtTimeReleased and ObservationTime. Maybe solution is:
If Not IsNull(Me.txtObservationTime) And Not IsNull(Me.txtTimeReleased) Then
If (Me.txtTimeReleased) <= (Me.txtObservationTime) Then
Opening the .mdb with the /decompile flag is one of the first things I would have suggested, but you said you already tried that.
Here's another undocumented trick to deal with "hidden" compile problems that get baked in by VBA behind the scenes:
First, make a backup copy of your .mdb just to be safe (this is, after all, an undocumented technique).
Then, save your form to a text file using SaveAsText:
SaveAsText acForm, "MyFormName", "C:\MyFormName.txt"
Finally, re-load your form using the equally undocumented LoadFromText:
LoadFromText acForm, "MyFormName", "C:\MyFormName.txt"
Compile.
Compact.
Repair.
Hope for the best.
Good luck.
I suggest you use variables:
intThat = Me.txtTimeReleased
If intThis <= intThat Then
Try using ! instead of a dot:
intThat = Me!txtTimeReleased
If intThis <= intThat Then
And now, the answer that worked for me last week:
Comment out the offending line.
Run a compile that is successful.
Restore the offending line.
The compile may work now. Don't ask me why.

Display only last characters in a textbox

I have an Access 2002 database/application where my clients can enter multiple information about their own clients, including a code which follow some rules.
However, when they view these information after they have been entered, I need to hide every characters in this code except for the 4 last characters. However, the agent needs to be able to edit this code if it needs to be modified.
So basically, I have 3 phases possible:
First time information are filled, empty data. The field must show the characters entered.
At a later date, the code must be hidden in some way to show only the last 4 characters. It can be with * or simply the last 4 characters, but the user must not be able to see what is before these.
The agent edit the code, the code must then be properly modified in the database. The characters must be shown.
I tried to show only the 4 last characters, however my database gets modified... So the code gets cut in the database.
I wrote the following function to obscure sensitive data. Its primary use is to discourage shoulder surfing. I'm not sure if it will meet your particular needs, but it is simple, straightforward and may help others who stumble upon this question.
'Use to hide data in sensitive fields (e.g., BirthDate, PhoneNum, SSN)
'Usage: Ctl OnEnter property: =ObscureInfo(False, Form.ActiveControl)
' Ctl OnExit property: =ObscureInfo(True, Form.ActiveControl)
' Form Open property: =ObscureInfo(True, [BirthDate], [HomePhone], [SSN])
Function ObscureInfo(HideIt As Boolean, ParamArray Ctls() As Variant)
Dim Ctl As Variant
For Each Ctl In Ctls
If HideIt Then
If IsNull(Ctl.Value) Then
Ctl.BackColor = vbWhite
Else
Ctl.BackColor = Ctl.ForeColor
End If
Else
Ctl.BackColor = vbWhite
End If
Next Ctl
End Function
Wow - I'm shocked that this hasn't been answered sufficiently. The best answer is to use an unbound text box in your form instead of a bound one. First you'll need to make your unbound text box populate the actual field. You'll do that in the AfterUpdate event.
Private Sub UnboundTextBox_AfterUpdate()
[MyField] = Me.UnboundTextBox
End Sub
Then you'll need to set an OnCurrent event to populate your unbound text box with the protected view whenever the agents view the record:
Private Sub Form_Current()
Me.UnboundTextBox = String(Len([MyField])-4, "*") & Right([MyField], 4)
End Sub
However, you also want to let your agents edit or view the full code later, if necessary. The best way to do this would be to set the OnEnter event for your unbound text box to pull the whole field value, so the agent can see and edit it - effectively the reverse of your OnUpdate event.
Private Sub UnboundTextBox_Enter()
Me.UnboundTextBox = Nz([Field1]) 'The Nz deals with Null errors
End Sub
I've used this with a field displaying SSN's and it works like a charm.

Can't set field value in Access form with new record

When I open my access form ActivityTracker to a new record, I want it to auto-populate the field *start_time* with the current time now().
Private Sub Form_Open(Cancel As Integer)
If IsNull(Form_ActivityEntry.Start_time) And IsNull(Form_ActivityEntry.id) Then
Form_ActivityEntry.Start_time = Now()
End If
End Sub
This throws an error "You can't assign a value to this object" and stops execution.
I can fix the error by going explicitly to a new record
Private Sub Form_Open(Cancel As Integer)
If IsNull(Form_ActivityEntry.Start_time) And IsNull(Form_ActivityEntry.id) Then
DoCmd.RunCommand acCmdRecordsGoToNew
Form_ActivityEntry.Start_time = Now()
End If
End Sub
or by
Private Sub Form_Open(Cancel As Integer)
If IsNull(Form_ActivityEntry.Start_time) And IsNull(Form_ActivityEntry.id) Then
Me.Recordset.AddNew
Form_ActivityEntry.Start_time = Now()
End If
End Sub
but either of these causes a popup warning, "You can't go to the specified record."
I've tried to suppress the warning with this
DoCmd.SetWarnings False
DoCmd.RunCommand acCmdRecordsGoToNew
DoCmd.SetWarnings True
or by setting up error handling,
On Error GoTo Err_SomeName
but I still get the warning.
AllowAdditions is set to True. Recordset type is Dynaset.
Otherwise, everything works fine with this form.
What am I doing wrong? Is there an event called "opening new record" as opposed to "open form"?
Thanks for your help!
The problem here is still no one correctly answered why the code as posted does not work.
The REASON why is that you are using the on-open event.
Unlike .net and most systems access has a GREAT design in which you have two events occur when you an open form event (which can be canceled) and an on-load event.
This great design of Access means that code to test conditions and prevent the form load can be placed in the on-open event. If you look CLOSE you will see that the on-open even has a cancel. If you set cancel = true, then the FORM WILL NOT LOAD, AND WILL NOT DISPLAY.
So, you can test for no data, or for user rights or whatever, and huts cancel the form loading. If you can cancel the form loading, then it would make little sense to allow modifying the value of bound controls – as such all bound controls are READ ONLY.
You are NOT ALLOWED to change the values of BOUND controls in the on-open event. This is TOO soon and is by DESIGN and intention of the product.
So, testing of conditions to prevent the form load goes in on-open.
This thus suggests that setup of variables,, setup of controls, setting values of controls and your basic form startup code belongs in the ON-LOAD event. On-open is too soon.
And of Couse if your code is going to cancel a form load, then it is logical that all of your forms startup and setup code SHOULD NOT and does not need to run.
In summary
On-open event:
Can set cancel = true
Code goes here to test if you wish to prevent the form from loading and being seen by the user.
On-LOAD event:
All your startup code, setting of variables, setting of controls etc. is now recommended and possible.
And this simple design in Access ALSO means that as a developer you now know where to look for the code that will prevent and cancel the form load. Of course without this basic understanding of the difference and WHY two events exist in Access then the confusing of the poster and answers given becomes obvious.
I suggest you set the default value of the control to Now() or Date(). The default value only applies to new records and once you complete any other field, the value will be assigned.
In this particular case, there is even an argument for setting the default value of the field in the table.
Please do not use Set Warnings: What's the difference between DoCmd.SetWarnings and CurrentDB.Execute
I am not a fan of moving to a new record on a form. There are too many data holes that can occur and you run into issues like yours.
I reccomend a blank, unbound form, with a textbox, calander conrol, number up down... etc for each of the fields that you want to add. This way you can type check each of the fields or do other checks against what you want. Then, when the user is happy, then add the record with an insert query.
However, for the question that you asked. It looks like you are trying to assign a value to the bound field. Try assigning the value to the object the field is bound to.

access to oldValue for control in continous form generates error 3251 in beforeUpdate when a checkbox on form is updated

This is one of the stranger issues I have seen in MS Access. I have the following code in a continuous form:
Private Sub thisForm_BeforeUpdate(Cancel As Integer)
If Not Cancel Then
Debug.Print "pre-logging data changes..."
' here we need to doublecheck to see if any values changed.
' we simply iterate through the whole list, re-setting oldValue
' and newValue.
For Each control In thisForm.Section(acDetail).controls
If control.ControlType = acTextBox Or _
control.ControlType = acComboBox Or _
control.ControlType = acListBox Or _
control.ControlType = acOptionGroup Or _
control.ControlType = acCheckBox Then
Debug.Print control.Name
oldValues(control.Name) = control.oldValue
newValues(control.Name) = control.value
End If
Next
End If
End Sub
oldValues and newValues are Dictionary objects (although likely not related to the issue).
My form has 3 textbox controls, and a checkbox control. One of the text box controls is disabled, and is populated via the results of a simple inner join (to get the human readable name associated with a foreign key). The data source comes from the form's recordsource (no DLookup or anything is used).
If I edit one of the other two textbox controls, this code runs absolutely fine. HOWEVER, if I toggle the checkbox on the form, i get a runtime error 3251. In the watches window, I get the error again when i try to view the properties of "control". It shows the value of oldValue for the disabled control to be "Reserved Error".
If it did this consistently, I would think it was due to the control being disabled; but since it works without a problem when the other textboxes receive edits, and only breaks when the checkbox is toggled; I am stumped. I'm almost inclined to believe I found a bug in access, but I could use some extra input.
Anyone else every encounter an issue like this?
EDIT: Upon digging further, I found that in actuality only one of the 3 editable fields will not trigger this error. It holds string data. The other two controls hold a date value, and a yes/no value. Now I am even more confused.
i've got two ideas to that issue.
First one: If the RecordSource of your Form is an ODBC-Table thats linked to a SQL-Server then you should set a standard value for the CheckBox-Column. Otherwise it will try to set NULL to False and throw an error saying that somebody else edited the current record.
Second idea: Sometimes Access just has a little "hiccup" when it compiles the code. You could make a backup of your database and then try to decompile it using "C:\Program Files\Microsoft Office 2007\Office12\MSACCESS.EXE" "C:\yourFolder\yourDatabase.accdb" /decompile in the Run... Window (of course you have to insert the Path as it is on your machine). That often helps solving strange Problems.

Auto capitalize letters in bounded forms

i have a bounded, continuous form with some textbox that users can modify.
I want to use some code to auto capitalize what ever the users typed, like
Private Sub textbox_BeforeUpdate(Cancel As Integer)
Me.textbox = UCase(Me.textbox)
End Sub
but it causes error... Any suggestions?
And I noticed that in a bounded, continuous form, I can't move the pointer in its recordset.
Me.Recordset.MoveNext
This causes error (number 3462). Why?
Put the UCase expression in the text box control's After Update event.
Private Sub textbox_AfterUpdate()
Me.textbox = UCase(Me.textbox)
End Sub
As for the second question, this works without error for a command button on my form.
Private Sub cmdMoveNext_Click()
Me.Recordset.MoveNext
End Sub
This is what I get in the Immediate Window for your error number.
? AccessError(3462)
Failure to load a DLL.
Is that the error text you see? If so, I don't know the cause, but I would wonder if your database is corrupt. Can you re-create a simplified version of this form in a new database to see whether it works there? If it also fails then, perhaps your Access installation is broken.
Frankly, I'm just grasping at straws. This seems very unusual to me.
For the first question:
Use the Keypress event to check for lower case letter and then return it's upper case counter-part:
Private Sub textbox_KeyPress(KeyAscii As Integer)
Const ASCII_LOWER_A = 97
Const ASCII_LOWER_Z = 122
Const UPPER_MODIFIER = -32
If KeyAscii >= ASCII_LOWER_A And KeyAscii <= ASCII_LOWER_Z Then
KeyAscii = KeyAscii + UPPER_MODIFIER
End If
End Sub
UPDATE: Since I received criticism and a downvote for my answer, I will elaborate on when and why you might use the code above.
First, let me say that the *After_Update* event fires after the control tied to that event loses focus (either via tab or actually moving to the next record). *After_Update* also recognizes a paste if the user cuts and pastes text; admittedly, my code does not. If your only concern is storing the text in the database in upper case, by all means use *After_Update* to UCase the value.
However, there is one advantage that *KEY_PRESS* press will give you: the ability to correct the letter pressed in real time.
To the purist developer, this event may seem wasteful. However, anyone who’s received a paycheck from a company should recognize that keeping their paying client happy is imperative. If that paid client says “I want to see capital letters when the user starts typing”, then you would use the code above. It will have little (if any) impact on how the application behaves and will meet your boss's requirement.
In fact, maybe the client says “Hey, this field is for putting in serial numbers for our inventory. All of products’ serial numbers use zeros, not the letter O. So please make sure they do not enter O for this field.”
Now *KEY_PRESS* has an additional use. You could either negate the letter O being pressed or you can replace the letter O getting pressed by the value you need:
Private Sub TextBox_KeyPress(KeyAscii As Integer)
Const ASCII_LOWER_A = 97
Const ASCII_LOWER_Z = 122
Const UPPER_MODIFIER = -32
Const UPPER_O = 79
Const LOWER_O = 111
Const ZERO_ASCII = 48
Select Case KeyAscii
Case UPPER_O, LOWER_O
KeyAscii = 0 'Cancel it out
'KeyAscii = ZERO_ASCII 'OR Replace it with the number zero
Case ASCII_LOWER_A To ASCII_LOWER_Z
KeyAscii = KeyAscii + UPPER_MODIFIER 'The old familiar code that will capitalize all other letters
Case Else
'Whatever other validations you want to do on the textbox here.
End Select
End Sub
So what about cutting and pasting invalid values? That is what Form_BeforeUpdate event is for. You can check all values before you save and set Cancel = True if something doesn’t jive.
It’s a very useful event and completly applicable if you want to UCase letters in real time.
Have you tried putting it in the TextBox1_Change Sub ???
Try
Private Sub textbox_BeforeUpdate(Cancel As Integer)
Dim strBuffer As String
strBuffer = Me.textbox
Me.textbox.Focus
Me.textbox = UCase(strBuffer)
End Sub