Copy paste rows meeting conditions stated from a multiple selection in a drop-down list (with no repetition) - excel

I have:
1) one worksheet named "Data" for my products data base;
2) one worksheet named "Quotation ENG" for products quotations based on selected products from the data base;
3) one worksheet named "Manager" with dropdown lists to make the criteria selections.
Then, I have two pieces of code running fine independently.
One named Sub Quote is for copy-pasting part of the rows from my database to the quotation sheet when a criterion is met,
And one named Sub Worksheet_Change (credits: TrumpExcel) is for enabling multiple selections in a dropdown list.
I'm quite clueless on how to change the code of my Sub Quote module to make the copy-paste operation possible if the dropdown lists enable multiple criteria. ANy guidance is welcome :)
Sub Quote()
Dim Source As Worksheet
Dim Target As Worksheet
Dim Company As String
Dim InfoA As String
Dim Finalrow As Integer
Dim I As Integer
Set Source = Worksheets("Data")
Set Target = Worksheets("Quotation ENG")
Company = Worksheets("Manager").Range("E5").Value 'Where one dropdown list is located.
InfoA = Worksheets("Manager").Range("E7").Value 'Where one dropdown list is located.
Source.Select
Finalrow = Cells(Rows.Count, 1).End(xlUp).Row
For I = 2 To Finalrow
If Cells(I, 1) = Company And Cells(I, 2) = InfoA Then
Source.Range(Cells(I, 16), Cells(I, 23)).Copy Target.Range("A200").End(xlUp).Offset(1, 0).Resize(1, 8)
End If
Next I
Target.Select
Range("A1").Select
End Sub
=============================================================
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Address = "$E$5" Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
If InStr(1, Oldvalue, Newvalue) = 0 Then
Target.Value = Oldvalue & ", " & Newvalue
Else:
Target.Value = Oldvalue
End If
End If
End If
End If
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True
End Sub

I refactored some parts of your code and turned the company variable into an array so it can store several values. Please read the comments inside the code.
As a suggestion, try to use Excel structured tables to store your data. It's gonna be easier to work with them in the future.
Replace your current Quote Sub for this:
Sub Quote()
Dim Source As Worksheet
Dim Target As Worksheet
Dim Company() As String ' Converted the company variable to an array
Dim InfoA As String
Dim Finalrow As Integer
Dim counter As Integer
Dim I As Integer
Set Source = Worksheets("Data")
Set Target = Worksheets("Quotation ENG")
Company = Split(Worksheets("Manager").Range("E5").Value, ",") 'Where one dropdown list is located.
InfoA = Worksheets("Manager").Range("E7").Value 'Where one dropdown list is located.
' Added the source sheet and removed the select as it slows down your code
Finalrow = Source.Cells(Rows.Count, 1).End(xlUp).Row
' Loop through each company contained in the array
For counter = 0 To UBound(Company)
' Loop through each data row
For I = 2 To Finalrow
' Added Company(counter) so you can access each array element and wrapped it with trim to delete extra spaces
If Source.Cells(I, 1) = Trim(Company(counter)) And Source.Cells(I, 2) = InfoA Then
Source.Range(Source.Cells(I, 16), Source.Cells(I, 23)).Copy Target.Range("A200").End(xlUp).Offset(1, 0).Resize(1, 8)
End If
Next I
Next counter
' Activate worksheet
Target.Activate
' Refer to the object full path
Target.Range("A1").Select
End Sub
Let me know if it works.

Related

Target cells not triggered by event `Worksheet_Change`. How to fix?

