I've got a calendar that I'm trying to have populate with userform data in the first blank "appointment slot" upon a button click. An Appointment slot for the date 12/1 would be the cell ranges from E2:J3 then the next appointment slot for the same date is K2:P3 and so on and so forth.
What I'm trying to get it to do is for the next open appointment slot to be filled with the userform data on click of a button.
I.E; Once button is clicked, if date is 12/1 then groupname.text would paste to E2, groupsize.text would paste in F2, lessontime.text would paste in E3...etc. If there's already an appointment in E2:J3 then it would paste in the next apoointment slot; K2 for groupname.text, etc.
It must be based on the date in column D, which the code below can find, but I can't figure out how to use LastRow to loop through the appointment slots to find a blank one. Not to mention how to paste in several different cells and not just ones in the same row.
Help?
If IsDate(DT.Text) Then
Dim thedate As Date
thedate = CDate(DT.Text)
Dim themonth As String
themonth = Format(thedate, "mmm") 'this bit of code is used for another sheet as well
'a bunch of other code for a different task that's happening at the same time.
Dim rng1 As Range
Dim dateStr As String
Dim dateToFind As Date
Dim foundDate As Range
dim cal as worksheet
dim nxtappointment as long
cal = thisworkbook.sheets("calendar")
nxtappointment '= ? this would be the lastrow
dateStr = Format(thedate, ddmm)
dateToFind = DateValue(DT.Text)
Set rng1 = Sheets("Calendar").Range(Cells(4, 1), _
Cells(Rows.Count, 1).End(xlUp))
Set foundDate = rng1.Find(What:=dateToFind, _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not foundDate Is Nothing Then
cal.range(E & nxtappointment).value = groupname.text
'etc.
If I understand correctly, there's to things:
Find the first blank slot for an appointment. I usually do something like this. Adapt the column and possibly the offset from last non-empty cell accordingly. For a start this should be good.
Function FindEmptySlotRow(wks As Worksheet) As Long
Dim i As Long
Dim columnToSearch As Integer
columnToSearch = 2 'replace with column index which is always filled for an appointment.
Dim firstEmptyRow As Long
'Search from the bottom of the file in case we have a blank slot in between
For i = wks.UsedRange.Rows.Count To 1 Step -1
If wks.Cells(i, columnToSearch).Value <> vbNullString Then
'we've got a non-empty cell. We just need to know if it's the first or second row of an appoitment slot.
Dim isFirstRowOfAppointment As Boolean
isFirstRowOfAppointment = i Mod 2 = 0 'Your first appointment rows appear to be in odd even rows
If isFirstRowOfAppointment Then
firstEmptyRow = i + 2 '+2 rows - since we found the non-empty cell in first appointment row
Else
firstEmptyRow = i + 1 '+1 row - since we found the non-empty cell in second appointment row
End If
Exit For
End If
Next i
FindEmptySlotRow = firstEmptyRow
End Function
Paste the data. I would do something reusable based on the empty row/cell I find in the previous function. Something in the likes of:
'fill appointment details based on top left cell of appointment
Sub PasteAppointmentData(topLeftAppointmentCell As Range)
'define ranges based on starting cell - EXAMPLE VALUES - adapt to your needs
Dim appointmentNameCell As Range
Dim appointmentTypeCell As Range
appointmentNameCell = topLeftAppointmentCell
appointmentTypeCell = topLeftAppointmentCell.Offset(0, 1)
'define values you want to store and get them from the form - EXAMPLE VALUES - adapt to your needs
Dim appointmentName As String
Dim appointmentType As String
appointmentName = txtboxName.Text
appointmentType = textboxType.Text
'store values in respective ranges
appointmentNameCell.Value = appointmentName
appointmentTypeCell.Value = appointmentType
End Sub
REMARK:
Not sure if I understand correctly, but If you're planning to store multiple appointments for the same day in the same row, I advise against this. Just like I would advise against using 2 rows per appointment. I would suggest a database-like approach. One row per record/appointment with columns that contain appointment properties. That proves beneficial if you want to make some edits/reporting later. Also, it makes your code much easier to write (e.g. no problem of checking for first or second row of appointment anymore).
Hope this helps you get an idea on how to solve this.
Related
I am in need of help with looping through a range (In this case Column "C") and finding 5 specific words (Sort, Set, Shine, Standardize, and Sustain) and then offsetting one row below the found value and copying that value to a categorized list on another sheet. To add complexity eliminating duplicate comments and blanks if possible would be great!
This is an imported form to my workbook and i am trying to create a quickly generated list of each comment under each specific word so that i can then copy into a report.
This code is borrowed and i am in need to adding multiple strings to find and be listed under each category.
Code:`Sub Find_Range()
'
Dim strFind As String
Dim oRng As Range
Dim fRng As Range
Dim i As Long
strFind = "SET IN ORDER" ' string to find
Set oRng = Worksheets("IMPORTED DATA").Columns(3) ' column to search
Set fRng = oRng.Cells(oRng.Cells.Count)
For i = 1 To Application.CountIf(oRng, strFind & "*")
Set fRng = oRng.Cells.Find(What:=strFind, _
LookIn:=xlValues, _
LookAt:=xlPart, _
After:=fRng, _
MatchCase:=False)
If Not fRng Is Nothing Then
With Worksheets("5S COMMENTS")
.Cells(i, "A") = fRng.Offset(1, 0).Value2
'.Cells(i, "B") = fRng.Offset(2, 1).Value2
End With
End If
Next i
'
End Sub`
Any help would be greatly appreciated!!!
Picture of excel list
Instead of VBA I suggest to use a formula:
=LET(
Source; 'Imported data'!C1:C999;
Step; 3;
FirstRow; INDEX(ROW(Source);1;0);
Criteria; FILTER(Source; MOD(ROW(Source)-FirstRow; Step) = 0);
Data; FILTER(Source; MOD(ROW(Source)-FirstRow; Step) = 1);
FILTER(Data; Criteria = INDIRECT("R[-1]C";FALSE)))
Here:
Source is a range with data to filter, where the first cell should be the one to check for the specific words
Step is a data chunk size, which is 3 in your case I guess
Criteria is cells to check for specific words
Data is the Criteria shifted one row down
INDIRECT("R[-1]C", False) addresses a cell right above the formula
Put the formula under the word to search for
situation is following:
I have 32 columns with data (various number of rows in columns) and need to delete cells with .value "downloaded" (always last cell in a column).
I have a code looping from column 32 to 1 and searching last_row for "downloaded" value. For 30 columns code seems to be working flawlessly but 2 columns return last_row value 1 even though there are multiple values (in fact hundreds of them) but they are non existent for VBA code.
Code:
Last_Col = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
last_row = ws.Cells(Rows.Count & Last_Col).End(xlUp).Row
For R = Last_Col To 1 Step -1
With ws
Last_Col = R
last_row = ws.Cells(.Rows.Count & Last_Col).End(xlUp).Row
If Cells(last_row, Last_Col).Value Like "*Downloaded*" Then
Cells(last_row, Last_Col).ClearContents
End If
End With
Next R
Data is being drained from another worksheets. For 2 columns where I experience an error, I manually deleted values and inserted another, random batch of values and code worked as intended.
Checked columns formatting, worksheets from which data is taken but I struggle to find a solution.
Thank you for your help.
Clear Last Cell If Criteria Is Met
The main mistake was using Cells(.Rows.Count & Last_Col), where .Rows.Count & Last_Col would have resulted in a 8 or 9-digit string, while it should have been ws.Cells(ws.Rows.Count, Last_Col).End(xlUp).Row which was pointed out by chris neilsen in the comments.
Another important issue is using ws. in front of .cells, .rows, .columns, .range, aka qualifying objects. If you don't do it and e.g. the wrong worksheet is active, you may get unexpected results.
There is no need for looping backwards unless you are deleting.
Although it allows wild characters (*, ?), the Like operator is case-sensitive (a<>A) unless you use Option Compare Text.
The first solution, using the End property, will fail if a number of last columns is hidden or if you insert a new first row e.g. for a title.
The second solution, using the Find method (and the first solution), may fail if the data is filtered.
The Code
Option Explicit
Sub clearLastEnd()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim LastCol As Long
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Dim LastRow As Long
Dim c As Long
For c = 1 To LastCol
LastRow = ws.Cells(ws.Rows.Count, c).End(xlUp).Row
With ws.Cells(LastRow, c)
If InStr(1, .Value, "Downloaded", vbTextCompare) > 0 Then
.ClearContents
End If
End With
Next c
End Sub
Sub clearLastFind()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim cel As Range
Set cel = ws.Cells.Find(What:="*", _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious)
If Not cel Is Nothing Then
Dim c As Long
For c = 1 To cel.Column
Set cel = Nothing
Set cel = ws.Columns(c).Find(What:="*", _
SearchDirection:=xlPrevious)
If Not cel Is Nothing Then
If InStr(1, cel.Value, "Downloaded", vbTextCompare) > 0 Then
cel.ClearContents
Else
' The current last non-empty cell does not contain criteria.
End If
Else
' Column is empty.
End If
Next c
Else
' Worksheet is empty.
End If
End Sub
EDIT:
So you are curious why it worked at all. The following should shed a light on it:
Sub test()
Dim i As Long
Debug.Print "Right", "Wrong", "Rows.Count & i"
For i = 1 To 32
Debug.Print Cells(Rows.Count, i).Address, _
Cells(Rows.Count & i).Address, Rows.Count & i
Next i
End Sub
In a nutshell, Cells can have 1 or 2 arguments. When 1 argument is used, it refers to the n-th cell of a range, and it 'counts' by row. The more common usage is with 2 arguments: rows, columns. For example:
Cells(5, 10) ' refers to cell `J5`.
Using one argument is inconvenient here:
Cells(16384 * (5-1) + 10)
i.e.
Cells(65546)
It may be convenient when processing a one-column or a one-row range.
Well , let me see if i understand you have a table in worksheet table have 32 columns and X rows (because you only put WS and i can know if is WS=worksheet or WS= Table-range)
for this i am going to say is selection (if you put worksheet only hace to change for it)
in your code put:
Last_Col = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
but in this you always wil obtein 1st cell so i dont understand why exist?
WS.columns.count
return number of columns you selection have
.End(xlToLeft)
return last cell if star to move to left (like Ctrl - left key)
so
Last_Col ---first go to cells (1,ws.Columns.Count) then go to left (End(xlToLeft)) and the end return number of column where finish (.Column) in this case you always get cell (1,"first column of your table")
NOTE: because you wrote that you have allways value in your cells (I have 32 columns with data (various number of rows in columns)
And for Row you have same question
Then you Wrote you want "Delete" but in your code you put Erase value (.ClearContents) so what do you want? because both are no equal
BUT if you have a table and want to search in any cells that have "Download" and only want to "clear content" you just may to use ".find" instead; or if you want to do all at same time you can use .replace (need to check before if .find return "nothing" or no , because if return nothing you get error)
If you have a table with 32 columns and each row have one cell where you put "Donloaded" and want to "delete" all row your code only need select column where appear "downloaded" (example Column "status").
If you have a table where any cell can take value "downloaded" and want to "delete" that cell you need to take care to resize your table and "move to" (when you delete cells you need to say where you want to move yor data remain "letf, "rigth", "up", down).
However if you say that "Downloaded" always appear in last row you can use For to change for all columns and use .end(xlDown)
For i=1 to 32
if cells(1,i).end(xlDown).value="downloaded" then cells(1,i).end(xlDown).ClearContents
next
BUT you need put more information because if you cant garantize that all cells have values and exist cells with "nothing" you will need
I'm currently working on sorting a big excel sheet with projects, working hours and employees and make it into an overview per employee on worksheet2. The projects, employees are sorted horizontally in rows and the months organized vertically, with showing how the working hours per month in the column of each employee. So it is something like this I have to work with, to give a fictional example and transform it into an overview per employee:
See for Excel Sheet
What I need to do is to filter the rows for employees, copy the project they are involved in into my new worksheet as a column and get the matching working hours per month per project. All needs to be done automatically, if new employees or projects are added, that it updates automatically. Key is to get an overview per employee. The difficulty is, that the employees in the 1. worksheet are mentioned several times, that I have to track them, get the column they are mentioned and copy certain values in that column.
I tried working with IF, MATCH, INDEX and what I have so far is
=INDEX(Worksheet1!S2:AY11;10;MATCH(Employee1; Worksheet1!S11:AY11; 0))
but that is only half of the solution and only tells me, where the first employee is located in the large horizontal list and copy the name into the cell.
Maybe someone of you has an idea how to match a value that exists multiple times and copy certain cells (working hours, project) from that column of the employee. THANK YOU!
Here is my code:
Private Sub Worksheet_Activate()
'Declarations.
Dim WksReport As Worksheet
Dim WksData As Worksheet
Dim RngDataFirstCell As Range
Dim RngReportFirstCell As Range
Dim RngRange01 As Range
Dim RngTarget As Range
Dim IntCounter01 As Integer
Dim StrTotalLabel As String
'Setting variables.
Set WksReport = Sheets(Me.Name) 'put here the sheet where the report will be contained (no need to edit if it remain a worksheet_activate sub)
Set WksData = Sheets("Foglio7") 'put here the sheet where the data will be contained
Set RngDataFirstCell = WksData.Range("A2") 'put here the first top left cell of the data table (according to your screenshot it's A2)
Set RngReportFirstCell = WksReport.Range("B2") 'put here the first top left cell of the report table
StrTotalLabel = "Gesamt" 'put here the label used for columns with totals
'Asking permission to update the report. If denied, the sub is terminated.
If MsgBox("Update the report?", vbYesNo, "Report update") = 7 Then
Exit Sub
End If
'Checking for pre-existing report.
If RngReportFirstCell.Value <> "" Then
'Setting RngTarget.
Set RngTarget = WksReport.Range(RngReportFirstCell, WksReport.Cells(RngReportFirstCell.Offset(0, 1).End(xlDown).Row, RngReportFirstCell.Offset(0, 3).column))
'Clearing RngTarget.
RngTarget.ClearContents
RngTarget.ClearFormats
End If
'Typing in the report's label.
RngReportFirstCell.Value = "Person" 'Person
RngReportFirstCell.Offset(0, 1).Value = "Project" 'Project
RngReportFirstCell.Offset(0, 2).Value = "Monat" 'Month
RngReportFirstCell.Offset(0, 3).Value = "Arbeitsstunde" 'Working hour
'Setting RngRange01 to cover the data without any row/column tag.
Set RngTarget = RngDataFirstCell.EntireRow.Find("", RngDataFirstCell, xlValues, xlWhole, xlByColumns, xlNext, False, , False).Offset(2, -1)
Set RngRange01 = RngDataFirstCell.EntireColumn.Find("", RngDataFirstCell.Offset(2, 0), xlValues, xlWhole, xlByRows, xlNext, False, , False).Offset(-1, 1)
Set RngRange01 = WksData.Range(RngTarget, RngRange01)
'Covering each cell in RngRange01.
IntCounter01 = 1
For Each RngTarget In RngRange01
'Checking if the column is not labeled for totals.
If WksData.Cells(RngDataFirstCell.Row + 1, RngTarget.column) <> StrTotalLabel Then
'Report the person.
RngReportFirstCell.Offset(IntCounter01, 0).Value = WksData.Cells(RngDataFirstCell.Row + 1, RngTarget.column)
'Report the project.
RngReportFirstCell.Offset(IntCounter01, 1).Value = WksData.Cells(RngDataFirstCell.Row, RngTarget.column)
'Report the month.
RngReportFirstCell.Offset(IntCounter01, 2).Value = WksData.Cells(RngTarget.Row, RngDataFirstCell.column)
'Report the working hour.
RngReportFirstCell.Offset(IntCounter01, 3).Value = RngTarget.Value
'Setting IntCounter01 for the next row.
IntCounter01 = IntCounter01 + 1
End If
Next
End Sub
It is designed as a private Worksheet_Activate subroutine, therefore it must be placed in the report sheet module. It basically cover every cell of your data, as long as there are no gaps between months or between projects. Then it reports the cell value on a row with the relative person, project and month. It already ignores your totals (Gesamt). This way you should be easily apply a pivot table on it. Tell me if it works and if you need any clarifications as well as edits/improvements.
I have a table of data which needs certain values to be changed based upon the value in column A. The data resides in a column headed Analysis\xx where xx is a variable value from 1 to 60. The number of columns varies per row, so that row 4 may go up to Analysis\4 whereas row 5 might go up to Analysis\30. Every value appearing in the Analysis\xx field needs to be updated, where a certain value exists in column A.
I have code to update values based upon a single fixed column position but I'm struggling to work out how to iterate through each row to update a variable number of columns.
Any suggestions would be gratefully received.
I've attached a sample of the data below which shows a good selection of the variation, with the columns that don't need amending hidden. Row 26 actually goes all the way to column NB.
Assuming your table has a name you can use the follwoing code to loop through each row off column A and in this case just print the value of a column in the same row.
Sub Tester()
Dim rg As Range
Dim rgA As Range
Dim sngCell As Range
Set rg = Range("Your TableName")
Set rgA = rg.Columns(1)
For Each sngCell In rgA.Rows
Debug.Print sngCell.Offset(, 3).Value2
Next
End Sub
Does that code go in the right direction?
EDIT: Assuming you use a listobject, this code might help you
Sub ChangeTable()
Const ACCT_NO = "ABCD1234"
Const HEADING = "Analysis*"
Const NEW_VAL = "80321"
Dim tbl As ListObject
Dim x As Long
Dim i As Long
Dim hdrCount As Long
Set tbl = ActiveSheet.ListObjects("Your Name here")
hdrCount = tbl.HeaderRowRange.Columns.Count
For x = 1 To tbl.ListRows.Count
With tbl.ListRows(x)
If .Range(1, 1).Value2 = ACCT_NO Then
For i = 2 To hdrCount
If tbl.HeaderRowRange(i).Value2 Like HEADING Then
.Range(1, i).Value = NEW_VAL
End If
Next
End If
End With
Next
End Sub
Hallo have you tried to use something like this for which TEST1 is the Varibale you like to replace and Test2 is the Variable you like to chnage it to ? This code does it for the entire Worksheet.
Sub Macro1()
Cells.Replace What:="TEST1", Replacement:="Test2", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
End Sub
What I am trying to do should be easy but the noob in me is showing. I am creating a database in excel using a dataentry form in another worksheet. When the “enter” button is clicked, it runs a macro that places the info from the dataentry sheet to an Excel database. The problem is, of course, that the next record overwrites the previous record. What I want to do is copy the data from a field in the dataentry form, then go to the database and find the next empty row and paste the info there. Ideally, the procedure would continue and copy the next field in the dataentry form, find the empty cell to the right of the data previously pasted and then repeat until all 6 fields have been copied and pasted to the excel database. I want to do this all in excel rather than into an access database. Any ideas?
You could try
Sub LastRow()
Dim rngLast As Range
With ThisWorkbook.Worksheets("Sheet1")
If .Range("A1").Value = "" Then
Set rngLast = .Range("A1")
Else
Set rngLast = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0)
End If
MsgBox "Next blank row is " & rngLast.Address
End With
End Sub
This will find the very last cell (last column & last row) used in the workbook & sheet provided. Adapt to taste (e.g you don't need targetwkb if you only work with one workbook). In case you don't need to find the last column, or just want the specific last row of a given column, you could just define different the "With" part (w.e. "With Worksheets(targetSheet).Range("A:A") or something similar:
Function FindLastCell(targetWbk As String, targetSheet As String) As Range
Dim LastColumn As Integer, lastRow As Integer
'Finds the last used cell in target wbk/sheet
With Workbooks(targetWbk).Worksheets(targetSheet)
lastRow = .Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByRows, SearchDirection:=xlPrevious).row
LastColumn = .Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Set FindLastCell = Range(.Cells(1, 1), .Cells(lastRow, LastColumn))
End With
End Function
The above is a better approach overall because it searches from the last cell to the top. The otherway around, if you have an empty cell then Excel will stop searching, and you could overwrite all the data that follows just because a blank cell was left somewhere.
If you have lots of data (like 10s of thousands) you may want to change integer for long, so you don't bump on the integer limit. It also doesn't need to be a function, could be a sub but doesn't matter much...