This is for a project in MS Access 2016. I'd like to implement a radio button toggle across all records displaying in a continuous form. only one record can have the toggle "on" so when it's clicked it has to reset the previous record's flag to off. I'm only finding help on radio button usage to select from multiple values of a field for one record, usually on single form. Can this be done?
Radio button must be bound to a yes/no field then use UPDATE action SQL to make sure all records except current have field set to 0. Need a unique record identifier field such as autonumber.
Private Sub Option29_Click()
CurrentDb.Execute "UPDATE tablename SET fieldname = 0 WHERE ID <>" & Me.ID
End Sub
Be aware that in a multi-user database users can conflict with each other and another solution will be needed. Depends what you need to do with the selected record.
Use the RecordSetClone of the form:
Private Sub Active_AfterUpdate()
Dim Records As DAO.Recordset
Me.Dirty = False
If Me!Active.Value = True Then
Set Records = Me.RecordsetClone
Records.MoveFirst
While Not Records.EOF
If Records!Id.Value <> Me!Id.Value Then
If Records!Active.Value = True Then
Records.Edit
Records!Active.Value = False
Records.Update
End If
End If
Records.MoveNext
Wend
Records.Close
End If
End Sub
You can do it in easy way:
1- Add to your table a new Yes/No Field ( e.g named CurrentRec)
2- Put the Radio Button and link it to this field ( e.g named rdCurrRec)
3- For the radio button Set 'Enabled' property to False and 'Locked' property to 'True'. This will disable Click - Double Click and Hover response.
4- Put the following code
Dim PreBookmark(4) As Byte 'Save old bookmark
Private Sub Form_Current()
If Not Me.NewRecord Then 'Make sure not on new record
Me.rdCurrRec = True
If (PreBookmark(0) <> 0 Or PreBookmark(1) <> 0 Or PreBookmark(2) <> 0 Or PreBookmark(3) <> 0) Then
Me.RecordsetClone.Bookmark = PreBookmark
Me.RecordsetClone.Edit
Me.RecordsetClone.Fields("CurrentRec") = False
Me.RecordsetClone.Update
End If
PreBookmark(0) = Me.Bookmark(0)
PreBookmark(1) = Me.Bookmark(1)
PreBookmark(2) = Me.Bookmark(2)
PreBookmark(3) = Me.Bookmark(3)
Else 'If new record remove bullet from previous record
If (PreBookmark(0) <> 0 Or PreBookmark(1) <> 0 Or PreBookmark(2) <> 0 Or PreBookmark(3) <> 0) Then
Me.RecordsetClone.Bookmark = PreBookmark
Me.RecordsetClone.Edit
Me.RecordsetClone.Fields("CurrentRec") = False
Me.RecordsetClone.Update
End If
End If
End Sub
Private Sub Form_Close()
'Remove the mark before exiting to avoid it appear next time to open the form in two places
If Not Me.NewRecord Then
If (PreBookmark(0) <> 0 Or PreBookmark(1) <> 0 Or PreBookmark(2) <> 0 Or PreBookmark(3) <> 0) Then
Me.RecordsetClone.Bookmark = PreBookmark
Me.RecordsetClone.Edit
Me.RecordsetClone.Fields("CurrentRec") = False
Me.RecordsetClone.Update
End If
End If
End Sub
This will not iterate all the data so for large number of records it will not delay the response as it works only on two records
The code is supposed to check if a filed "YearBigins" is empty and request an input then checks if the date value in "YearBigins" filed in older than one year and sets the value in the "counter" filed to 0. I have the following codes but does not work:
Private Sub Form_Load()
Me.Caption = Date
YearBegins_AfterUpdate
End Sub
Private Sub YearBegins_AfterUpdate()
Dim rst As Recordset
Do While Not rst.EOF
If IsNull(rst.Fields("YearBegins").Value) Then
YearBegins = InputBox("Please Enter the Beginning of counter Year Date as:
dd/mm/yyyy")
Else: YearBegins = YearBegins.Value
End If
rst.MoveNext
If (DateTime.Now - YearBegins.Value) < 365 Then
counter.Value = 0
End If
Loop
End Sub
You're missing the part where you actually update the recordset.
Private Sub Form_Load()
Me.Caption = Date
YearBegins_AfterUpdate
End Sub
Private Sub YearBegins_AfterUpdate()
Dim rst As Recordset
Do While Not rst.EOF
If IsNull(rst.Fields("YearBegins").Value) Then
YearBegins = InputBox("Please Enter the Beginning of counter Year Date as: dd/mm/yyyy")
rst.Edit '<---
rst("YearBegins") = cdate(YearBegins) '<---
rst.Update '<---
Else
YearBegins = YearBegins.Value
End If
rst.MoveNext
If (DateTime.Now - YearBegins.Value) < 365 Then
counter.Value = 0
End If
Loop
End Sub
With that said there are better ways to do this. You could open another form with only the data you want to update which will allow you to give your users some context. Just having a popup dialog with no data validation doesn't let them even really see what record they're updating. How do they know what value to put in?
It would be better if you could refactor your code in YearBegins_AfterUpdate to a new function and call that from Form_Load instead of calling an event handler like YearBegins_AfterUpdate.
I don't see where you're setting rst. You go from Dim rst As Recordset to using it but given the code you provided rst will always be null.
Hi I want to Hide or Disable the button in my 3rd column which I intend to add row every time I click it. The mess is, the previous buttons are active and can add row. How can I place the button only in the last row?
Here's my working code:
Private Sub dgAppliances_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgAppliances.CellContentClick
If e.ColumnIndex <> 2 Then
Exit Sub
Else
If Me.dgAppliances.RowCount - 1 = 20 Then
MsgBox("Maximum of 20 appliances only.")
Else
Me.dgAppliances.Rows.Add()
End If
End If
End Sub
But when I added some codes:
Private Sub dgAppliances_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgAppliances.CellContentClick
If e.ColumnIndex <> 2 Then
Exit Sub
Else
If Me.dgAppliances.RowCount - 1 = 20 Then
MsgBox("Maximum of 20 appliances only.")
Else
Me.dgAppliances.Rows.Add()
Dim cell As DataGridViewButtonCell = dgAppliances.Rows(0).Cells(2)
cell.Value = String.Empty
cell = New DataGridViewColumn()
cell.ReadOnly = True
End If
End If
End Sub
This line cell = New DataGridViewColumn() has an error.
It says, 'System.Windows.Forms.DataGridViewColumn' cannot be converted to 'System.Windows.Forms.DataGridViewButtonCell'.
Can anyone help me about this? TIA.
Here's the sample image.
You're trying to assign a DataGridViewColumn into a DataGridViewButtonCell - an entire column to a single cell.
Perhaps your intention was: cell = New DataGridViewTextBoxCell()? But even then, if you're trying to
place the button only in the last row
Then you're going to run into the problem of Adding Different DataGridView Cell Types to a Column. You could do the conversion as seen below:
Me.dataGridView1.ColumnCount = 3
Me.dataGridView1.AllowUserToAddRows = False
Me.dataGridView1.AllowUserToDeleteRows = False
Me.dataGridView1.RowCount = 1
Me.dataGridView1(2, 0) = New DataGridViewButtonCell()
Private Sub DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles dataGridView1.CellContentClick
If TypeOf Me.dataGridView1(e.ColumnIndex, e.RowIndex) Is DataGridViewButtonCell Then
If Me.dataGridView1.RowCount - 1 = 20 Then
MessageBox.Show("Maximum of 20 appliances only.")
Else
Me.dataGridView1.Rows.Add()
Me.dataGridView1(e.ColumnIndex, e.RowIndex) = New DataGridViewTextBoxCell()
Me.dataGridView1(e.ColumnIndex, e.RowIndex).[ReadOnly] = True
Me.dataGridView1(e.ColumnIndex, Me.dataGridView1.RowCount - 1) = New DataGridViewButtonCell()
End If
End If
End Sub
I would like to find the best way to have a text box displays a message everytime another control (a button) is pushed. Each time the button is pushed, the message will change and that message should show in my text box. The trick I would like to do is after the user stops pressing the button, that after a certain period (3 seconds) the text box will disappear.. (perhaps the message can be deleted). What is the correct event to use ?
Basically, for each control named 'msgPrincipio' in the code below, i would like that message to appear within the text box for 3 seconds and then disappear:
Private Sub Form_Timer()
Dim intTimerStart As Integer, intTimerUsed As Integer
Dim intCountdown As Integer
On Error GoTo Err_Handle
If Me!msgPrincipio <> "" Then
If intTimerStart > 0 Then
intTimerUsed = CLng((Timer / 60) - intTimerStart)
Else
intTimerStart = CLng(Timer / 60)
End If
If intCountdown > 3 Then
Me!msgPrincipio = ""
End If
intCountdown = intCountdown + 1
End If
Err_Exit: Exit Sub
Err_Handle: Resume Next
End Sub
Dim intTimerStart as Integer, intTimerUsed as Integer
Dim intCountdown as Integer
Sub Form_Timer()
On Error GoTo Err_Handle
If Me!MyBox <> "" Then
If intTimerStart > 0 Then
intTimerUsed = CLng((Timer / 60) - intTimerStart)
Else
intTimerStart = CLng(Timer / 60)
End If
If intCountdown > 3 Then
Me!MyBox = ""
End If
intCountdown = intCountdown + 1
End If
Err_Exit: Exit Sub
Err_Handle: Resume Next
End Sub
You also need to go to the form's design view and set the "Timer Interval" property on the form to an appropriate value. This code assumes 1,000 (1 second).
You almost never want to use Resume Next, but it's good here -- the goal is to pass through this block of code as seamlessly as possible. (Which you can accomplish with simple On Error Resume Next at the start -- but I don't like seeing it in my code that way, not one bit. I do this so I'll easily recognize it's by design, not carelessness.)
New to Access' Form Timer?
Private Sub Form_Timer()
Debug.Print Time ' Update time display.
End Sub
Put this code in the form's VBA module. Return to the form design view and switch to form view. Now go back to VBA and check your Immediate window. You should see evidence the form timer event is kicking. Note the Timer property of the form (found under form properties, design view) must not be blank or zero. It needs an entry to kick.
Using C#
using System.Windows.Forms;
public partial class Form1 : Form
{ private Timer x = new Timer();
public Form1()
{
x.Interval = (6000); //1 second = 1000
x.Tick += new EventHandler(TimerTask);
x.Start();
}
private void TimerTask(object sender, EventArgs e)
{
TextboxName.Text = String.Empty;
}
To set a label content dispear automatically: https://gamespec.tech/how-to-clear-textbox-after-few-seconds-in-c-sharp/#3-set-label-content-and-make-it-disappear-automatically
I have a form which includes a data sheet. I would like to make it possible for a user to select multiple rows, click on a button and have some sql query run and perform some work on those rows.
Looking through my VBA code, I see how I can access the last selected record using the CurrentRecord property. Yet I don't see how I can know which rows were selected in a multiple selection. (I hope I'm clear...)
What's the standard way of doing this? Access VBA documentation is somewhat obscure on the net...
Thanks!
I used the technique similar to JohnFx
To trap the Selection height before it disappears I used the Exit event of the subform control in the Main form.
So in the Main form:
Private Sub MySubForm_Exit(Cancel As Integer)
With MySubForm.Form
m_SelNumRecs = .SelHeight
m_SelTopRec = .SelTop
m_CurrentRec = .CurrentRecord
End With
End Sub
Here is the code to do it, but there is a catch.
Private Sub Command1_Click()
Dim i As Long
Dim RS As Recordset
Dim F As Form
Set F = Me.sf.Form
Set RS = F.RecordsetClone
If F.SelHeight = 0 Then Exit Sub
' Move to the first selected record.
RS.Move F.SelTop - 1
For i = 1 To F.SelHeight
MsgBox RS![myfield]
RS.MoveNext
Next i
End Sub
Here's the catch:
If the code is added to a button, as soon as the user clicks that button, the selection is lost in the grid (selheight will be zero). So you need to capture that info and save it to a module level variable either with a timer or other events on the form.
Here is an article describing how to work around the catch in some detail.
http://www.mvps.org/access/forms/frm0033.htm
Catch 2: This only works with contiguous selections. They can't select mutliple non-sequential rows in the grid.
Update:
There might be a better event to trap this, but here is a working implementation using the form.timerinterval property that i have tested (at least in Access 2k3, but 2k7 should work just fine)
This code goes in the SUBFORM, use the property to get the selheight value in the master form.
Public m_save_selheight As Integer
Public Property Get save_selheight() As Integer
save_selheight = m_save_selheight
End Property
Private Sub Form_Open(Cancel As Integer)
Me.TimerInterval = 500
End Sub
Private Sub Form_Timer()
m_save_selheight = Me.selheight
End Sub
I've tried doing something like that before, but I never had any success with using a method that required the user to select multiple rows in the same style as a Windows File Dialog box (pressing Ctrl, Shift, etc.).
One method I've used is to use two list boxes. The user can double click on an item in the left list box or click a button when an item is selected, and it will move to the right list box.
Another option is to use a local table that is populated with your source data plus boolean values represented as checkboxes in a subform. After the user selects which data they want by clicking on checkboxes, the user presses a button (or some other event), at which time you go directly to the underlying table of data and query only those rows that were checked. I think this option is the best, though it requires a little bit of code to work properly.
Even in Access, I find sometimes it's easier to work with the tables and queries directly rather than trying to use the built-in tools in Access forms. Sometimes the built-in tools don't do exactly what you want.
A workaround to the selection loss when the sub form loses the focus is to save the selection in the Exit event (as already mentioned by others).
A nice addition is to restore it immediately, using timer, so that the user is still able to see the selection he made.
Note: If you want to use the selection in a button handler, the selection may not be restored already when it executes. Make sure to use the saved values from the variables or add a DoEvents at the beginning of the button handler to let the timer handler execute first.
Dim m_iOperSelLeft As Integer
Dim m_iSelTop As Integer
Dim m_iSelWidth As Integer
Dim m_iSelHeight As Integer
Private Sub MySubForm_Exit(Cancel As Integer)
m_iSelLeft = MySubForm.Form.SelLeft
m_iSelTop = MySubForm.Form.SelTop
m_iSelWidth = MySubForm.Form.SelWidth
m_iSelHeight = MySubForm.Form.SelHeight
TimerInterval = 1
End Sub
Private Sub Form_Timer()
TimerInterval = 0
MySubForm.Form.SelLeft = m_iSelLeft - 1
MySubForm.Form.SelTop = m_iSelTop
MySubForm.Form.SelWidth = m_iSelWidth
MySubForm.Form.SelHeight = m_iSelHeight
End Sub
There is another solution.
The code below will show the number of selected rows as soon as you release the mouse button.
Saving this value will do the trick.
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
MsgBox Me.SelHeight
End Sub
Use a Global variable in the form, then refer to that in the button code.
Dim g_numSelectedRecords as long
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
g_numSelectedRecords = Me.SelHeight
End Sub
Dim formRecords As DAO.Recordset
Dim i As Long
Set formRecords = Me.RecordsetClone
' Move to the first record in the recordset.
formRecords.MoveFirst
' Move to the first selected record.
formRecords.Move Me.SelTop - 1
For i = 1 To numSelectedRecords
formRecords.Edit
formRecords.Fields("Archived") = True
formRecords.Update
formRecords.MoveNext
Next i
Why not use an array or recordset and then every time the user clicks on a row (either contiguous or not, save that row or some identifier into the recordset. Then when they click the button on the parent form, simply iterate the recordset that was saved to do what you want. Just don't forget to clear the array or recordset after the button is clicked.?
Another workaround to keeping the selection while attempting to execute a procedure - Instead of leaving the datasheet to activate a button, just use the OnKeyDown event and define a specific keycode and shift combination to execute your code.
The code provided by JohnFx works well. I implemented it without a timer this way (MS-Access 2003):
1- Set the Form's Key Preview to Yes
2- put the code in a function
3- set the event OnKeyUp and OnMouseUp to call the function.
Option Compare Database
Option Explicit
Dim rowSelected() As String
Private Sub Form_Load()
'initialize array
ReDim rowSelected(0, 2)
End Sub
Private Sub Form_Current()
' if cursor place on a different record after a selection was made
' the selection is no longer valid
If "" <> rowSelected(0, 2) Then
If Me.Recordset.AbsolutePosition <> rowSelected(0, 2) Then
rowSelected(0, 0) = ""
rowSelected(0, 1) = ""
rowSelected(0, 2) = ""
End If
End If
End Sub
Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer)
rowsSelected
If KeyCode = vbKeyDelete And Me.SelHeight > 0 Then
removeRows
End If
End Sub
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
rowsSelected
End Sub
Sub rowsSelected()
Dim i As Long, rs As DAO.Recordset, selH As Long, selT As Long
selH = Me.SelHeight
selT = Me.SelTop - 1
If selH = 0 Then
ReDim rowSelected(0, 2)
Exit Sub
Else
ReDim rowSelected(selH, 2)
rowSelected(0, 0) = selT
rowSelected(0, 1) = selH
rowSelected(0, 2) = Me.Recordset.AbsolutePosition ' for repositioning
Set rs = Me.RecordsetClone
rs.MoveFirst ' other key touched caused the pointer to shift
rs.Move selT
For i = 1 To selH
rowSelected(i, 0) = rs!PositionNumber
rowSelected(i, 1) = Nz(rs!CurrentMbr)
rowSelected(i, 2) = Nz(rs!FutureMbr)
rs.MoveNext
Next
Set rs = Nothing
Debug.Print selH & " rows selected starting at " & selT
End If
End Sub
Sub removeRows()
' remove rows in underlying table using collected criteria in rowSelected()
Me.Requery
' reposition cursor
End Sub
Private Sub cmdRemRows_Click()
If Val(rowSelected(0, 1)) > 0 Then
removeRows
Else
MsgBox "To remove row(s) select one or more sequential records using the record selector on the left side."
End If
End Sub