I am using below codes as the following:
Code(1)# Worksheet_SelectionChange Insert Date by using Date Picker(calendar) on sheet "North"
Column M.
Code(2) # Worksheet_Change of sheet North to Log changes of any cells and put in sheet("Log").
Code(3) in a separate module "Calendar" to initiate calendar
the codes works except in one condition
Target cells not triggered by event Worksheet_Change
to produce issue use calendar to enter any value but not click outside Column M then delete these values again , then switch to sheet "Log" you will notice that there are no entries for deleted values at all.
As always: any help will be appreciated.
(Link for the real file found in first comment)
Option Explicit
Option Compare Text
Private Sub worksheet_SelectionChange(ByVal Target As Excel.Range)
If Not Intersect(Target, Range("M3:M100")) Is Nothing Then
Call Basic_Calendar
Else
boolDate = False 'make it false to trigger the previous behavior in Worksheet_Change event
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range) 'Log Changes of Current Sheet and put in Sheet("Log")
Dim RangeValues As Variant, r As Long, boolOne As Boolean, TgValue 'the array to keep Target values (before UnDo)
Dim SH As Worksheet: Set SH = Sheets("Log")
Dim UN As String: UN = Application.UserName
If Not Intersect(Target, Range("AK:XFD")) Is Nothing Then Exit Sub 'not doing anything if a cell in AK:XFD is changed
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
If Target.Cells.Count > 1 Then
TgValue = ExtractData(Target)
Else
TgValue = Array(Array(Target.value, Target.Address(0, 0))) 'Put the target range in an array (or as a string for a single cell)
boolOne = True
End If
Application.EnableEvents = False 'Avoide trigger the change event after UnDo
If boolDate Then '____________________________________________________________
Dim prevTarget
prevTarget = Target.value 'memorize the target value
Target.value = PrevVal 'change the target value to the one before changing
RangeValues = ExtractData(Target) 'extract data exactly as before
Target.value = prevTarget 'set the last date
Else '____________________________________________________________
Application.Undo
RangeValues = ExtractData(Target) 'Define RangeValue
PutDataBack TgValue, ActiveSheet 'Put back the changed data
End If
If boolOne Then Target.Offset(1).Select
Application.EnableEvents = True
Dim columnHeader As String, rowHeader As String
For r = 0 To UBound(RangeValues)
If RangeValues(r)(0) <> TgValue(r)(0) Then
columnHeader = Cells(1, Range(RangeValues(r)(1)).Column).value
rowHeader = Range("B" & Range(RangeValues(r)(1)).Row).value
Sheets("Log").Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Resize(1, 6).value = _
Array(UN, Now, rowHeader, columnHeader, TgValue(r)(0), RangeValues(r)(0))
End If
Next r
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Sub PutDataBack(arr, SH As Worksheet)
Dim i As Long, arrInt, El
For Each El In arr
SH.Range(El(1)).value = El(0)
Next
End Sub
Function ExtractData(Rng As Range) As Variant
Dim a As Range, arr, Count As Long, i As Long
ReDim arr(Rng.Cells.Count - 1)
For Each a In Rng.Areas 'creating a jagged array containing the values and the cells address
For i = 1 To a.Cells.Count
arr(Count) = Array(a.Cells(i).value, a.Cells(i).Address(0, 0)): Count = Count + 1
Next
Next
ExtractData = arr
End Function
' in a separate module "Calendar" to initiate calendar
Option Explicit
Option Compare Text
Public PrevVal As Variant, boolDate As Boolean
Sub Basic_Calendar()
Dim datevariable As Variant
datevariable = CalendarForm.GetDate
If datevariable <> 0 Then
PrevVal = Selection.value: boolDate = True
Selection.value = datevariable
End If
End Sub
In order to make the solution allowing multiple cells entry from the Callendar, but also allowing multiple deletions, please adapt it in the next way:
Use this modified code in the module where Basic_Calendar Sub exists:
Option Explicit
Option Compare Text
Public PrevVal(), boolDate As Boolean
Sub Basic_Calendar()
Dim datevariable As Variant
datevariable = CalendarForm.GetDate
If datevariable <> 0 Then
PrevVal = Selection.value: boolDate = True
Selection.value = datevariable
Else
Erase PrevVal 'to identify the case of deletion
End If
End Sub
Edited:
If your installation/version is not deal with directly loading the array, please use the next version, which do it by iteration:
Sub Basic_Calendar()
Dim datevariable As Variant
datevariable = CalendarForm.GetDate
If datevariable <> 0 Then
Dim i As Long
ReDim PrevVal(1 To Selection.Rows.Count, 1 To 1)
For i = 1 To Selection.Rows.Count
PrevVal(i, 1) = Selection.Cells(i).value
Next i
boolDate = True
Selection.value = datevariable
Else
Erase PrevVal 'to identify the case of deletion
End If
End Sub
Adapt this part of the Worksheet_Change event code in the next way:
If Target.Cells.Count > 1 Then
If Not CBool(Not Not PrevVal) Then boolDate = False 'the new line checking if the multiple rows array is empty (or not)
TgValue = ExtractData(Target)
Else
TgValue = Array(Array(Target.value, Target.Address(0, 0))) 'Put the target range in an array (or as a string for a single cell)
boolOne = True
End If
The logic of the modification works as following:
a. When the Calendar form is called and it returns a Date, in a multi rows range, the delivered datevariable is dropped in the selected cells, and their previous value are loaded in PrevVal() array;
b. A change in Column "M:M" triggers the event and in case of PrevVal() not empty, it acts as usually for inserting Data (using the PrevVal() array elements instead of UnDo, which does not work for data added by code). In case of an empty array, it makes boolDate = False, switching the code to the clasic variant (able to use UnDo, because deletion has been done by the user)...
No need to check the code on another PC. It was a matter of solution logic starting from a wrong assumption and it cannot work differently than on your laptop.

