In MS Access VBA determine the next control to have focus - forms

Understanding the order of operations, is there a way for an OnExit or OnLostFocus script to know what control was clicked to take that focus?
I have a form that has a list box from which you have to select an item for the the rest of the data entered into the form to be saved, so that control has the focus when you open the form. If you click on any other control without selecting an item from the list, an OnExit gives you a warning that you haven't made a selection.
The problem that creates is, should you open the form and then decide you don't want it, you get that message when you close the form (either with the close button on the form or the built-in closer).
I would like to be able to tell the OnExit that either the close button has been clicked or the form itself is closing, so don't show the no entries message.
Any thoughts would be appreciated.

Not that I know of.
If your form is rather simple(not to many controls.) Move the warning out of the onExit to a sub. Call it from the other controls when they get focus. But not from the Close button.
Dim bDidUserSelectSomething As Boolean
'In the list box
Private sub ListBox1_Click()
'Do some code here
'Set the flag that they did something
bDidUserSelectSomething = True
End sub
'When you enter the other controls, check if they have done what they should have.
Private Sub TextBox1_Enter()
If bDidUserSelectSomething = False Then
'Warning
End If
End Sub

Consider altering your warning check.
Instead of an OnExit() Event of the listbox, add a validation in all other pertinent controls' BeforeUpdate() events. Usually this event is where validation checks are made.
First, build a general function behind the form that calls your warning:
Public Function WarningMessage() As Boolean
If IsNull(Me.Listbox) Then
Msgbox "Please first select an option in listbox.", vbExclamation, _
"MISSING LISTBOX SELECTION"
WarningMessage = True
Else
WarningMessage = False
End If
End Function
Then call the function to each control:
Private Sub cntr1_BeforeUpdate(Cancel As Integer)
Cancel = WarningMessage
End Sub
Private Sub cntr2_BeforeUpdate(Cancel As Integer)
Cancel = WarningMessage
End Sub
...
As you can see from above, if the listbox is null then the message appears and returns True which is passed back to the control's BeforeUpdate() subroutine to cancel the update.
Now, if there are too many controls to make this doable. Consider hiding all relevant controls in Form's OnCurrent() event except the listbox. When listbox is updated, render the other controls visible. Many softwares and web applications run dynamically like this to prevent users from missing important fields.
Private Sub Form_Current()
If IsNull(Me.listbox) Then
Me.ctnrl1.Visible = False
Me.ctnrl2.Visible = False
Me.ctnrl3.Visible = False
...
Else
Me.ctnrl1.Visible = True
Me.ctnrl2.Visible = True
Me.ctnrl3.Visible = True
...
End if
End Sub
Private Sub listbox_AfterUpdate()
Me.ctnrl1.Visible = True
Me.ctnrl2.Visible = True
Me.ctnrl3.Visible = True
...
End Sub

Related

React-Bootstap-Typeahead: Manually set custom display value in onChange() upon menu selection

