I am a VBA novice and am trying to "automate" a repetitive task within an excel spreadsheet. Am trying to indent a column based upon outline levels in a different column. I've been attempting to adapt code from a similar task but have run into a wall. Basically what I'm trying to accomplish is have the macro read down the OutLvl column and indent the next column based upon the outline level. Below is a screen shot of this macro and highlighted in yellow is where it got hung up. Any opinions or feedback is appreciated as I am trying to learn the application. Thanks!
Indent Macro Attempt
It looks here like you are not properly initializing the range that you intend to iterate over. The Range object is tricky, in that it can actually refer to a group of cells itself, and the array usage here is unnecessary and even confusing. Try something like this:
Dim OutLvl as Range ' This will refer to the whole range of interest
Dim t as Range ' Always good practice to declare all variables
... ' Fill in the code as you have written
Set OutLvl = Range("A1:A20") ' Now OutLvl refers to A1:A20 on the active sheet.
'Note that I can't possibly know how to appropriately set this range.
For Each t in OutLvl
... ' Now perform the loop as you have written
Next t
More on the error you have received. Note I have not watched this, so I can't speak for its accuracy:
https://www.youtube.com/watch?v=kWT2YfSHpfM
Related
I'm trying to get some details copied in Excel from Sheet 1 columns 1-5 to Sheet 2 columns 1-4, but only for lines that include text or values on sheet one under a specific column (in this case, Column 2). There are other columns in between, so I need to be able to use exact columns rather than A:D for example.
Example of what I'm trying to achieve:
I have tried using a simple IF function with A:A<>"" so it would include any rows that have any data in them, however this does not seem to copy as I need and occasionally based on my attempts i also get circular reference errors. Additionally, I’m not sure how to make sure this gets pasted at the bottom of a table that will expand with each addition.
I realize a probably easier option would be to simply copy Sheet 1 entirely and use a filter on row 1 to deselect Blanks on A:A, but the sheet has so much more info that it would be a waste, and additionally info is constantly added so I need something scale-able. It also occurred to me now that by doing this i would include info from the "header" and "footer", basically a frozen pane - which I do not need.
Could this be done via a simple function, or would it require a Macro?
Please keep in mind I'm rubbish at programming, just trying to make my life easier and learn as I go. A lot of excel forums help but still I'm no coder. I can understand to a pretty big degree what the code does and can adjust accordingly though :)
As suggested, this cannot be done with formulas. There are different ways to achieve this.. below is one approach:
Sub CopyFilteredRows()
Dim oSourceSheet As Worksheet: Set oSourceSheet = ThisWorkbook.Worksheets("Sheet3") ' Set your source sheet here
Dim oRng As Range: Set oRng = oSourceSheet.Range("A2:E" & oSourceSheet.Range("C" & oSourceSheet.Rows.Count).End(xlUp).Row)
' Set filter on column B
oRng.AutoFilter
oRng.AutoFilter 2, "<>"
' Copy to specified sheet
oRng.SpecialCells(xlCellTypeVisible).EntireRow.Copy ThisWorkbook.Worksheets("Sheet4").Range("A2") ' Change your destination sheet here
' Clear objects
Set oRng = Nothing
Set oSourceSheet = Nothing
End Sub
Paste the above UDF in a Module and then run it whenever you want to perform the copy. I suspect you would have to modify it a bit so that you can cater for your particular scenario but it should give you a start
I am attempting to efficiently copy columns of data from one worksheet to a second worksheet in Excel using VBA, starting with a defined name for the column.
I am unsure why my code doesn't work. I cannot emphasis enough, how little I know about coding. I am attempting to teach myself VBA in order to manipulate vast quantities of data in Excel.
Function SortDataC()
'cuts and pastes columns from the unsorted worksheet to the sorted worksheet
Worksheets("UnsortedData").Range("DeltaModScore").Copy Destination:=Worksheets("SortedData").Columns(1)
End Function
DeltaModScore is the column header. If I look in defined named it is present on the sheet UnsortedData with workbook scope. Thus, I assume I have screwed up the syntax somewhere?
I have used the term Sheets("UnsortedData").Range..... as well as Worksheets.... as you see above. I've been basically searching the web for code examples and trying to get them to work with my data. Inevitably, I end up with errors I have much difficulty fixing. I hope this is something simple someone can point out.
You only require the single top left cell of a destination to complete a Copy & Paste.
SUB SortDataC()
'COPIES and pastes columns from the unsorted worksheet to the sorted worksheet
Worksheets("UnsortedData").Range("DeltaModScore").Copy _
Destination:=Worksheets("SortedData").Cells(1, "A")
End SUB
Functions are intended to return a value. If you simply want to complete an operation, a sub procedure is more appropriate.
In order to navigate through complex spreadsheets that I'm asked to analyse I need a list of all current regions in a worksheet. Excel help does not give me many clues. My solution so far is to loop over areas using the special cells function, but it is rather slow.
Function list_all_current_regions(work_sheet)
Dim current_region_dic As New Dictionary
Set r = work_sheet.Cells(1, 1)
For Each x In Array(xlCellTypeConstants, xlCellTypeFormulas)
Set c = work_sheet.Cells(1, 1).SpecialCells(x, 23)
For Each a In c.Areas
If Not current_region_dic.Exists(a.CurrentRegion.Address) Then
current_region_dic.Add a.CurrentRegion.Address, ""
End If
Next
Next
Set list_all_current_regions = current_region_dic
End Function
Is there a smarter way to list all the current regions in a worksheet?
Over the years I've avoided having multiple ranges on Worksheets for the very reason you are asking about, and I move disconnected content onto its own Worksheet when a client gives me a model designed as a single "ubersheet".
The only way around this I've found is to use Named Ranges. They are accessed using the Names collection attached to the Workbook and Worksheet objects.
One other tip I'll give is to just used the Named Range on the top left cell only then you can use the CurrentRegion property to grab the entire range. This helps when you have expanding regions that you don't want to go in to set and reset the entire range.
https://msdn.microsoft.com/en-us/library/office/ff196678.aspx?f=255&MSPPError=-2147217396
I'm creating an Excel file from data in an Access database.
All I'm currently trying to do is offset a selection I've made in Excel from Access, and I keep receiving errors.
ExcelWS.Range(Range("H6"), Range("H6").End(xlDown)).Offset(0, -1).Select
ExcelWS is an Excel. Worksheet object.
My goal is to select from G6 to the end of the workbook. However, not every cell in column G is populated, so a simple End(xlDown) from cell G6 only takes me part way through the file. That being said, all cells in column H are populated, so I'm able to do an End(xlDown) from cell H6. From there, I want to move my selection one to the left, giving me the desired range in column G selected.
The above code is a product of about 2hrs of googling, and trying tons of different combinations of similar code. However, I'm still receiving an error.
Run-time error '1004':
Method 'Range' of object '_Global' failed
The odd thing is that 1/10 times, I'll receive the desire results. The rest of the time I receive the above error.
I feel as though my issue has something to do with running that line of code from Access, however that's currently my only way of completing this task as I have other things that rely on being able to do it from Access.
Any help would be appreciated, and if there's any more info I can give I'll try my best.
Thanks.
To run Select the sheet shoudl be active/ in -focus.
So you ned to set foucus to sheet.
ExcelWS.Activate
ExcelWS.Range(Range("H6"), Range("H6").End(xlDown)).Offset(0, -1).Select
Simalarly, to make the seet in-focus, your workbook needs to be active/ infocus.
Try to avoid Select as much as possible. Also fully qualify the names.
Dim rngTest as Range
Set rngTest= ExcelWS.Range(ExcelWS.Range("H6"), ExcelWS.Range("H6").End(xlDown)).Offset(0, -1)
Thanks to #cyboashu, I was able to get it working.
I took the examples he gave me and mushed them together to make it do what I wanted.
I realized after posting the question that it was working every other time, consistently.
Anyway, the code I used is
Dim ExcelR1 As Range
Set ExcelR1 = ExcelWS.Range(ExcelWS.Range("H6"), ExcelWS.Range("H6").End(xlDown)).Offset(0, -1)
ExcelR1.Activate
I'd like to preface this question by saying that I am an undergrad in college who knows C++ and has a very rudimentary understanding of VBA.
Now then, as stated in the title I need some help configuring some VBA code for an Excel worksheet so that whenever a cell in a column (specifically the D column) is modified it will automatically update other cells within the same row.
Essentially I want this to work such that when user Bob modifies cell D26 (for example) it will call a custom function I built and insert that code into cell B26 and then repeat with a different function for cell C26.
However, this function needs to be such that if cell D27 is modified it will only modify other cells in row 27, leaving row 26 and prior or subsequent rows alone until such a time as this function is called in D28 and so on.
I'm not entirely sure if this is even possible but I'd be gracious if anybody could help me configure this.
The code I built/scavenged from the internet for my custom function is this:
http://pastebin.com/RE0V2nrT
The second function I want to call for this project is the =TODAY() function built into Excel.
The code I have scraped together so far for checking if the cell has changed is this:
http://pastebin.com/S5E8cmty
If anybody could help me understand how to write what I'm looking for it would be much appreciated. If you have a different approach to solving the issue I would also love to hear it... as long as you could help me then enact your solution, haha!
Anyways, thanks to anybody who replies.
Have a look at the worksheet events available within the Excel namespace.
For this, you would use the Change event
If you double click on the worksheet you want to monitor, you can insert a Worksheet_Change sub. Then you can use the intersect function to check if the changed cell was within your range you want to monitor (e.g. D:D).
You can specify which cells you want to change. Here I just gave an example based on what you asked. This will put the output of your function into cell B[R] and put the current date into cell C[R]. Note that I'm using the Now() function since there is no Today() function in VBA. Since this returns both date and time, I'm using the Format function to get just the date.
Just for fun, let's go a little further into the object model and first get the Worksheet object to which the target range belongs. This is not 100% necessary - you could just rely on ActiveSheet. Now, you probably don't need to do this, and it's mostly just for fun, but it's also worth noting that if you were programmatically making changes to this sheet, but had not activated this sheet first (so another sheet was active) and you had not turned off EnableEvents you would get some strange results :)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim TargetSheet As Worksheet
Set TargetSheet = Target.Parent
With TargetSheet
If Not Application.Intersect(Target, .Range("D:D")) Is Nothing Then
.Cells(Target.Row, 2) = ExtractWindowsUser()
.Cells(Target.Row, 4) = Format(Now(), "YYYY-MM-DD")
End If
End With
End Sub
Explanation
Worksheet change sub is declared like this. The Worksheet objects have pre-defined method stubs for events. Kind of like an interface, though not listed as an interface in the documentation. If you think of it in that concept, this is your event handshake. See the link I posted above for a list of the worksheet events available.
Private Sub Worksheet_Change(ByVal Target As Range)
In the next lines we are getting the worksheet object to which the object named Target belongs. You can see in the sub declaration that Target is declared as an object of the type Range. If you check out the Worksheet object (linked above) or the Range object documentation you'll see that the range object is a member of the worksheet object, and the documentation kind of sucks here, but FYI the worksheet object is contained within the Parent property. Now, originally I had my code using the ActiveSheet member of the Application object - but I've edited it for the reasons given in my answer above.
Dim TargetSheet As Worksheet
Set TargetSheet = Target.Parent
I use With Blocks to save typing the same Worksheet reference in multiple places. A With block just lets me access the members of the namespace specified (in this case members of the object TargetSheet) by typing .SomeMember. The compiler understands that every reference like this refers to whatever is specified in the opening With .... statement. I personally like this for readability, but I also recommend it for maintenance (change reference one place vs many). Also having a single reference gives a tiny, insignificant, probably not worth mentioning performance boost over multiple references as well.
With TargetSheet
Next we check whether or not Target is within the range of cells we want to watch. The If....Then should look familiar enough. For our condition we use the boolean operator Not to check if the result of the intersect function (linked above) Is Nothing. The reason we do this is to check if the return is allocated. If an object is allocated the Not SomeObject Is Nothing condition will evaluate to False. If the object is not allocated (i.e. our Intersect function failed to return anything) then the statement evaluates to True. So, from the Intersect function documentation we know that if our return is allocated, the ranges intersect and the intersecting range object was returned. Thus if we want to know if they intersect, we can just check for the opposite of a failure.
If Not Application.Intersect(Target, .Range("D:D")) Is Nothing Then
The next lines then just execute some code on cells within the same row as Target. We use the Cells member of the worksheet object to specify what cells to modify. Per the documentation, the default property for Cells is Item which lets us access a range object through a row and column index like this: .Cells[Row,Column]. So, I simply use the row of our Target object and the column you wanted (column "A" =1, "B"=2, etc. You can see this by changing excel properties to R1C1 reference style if you are interested).
.Cells(Target.Row, 2) = ExtractWindowsUser()
And I think the Format() and Now() functions are pretty well explained in the documentation.