Execute Procedure when Value in a Cell/Range Changes

I'm new to VBA and wrote the following codes according to my data set. The goal here is to execute my procedure if a cell/range gets changed by pasting new data into the worksheet, most probably the sheet will be empty as it will follow by a clear content procedure.
However, the code is not triggering the change event, I've tried several codes from Google, but none of them worked. Please note that my procedure gets me exactly the data I want in the format I want, however, if changes are needed, kindly let me know.
PLEASE HELP
1. Change event trigger - stored under Sheet1
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("A1")) Is Nothing Then
Application.EnableEvents = False
Call LoopandIfStatement
Application.EnableEvents = True
End If
End Sub
2. My procedure - stored under Sheet1 below the event above
Sub LoopandIfStatement()
Dim SHT As Worksheet
Set SHT = ThisWorkbook.Worksheets("CB")
MyLr = SHT.Cells(Rows.Count, 1).End(xlUp).Row
Dim I As Long
For I = 1 To MyLr
Dim O As Long
Dim U As Range
Set U = SHT.Range("A" & I)
If IsEmpty(SHT.Range("a" & I).Value) = False Then
SHT.Range("k" & I).Value = SHT.Range("A" & I).Value
Else
On Error GoTo ABC
SHT.Range("k" & I).Value = U.Offset(-1, 0)
End If
Next I
For O = 2 To MyLr
If SHT.Range("g" & O).Value = "Closing Balance" Then
SHT.Range("l" & O).Value = SHT.Range("j" & O).Value
End If
Next O
ABC:
End Sub
Results
This will trigger whenever new data is pasted in any cell of columns A to J
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("A:J")) Is Nothing Then
Application.EnableEvents = False
Call LoopandIfStatement
Application.EnableEvents = True
End If
End Sub
Regarding your sub LoopandIfStatement here are some suggestions:
Use Option explicit at the top of your modules (see this)
Declare all your variables (you're missing: Dim MyLr as long)
Try to name your variables to something understandable (e.g. instead of MyLr you could have lastRow)
If you need to exit a Sub you can use Exit Sub instead of a Goto ABC
EDIT:
Added code for the loop and the change worksheet event.
Paste it behind the CB Sheet module
Some highlights:
When you triggered the loop on each worksheet change, it would re-apply all the steps to all the cells. You can work with changed ranges using the Target argument/variable in the Worksheet_Change event
To loop through an existing range see the AddAccountBalanceToRange procedure
Try to think and plan your code in steps or actions that can be grouped
Use comments to describe the purpose of what you're doing
Remember to delete obsolete code (saw you had a copy of the procedure in a module)
Option Explicit
Private Sub CommandButton1_Click()
ThisWorkbook.Worksheets("Data").Columns("A:J").Copy
ThisWorkbook.Worksheets("CB").Range("A:J").PasteSpecial Paste:=xlPasteValues
End Sub
Private Sub CommandButton2_Click()
ThisWorkbook.Worksheets("CB").Range("A:L").ClearContents
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim targetUsedRange As Range
' Do something on non empty cells
Set targetUsedRange = Intersect(Target, Target.Parent.UsedRange)
If Not Intersect(Target, Me.Range("A:J")) Is Nothing Then
Application.EnableEvents = False
Call AddAccountBalance(targetUsedRange)
Application.EnableEvents = True
End If
End Sub
Private Sub AddAccountBalance(ByVal Target As Range)
Dim targetSheet As Worksheet
Dim evalRow As Range
Dim lastColumn As Long
Dim accountNumber As String
Dim balanceString As String
Dim narrative As String
Dim balanceValue As Long
balanceString = "Closing Balance"
' If deleting or clearing columns
If Target Is Nothing Then Exit Sub
' Do something if there are any values in range
If Application.WorksheetFunction.CountA(Target) = 0 Then Exit Sub
' Get the parent sheet of the cells that were modifid
Set targetSheet = Target.Parent
' Get the last empty cell column in row 1 -Cells(3 -> this is row 3)- In the sample book: column K
lastColumn = targetSheet.Cells(3, targetSheet.Columns.Count).End(xlToLeft).Column
' Loop through each of the rows that were modified in range
For Each evalRow In Target.Cells.Rows
' Do something if account number or narrative are not null
If targetSheet.Cells(evalRow.Row, 1).Value <> vbNullString Or targetSheet.Cells(evalRow.Row, 7).Value <> vbNullString Then
' Store columns values in evaluated row
accountNumber = targetSheet.Cells(evalRow.Row, 1).Value
narrative = targetSheet.Cells(evalRow.Row, 7).Value
If IsNumeric(targetSheet.Cells(evalRow.Row, 10).Value) Then balanceValue = targetSheet.Cells(evalRow.Row, 10).Value
' Add account number
If accountNumber <> vbNullString Then
targetSheet.Cells(evalRow.Row, lastColumn).Value = accountNumber
End If
' Add closing balance
If narrative = balanceString Then
targetSheet.Cells(evalRow.Row, lastColumn).Value = targetSheet.Cells(evalRow.Row, 1).Offset(-1, 0).Value
targetSheet.Cells(evalRow.Row, lastColumn).Offset(0, 1).Value = balanceValue
End If
' Format last two columns (see how the resize property takes a single cell and expands the range)
With targetSheet.Cells(evalRow.Row, lastColumn).Resize(, 2).Interior
.ThemeColor = xlThemeColorAccent6
.TintAndShade = 0.799981688894314
.PatternTintAndShade = 0
End With
' Auto fit last column (K) (you could use the resize property as in the previous statement)
targetSheet.Columns(lastColumn).EntireColumn.AutoFit
End If
Next evalRow
End Sub
Public Sub AddAccountBalanceToRange()
Dim targetSheet As Worksheet
Dim evalRange As Range
Set targetSheet = ThisWorkbook.Worksheets("CB")
Set evalRange = targetSheet.Range("A1:A42")
AddAccountBalance evalRange
End Sub

How to copy only the last value of a split cell into a new worksheet

I am trying to copy a string of values into a single column on a new sheet. My code works when there is only one value in the active cell, but will copy every value in the cell once there are multiple values. I want it to copy only the most recent addition to the column on the new sheet. The input is selections from a drop-down menu that allows for multiple selections. I then have these selections being split and offset to a new cell 9 columns over (I also have other drop-downs so that is why there is so much space, but the larger loop should be able to handle the other drop-downs).
This is an image of the input:
This is what I am currently getting as an output:
This is my desired output:
If InStr(1, Oldvalue, Newvalue) = 0 Then
Target.Value = Oldvalue & "; " & Newvalue
Dim txt As String
Dim i As Integer
Dim FullName As Variant
txt = ActiveCell.Value
FullName = Split(txt, ";")
For i = 1 To UBound(FullName)
ActiveCell.Offset(i, 9).Value = FullName(i)
ActiveCell.Offset(i, 9).Copy
Worksheets("Links").Range("A3").End(xlUp).Offset(2, 0).Insert
Next i
I have included only the loop of code that is problematic in order to simplify finding a solution.
My best guess is that on a detected change, you want to update a distinct list of values in 9 cells over?
Right now you are already managing a single distinct list. All that you would need to do is clear the values in the column 9 cells over then print the values in your drop down.
Private Sub Worksheet_Change(ByVal Target As Range)
'Code by Sumit Bansal from https://trumpexcel.com
' To allow multiple selections in a Drop Down List in Excel (without repetition)
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Address = "$A$1" Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else:
If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
If InStr(1, Oldvalue, Newvalue) = 0 Then
Target.Value = Oldvalue & "; " & Newvalue
Else:
Target.Value = Oldvalue
End If
End If
Dim txt As String
Dim i As Integer
Dim FullName As Variant
txt = ActiveCell.Value
FullName = Split(txt, ";")
ActiveCell.Offset(, 9).EntireColumn.Clear
For i = 0 To UBound(FullName)
ActiveCell.Offset(i, 9) = Trim(FullName(i))
Next i
End If
End If
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True
End Sub
But what if I want a distinct list from more than one drop down or ; delimited array? The best way to manage a distinct list in is a Collection or a Dictionary object.
If that is what your looking for, I will update this answer with a way to use those objects.
Based on your feed back I have updated the code to below to use a collection object to manage your distinct list from more than one drop down.
Option Explicit
Private col As Collection
' ^ we are defining this to the module level. That means it will retain values
' and be able to be referenced from any other place in the project.
Private Sub Worksheet_Change(ByVal Target As Range)
'Code by Sumit Bansal from https://trumpexcel.com
' To allow multiple selections in a Drop Down List in Excel (without repetition)
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Not Intersect(Target, Range("$A$1:$B$1")) Is Nothing Then
' ^ this will make the area your looking more specific than just .row = 11
' you could also replace the address with a namedRange
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else:
If Target.Value = "" Then
'' My guess is that here you would want to make a call to a function that
'' removes values from the function. You should be able to loop over the collection
'' to find the value to remove.
GoTo Exitsub
Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
If InStr(1, Oldvalue, Newvalue) = 0 Then
Target.Value = Oldvalue & "; " & Newvalue
Else:
Target.Value = Oldvalue
End If
End If
ManageList Newvalue
' ^ you already have the newest value. You just need a easy way to check if it
' is in the list. To do this I made a sub that receives text, and checks
' if it is in the publicly scoped collection.
End If
End If
End If
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True
End Sub
Private Sub ManageList(txt As String)
' This Sub will receive a text value and try to put it in a collection.
If col Is Nothing Then Set col = New Collection
On Error Resume Next
col.Add Item:=txt, Key:=txt
' ^ this method will throw an error if the Key is already in the collection.
' all we need to do then is watch for errors, and handle if we found a new one.
' I have found that collections and dictionary objects can handle .5M keys without any issues.
' using a dictionary, would allow you to test for a key without defining an error handler.
' with the trade off being that you have to add an additional reference to your project.
If Err.Number = 0 Then
' we had a new value
PrintList col
End If
End Sub
Private Sub PrintList(col As Collection)
Dim printTo As Range
Dim i As Long
Set printTo = Range("e1")
' ^ change e1 to a fully qualified address of where you
' want you list to be printed.
printTo.EntireColumn.Clear
On Error GoTo eos:
For i = 0 To col.Count - 1
printTo.Offset(i) = col(i + 1)
Next
eos:
End Sub

Formatting Multiple cells with letters and number in VBA

I have been trying to figure this problem out for some time to no avail.
I have a file that tracks different types of invoices. The invoices have both numbers and letters ex. ABC_1234_12345678. I want excel to format the invoice codes by adding the under scores after the user inputs the invoice code(without the underscores). I currently have a code that can do it for single cell but I was wondering how I could change it format a select number of cells ex. A1-A8. I will add my code in the comments.
Thank you for the help, I will be very thankful. :)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngWatch As Range
Dim strOld As String
Dim strNew As String
'What cell is the invoice number in?
Set rngWatch = Range("A1")
'Did user change it?
If Intersect(rngWatch, Target) Is Nothing Then Exit Sub
strOld = rngWatch.Value
'Are there already hypens?
If Len(strOld) = Len(Replace(strOld, "_", "")) Then strNew = Left(strOld, 3) & "_" & Mid(strOld, 4, 3) & "_" & Mid(strOld, 8)
'Turn this off for the momenet
Application.EnableEvents = False
rngWatch.Value = strNew
Application.EnableEvents = True
End If
End Sub
Expand your rngWatch:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngWatch As Range, r As Range
Dim strOld As String
Dim strNew As String
'What cell is the invoice number in?
Set rngWatch = Range("A:A")
'Did user change it?
If Intersect(rngWatch, Target) Is Nothing Then Exit Sub
For Each r In Intersect(Target, rngWatch)
strOld = r.Value
'Are there already hypens?
If Len(strOld) = Len(Replace(strOld, "_", "")) Then
strNew = Left(strOld, 3) & "_" & Mid(strOld, 4, 3) & "_" & Mid(strOld, 8)
'Turn this off for the momenet
Application.EnableEvents = False
r.Value = strNew
Application.EnableEvents = True
End If
Next r
End Sub
Note:
We use a loop in case the user changes several cells in column A simultaneously via Copy/Paste.
This can depend upon how you want the code to run. You could for example create a macro that processes all of the cells within a specific range once it has been run, which to me is a sensible way to do it. You could get the macro to process only the selected cells, which is another option. There are many ways to do this.
I have taken your example code and adjusted it so that any adjustments to cells within the _MyNamedRange named range are processed. Just incase you enter the same code into more than 1 cell, it scans through the intersect using a for loop, but you may want to get rid of this depending on how you see your worksheet functioning. You will need to create a named range _MyNamedRange, where the macro will function.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngWatch As Range
Dim strOld As String
Dim strNew As String
Dim rngCell As Range, rngInter As Range
'What cell is the invoice number in?
Set rngWatch = Range("_MyNamedRange")
'Get intersect of the change
Set rngInter = Intersect(rngWatch, Target)
'Exit of the change does not intersect with the named range
If rngInter Is Nothing Then Exit Sub
'Scan through the intersect cells and adjust the cells
Application.EnableEvents = False
For Each rngCell In rngInter
strOld = rngCell.Value
'Are there already hypens?
strNew = ""
If Len(strOld) = Len(Replace(strOld, "_", "")) Then strNew = Left(strOld, 3) & "_" & Mid(strOld, 4, 3) & "_" & Mid(strOld, 8)
'Update the cell
rngCell.Value = strNew
Next rngCell
Application.EnableEvents = True
End Sub