In the onChange of React-Bootstrap-Typeahead, I need to manually set a custom display value. My first thought was to use a ref and do something similar to the .clear() in this example.
But although .clear() works, inputNode.value = 'abc' does not work, and I'm left with the old selected value from the menu.
onChange={option => {
typeaheadRef.current.blur(); // This works
typeaheadRef.current.inputNode.value = 'abc'; // This does not work (old value is retained)
}}
I also tried directly accessing the DOM input element, whose ID I know, and doing
var inputElement = document.querySelector('input[id=myTypeahead]');
inputElement.value = 'abc';
But that didn't work either. For a brief second, right after my changed value = , I do see the new display label, but then it's quickly lost. I think the component saves or retains the menu-selected value.
Note: I cannot use selected, I use defaultSelected. I have some Formik-related behavior that I've introduced, and it didn't work with selected, so I'm stuck with defaultSelected.
The only workaround I found is to re-render the Typeahead component (hide and re-show, from a blank state) with a new defaultSelected="abc" which is a one-time Mount-time value specification for the control.
I couldn't get selected=.. to work, I have a wrapper around the component which makes it fit into Formik with custom onChange and onInputChange and selected wasn't working with that.
So the simple workaround that works is, if the visibility of the Typeahead depends on some condition (otherwise it won't be rendered), use that to momentarily hide and re-show the component (a brand new repaint) with a new defaultSelected, e.g.
/* Conditions controlling the visibility of the Typeahead */
!isEmptyObject(values) &&
(values.approverId === null || (values.approverId !== null && detailedApproverUserInfo)
)
&&
<AsyncTypehead defaultSelected={{...whatever is needed to build the string, or the literal string itself...}}
..
// Given the above visibility condition, we'll hide/re-show the component
// The below will first hide the control in React's renders
setFieldValue("approver", someId);
setDetailedUserInfo(null);
// The below will re-show the control in React's renders, after a small delay (a fetch)
setDetailedUserInfo(fetchDetailedUserInfo());

Trap onClose event for instantiated form object

How do you trap the onClose or onUnload events for a manually instantiated form object?
I notice that if (within my controller class), I dimension an Access.Textbox or Access.CommandButton object using the WithEvents keyword, then the Access VBA IDE will automatically show that object, and its available events in the comboboxes immediately above the code window.
If however, I dimension a Form object, or a custom Form Object using the WithEvents keyword, there is no object nor available events listed.
Form_Popup_Credentials (Form Code):
Public Event CustomUnLoad()
Private Sub Form_Unload(Cancel As Integer)
RaiseEvent CustomUnLoad
End Sub
FormController Class:
Dim WithEvents Loginfrm As Form_Popup_Credentials
Dim WithEvents Loginbtn As Access.CommandButton
Public Sub GetCredentials()
' If the Popup Credential form is not instantiated,
' then bring it into existence
If Loginfrm Is Nothing Then
Set Loginfrm = New Form_Popup_Credentials
End If
' Set up a reference to the "Login" button
Set Loginbtn = Loginfrm.SignInButton
' Set up the Login Button Click() event
Loginbtn.OnClick = "[Event Procedure]"
' Set up the Login Form's events
Loginfrm.OnUnload = "[Event Procedure]"
Loginfrm.OnClose = "[Event Procedure]"
' Show the form and give it focus
Loginfrm.Visible = True
Loginfrm.SetFocus
End Sub
' Fires Correctly
Private Sub Loginbtn_Click()
MsgBox "Login Button was Clicked"
' If I uncomment this, then the CustomUnLoad() event fires
' DoCmd.Close acForm, Loginfrm.Name, acSavePrompt
Set Loginbtn = Nothing
Set Loginfrm = Nothing
End Sub
' Doesn't Fire
Private Sub Loginfrm_onClose()
MsgBox "Login Form onClose() fired"
Set Loginfrm = Nothing
Set Loginbtn = Nothing
End Sub
' Doesn't Fire
Private Sub Loginfrm_Close()
MsgBox "Login Form Close() fired"
Set Loginfrm = Nothing
Set Loginbtn = Nothing
End Sub
' Doesn't Fire
Private Sub Loginfrm_onUnload()
MsgBox "Login Form onUnload() fired"
Set Loginfrm = Nothing
Set Loginbtn = Nothing
End Sub
' Doesn't Fire
Private Sub Loginfrm_Unload()
MsgBox "Login Form Unload() fired"
Set Loginfrm = Nothing
Set Loginbtn = Nothing
End Sub
' This fires if the user clicks the X button to close the form,
' but not if the controller unloads the Loginfrm object
Private Sub Loginfrm_CustomUnLoad()
MsgBox "Login Form CustomUnLoad() fired"
Set Loginfrm = Nothing
Set Loginbtn = Nothing
End Sub
You have to declare your class level form variable as Form (or more explicit Access.Form) to get the standard events of Access forms:
Dim WithEvents Loginfrm As Access.Form
BTW, depending on the Access version it can be vital to make sure to set the references in your controller class to Nothing on Class_Terminate.

Access VBA Form Controls Disabled

I have a book database and a simple form to browse through it and edit or create new records. I have a 'Go to first', 'Go to previous', 'Go to next', and a 'Go to last' button. What I was wanting, was for the 'first' and 'previous' to be disabled when the user was viewing the first record, and the 'next' and 'last' disabled when viewing the last record.
I'm using this code in the 'On Current' event for the form:
Private Sub Form_Current()
If Me.CurrentRecord = 1 Then
Me.Prev_Rec.Enabled = False
Me.First_Rec.Enabled = False
Else
Me.Prev_Rec.Enabled = True
Me.First_Rec.Enabled = True
End If
If Me.CurrentRecord = Me.RecordsetClone.RecordCount Then
Me.Next_Rec.Enabled = False
Me.Last_Rec.Enabled = False
Else
Me.Next_Rec.Enabled = True
Me.Last_Rec.Enabled = True
End If
End Sub
When I open up the form (it opens to the first record), all the buttons are grayed out; however, if I fiddle with the controls (just clicking them and/or moving them) in Design mode, and then switch back, the 'next' and 'last' buttons are enabled again (I'm on the first record, so the 'first' and 'previous' buttons are disabled, as they should be). What am I doing wrong?
I suspected the initial RecordCount was not reliable because the form had not yet loaded all the rows into its recordset when you first checked RecordCount.
You were able to force the recordset to finish loading before you checked RecordCount by inserting this as the first line of the procedure ...
Me.RecordsetClone.MoveLast

Validate only parts of a form

I have a rather large form, that I've broken up into individual sections with "Next" and "Back" buttons for the user to navigate each step.
Here is how I set it up to work for now:
Each section is within its own xp:panel
Each xp:panel is hidden with "display:none". The transition happens when the user clicks on "Next" or "Back" by using JQuery's fade animation
What I am trying to do is, if the user clicks on "Next" I would like to validate only the fields in the current, visible section. If the validation fails, don't transition to the next step. If the validation passes, transition to the next step.
Right now, when I click on the "Next" button, every field is being validated and the transition doesn't happen.
Is there a way, that I can only validate the fields in a certain section or would I have to use something like Don Mottolo's code snippet: http://openntf.org/XSnippets.nsf/snippet.xsp?id=ssjs-form-validation-that-triggers-errormessage-controls
Thank you for your help!
P.S.: I know I could use the CSJS portion of the onClick event of the button to run some validation, but I'd like to use the "Display Error" controls.
You could look at computing the required attribute of each control that is to be validated and have that check which button is submitting the form. Tommy Vaaland has a function that does this:
// Used to check which if a component triggered an update
function submittedBy( componentId ){
try {
var eventHandlerClientId = param.get( '$$xspsubmitid' );
var eventHandlerId = #RightBack( eventHandlerClientId, ':' );
var eventHandler = getComponent( eventHandlerId );
if( !eventHandler ){ return false; }
var parentComponent = eventHandler.getParent();
if( !parentComponent ){ return false; }
return ( parentComponent.getId() === componentId );
} catch( e ){ /*Debug.logException( e );*/ }
}
Link: http://dontpanic82.blogspot.co.uk/2010/03/xpages-making-validation-behave.html
You can look at using a client side form validation framework such as Parsley: http://parsleyjs.org
This can of course be combined with server side validation for at least the final submission.

How do I make PDF the default export option for a Crystal Report?

I am working with CrystalDecisions.CrystalReports.Engine.ReportDocument in WinForms in Visual Studio 2008. Right now when the users click the export button the dialog defaults to saving the report as a CrystalReports formatted file. It's possible to change the selector to PDF, but the specific request that I've been given -- and I've searched for too many hours trying to find -- is to make the 'export report' dialog default to PDF format option.
Does anyone know how to do this?
As of CR XI, the only way I know is to replace the export dialog with your own. You can add your own button to the CrystalReportViewer control and hide their export button.
Here's vb.net code to replace the export button with your own button/eventhandler...
Public Shared Sub SetCustomExportHandler(ByVal crv As CrystalDecisions.Windows.Forms.CrystalReportViewer, ByVal export_click_handler As EventHandler)
For Each ctrl As Control In crv.Controls
'find the toolstrip
If TypeOf ctrl Is ToolStrip Then
Dim ts As ToolStrip = DirectCast(ctrl, ToolStrip)
For Each tsi As ToolStripItem In ts.Items
'find the export button by it's image index
If TypeOf tsi Is ToolStripButton AndAlso tsi.ImageIndex = 8 Then
'CRV export button
Dim crXb As ToolStripButton = DirectCast(tsi, ToolStripButton)
'clone the looks of the export button
Dim tsb As New ToolStripButton
With tsb
.Size = crXb.Size
.Padding = crXb.Padding
.Margin = crXb.Margin
.TextImageRelation = crXb.TextImageRelation
.Text = crXb.Text
.ToolTipText = crXb.ToolTipText
.ImageScaling = crXb.ImageScaling
.ImageAlign = crXb.ImageAlign
.ImageIndex = crXb.ImageIndex
End With
'insert custom button in it's place
ts.Items.Insert(0, tsb)
AddHandler tsb.Click, export_click_handler
Exit For
End If
Next
Exit For
End If
Next
'hide the default export button
crv.ShowExportButton = False
End Sub
Then in the click handler you'd show a customized SaveFileDialog and eventually call the ReportDocument.ExportToDisk method. This way you can set the dialog's title and filename to something useful and of course set the default export type.