How to continue the sequence of the unique numbers in the excel sheet after closing the userform?

I am facing a problem in getting the sequence of the unique numbers(Serial number) when the userform is closed and opened later on. Firstly, when I fill the data in the userform everything is captured in the excel sheet perfectly with correct sequence; if I close the userform and run the code by filling the userform with new data the unique ID's are again starting from "1" but not according to the excel sheet row number which was previously saved.
Below is the code I tried:
Private Sub cmdSubmit_Click()
Dim WB As Workbook
Dim lr As Long
Set WB = Workbooks.Open("C:\Users\Desktop\Book2.xlsx")
Dim Database As Worksheet
Set Database = WB.Worksheets("Sheet1")
eRow = Database.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
lr = Database.Range("a65536").End(xlUp).Row
With Sheets("Sheet1")
If IsEmpty(.Range("A1")) Then
.Range("A1").Value = 0
Else
Database.Cells(lr + 1, 1) = Val(Database.Cells(lr, 1)) + 1
End If
End With
Database.Cells(eRow, 4).Value = cmbls.Text
Database.Cells(eRow, 2).Value = txtProject.Text
Database.Cells(eRow, 3).Value = txtEovia.Text
Database.Cells(eRow, 1).Value = txtUid.Text
Call UserForm_Initialize
WB.SaveAs ("C:\Users\Desktop\Book2.xlsx")
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim maxNumber
If Not Intersect(Target, Range("B:B")) Is Nothing Then
' don't run when more than one row is changed
If Target.Rows.Count > 1 Then Exit Sub
' if column A in the current row has a value, don't run
If Cells(Target.Row, 1) > 0 Then Exit Sub
' get the highest number in column A, then add 1 and write to the
' current row, column A
maxNumber = Application.WorksheetFunction.Max(Range("A:A"))
Target.Offset(0, -1) = maxNumber + 1
End If
End Sub
Private Sub UserForm_Initialize()
With txtUid
.Value = Format(Val(Cells(Rows.Count, 1).End(xlUp)) + 1, "0000")
.Enabled = False
End With
With txtProject
.Value = ""
.SetFocus
End With
End Sub
In this image if you see unique id's are repeating 1 and 2, but I need as 1,2,3,4....
I think this is where the issue is coming from. You need to re-calculate the last row every time the user form is Initialized.
Private Sub UserForm_Initialize()
Dim ws as Worksheet: Set ws = Thisworkbook.Sheets("Database")
With txtUid
.Value = Format(ws.Range("A" & ws.Rows.Count).End(xlUp) + 1, "0000")
.Enabled = False
End With
With txtProject
.Value = ""
.SetFocus
End With
End Sub
It's always risky to use row numbers or [max range value +1] as a sequence number.
Safer to use something like a name scoped to the worksheet, which has a value you can increment. Then the sequence is independent of your data.
E.g.
Function GetNextSequence(sht As Worksheet) As Long
Const SEQ_NAME As String = "SEQ"
Dim nm As Name, rv As Long
On Error Resume Next
Set nm = sht.Names(SEQ_NAME)
On Error GoTo 0
'add the name if it doesn't exist
If nm Is Nothing Then
Set nm = sht.Names.Add(Name:=SEQ_NAME, RefersToR1C1:="=0")
End If
rv = Evaluate(nm.Value) + 1
nm.Value = rv
GetNextSequence = rv
End Function

Resources