I am looking at a macro which would hide columns for a particular range every time I run the query.
For example: For the first time when I run the query, I would need Columns A to D to be hidden, like wise the next time I run the query, I would need columns E to H to be hidden.
Its basically a query that would generalize the column hiding pattern.
I request your expertise in this regard.
The procedure below will hide columns A to D if you pass a RunCount value of 1, columns E to H if you pass a value of 2, columns I to L if you pass a value of 3, and so on and so forth...
Public Sub HideColumns(RunCount As Integer, TargetSheet As Worksheet)
Dim rngHide As Range
Dim ColOffset As Integer
' Initialise rngHide to columns A to D of the target worksheet.
Set rngHide = TargetSheet.[A:D]
' Calculate the offset from column A to the first column _
that needs to be hidden, update rngHide if required.
ColOffset = (RunCount - 1) * 4
If ColOffset > 0 Then
Set rngHide = rngHide.Offset(0, ColOffset)
End If
' Hide the columns.
rngHide.ColumnWidth = 0
End Sub
If you decide you only want to hide certain columns, just change the line:
Set rngHide = TargetSheet.[A:D]
E.g. To hide only the first & last columns, (column A and D, column E and H, etc.):
Set rngHide = TargetSheet.[A:A, D:D]
To hide only the first two columns:
Set rngHide = TargetSheet.[A:B]
Related
Last summer, I made a pretty basic VBA for an inventory sheet. Column A lists item name, and column B, C, and D are additional info for each item listed. The inventory is two-columned (A2:D28 and G2:J28). The VBA I made makes it so that if I delete the item entry in cell A4, the info in cells B4:D4 clears automatically with it.
The question is: I've been struggling to find a way to make the cells shift up a row when the row above it is cleared, to prevent the list from accumulating empty rows as inventory items are deleted. Most examples I found online were to delete those empty cells, whereas I'd rather just clear them and keep my formatting intact.
Is there a way to shift specific cells up like that? And, it would be lovely if there was a way to shift items from the top of the second table (G2:J2) down and over to the bottom of the first table, into A28:D28.
Any help would be greatly appreciated, or even a thumb towards a relevant tutorial. Thank you!
This code is a little tricky because of the two columns you have on your screen. My code below reads the two columns into a single one, sorts blank rows to the bottom and then splits the resulting single column back into two. All of this is done without touching the cells themselves. Therefore formatting stays in place.
Option Explicit
Enum Nsp ' Table specs
' 023
' These enumerations define your table. Modify to suit
NspAnchorClm = 1 ' 1 = column A
NspTblClmCount = 4 ' number of Table Columns
NpsSpaceClmCount = 2 ' number of blank sheet columns between List Columns
NspListClmCount = 2 ' number of List Columns
NspFirstRow = 2
NspNumRows = 10 ' number of rows per List Column
End Enum
Sub ResetList()
' 023
Dim Clm() As Variant ' First sheet column of each list column
Dim ArrIn As Variant ' Input data array
Dim Cin As Long ' input column counter
Dim Rin As Long ' input row counter
Dim ArrOut As Variant ' Output data array
Dim Rout As Long ' output row counter
Dim Rng As Range ' the sheet range of varying dimension
Dim Tmp As Variant ' intermediate memory
Dim L As Integer ' List column counter
Dim C As Long ' column counter
Dim R As Long ' row counter
Clm = Array(NspAnchorClm, NspAnchorClm + NspTblClmCount + NpsSpaceClmCount)
Tmp = (NspTblClmCount * NspListClmCount) + (NpsSpaceClmCount * (NpsSpaceClmCount - 1))
Set Rng = Range(Cells(NspFirstRow, Clm(0)), _
Cells(NspFirstRow + NspNumRows - 1, Tmp))
' read all of the list into an array
ArrIn = Rng.Value
' define a single list column array
ReDim ArrOut(1 To NspNumRows * NspListClmCount, 1 To NspTblClmCount)
' transfer the data to a single list column
For L = 1 To NspListClmCount
For R = 1 To NspNumRows
Rout = (L - 1) * NspNumRows + R
For C = 1 To NspTblClmCount
Cin = Clm(L - 1) + C - 1
ArrOut(Rout, C) = ArrIn(R, Cin)
Next C
Next R
Next L
' ArrIn is cleared and re-purposed to take data from ArrOut
ReDim ArrIn(1 To UBound(ArrOut), 1 To UBound(ArrOut, 2))
Rin = 0
For Rout = 1 To UBound(ArrOut)
' skip rows where the first column is blank
If Len(ArrOut(Rout, 1)) Then
Rin = Rin + 1
For C = 1 To UBound(ArrOut, 2)
ArrIn(Rin, C) = ArrOut(Rout, C)
Next C
End If
Next Rout
' assign NspNumRows high sections of ArrIn to ArrOut
For L = 1 To NspListClmCount
ReDim ArrOut(1 To NspNumRows, 1 To NspTblClmCount)
For R = 1 To NspNumRows
For C = 1 To NspTblClmCount
ArrOut(R, C) = ArrIn(((L - 1) * NspNumRows) + R, C)
Next C
Next R
Set Rng = Cells(NspFirstRow, Clm(L - 1)).Resize(NspNumRows, NspTblClmCount)
Rng.Value = ArrOut
Next L
End Sub
I'm afraid this code will make the code you already have obsolete. If the first cell in a row is empty any content in the others will be omitted, just like your own code does.
Please pay attention to the enumeration at the top of the code. It works like a switchboard where you can enter all parameters. You can modify them as you wish. For example my code has NspNumRows = 10. Your sheet has 27 data rows per column. You will need to change that number. Just to help you find your way:-
The "AnchorColumn" is the first sheet column of the first list column. All other columns are counted from there. It need not be column A. You could leave column A blank and anchor your list in Column B (=2).
"TableColumns" are the columns that repeat in each "ListColumn". You can have more than 4 or fewer.
A "ListColumn" consists of several "TableColumns". You can have more than 2.
"SpaceColumns" are blank sheet columns inserted between "ListColumns".
NspFirstRow specifies the first data row. Above it are captions or other data which this program doesn't touch on. You could reserve the first 10 rows for something else and start your list in row 11.
By setting these 6 enumerations you can create lists of 2 or more List Columns, anywhere on the worksheets, with any number of data rows. Not all of this has been tested exhaustively. When you delete a row anywhere (in fact only the first cell of that row) the list is rewritten to move the blank row to the bottom of the last list column.
I have a list of computers and users.
Column A is the names of the computers (with duplicates).
Column C is a list of the users that I was able to join together in a
CSV file format.
Example:
In column A, in rows 1 and 2 we have computer1.
In columnc C, row 1 it has user1 and in row 2 it has user1,user2.
I need to keep only the largest row for each computer so that instead of having Computer1 with user1 I only have computer1 with user1,user2.
You could use match to test for an error for all cells below and then use that as a True/False result. Here's a formula I built:
=IF(ISERROR(MATCH(A2,A3:A$999999,0)),"Use Me","ignore")
This Macro would also work, if you wanted to delete them:
Sub KillRows()
Dim ws As Worksheet: Set ws = ActiveSheet
Dim killRNG As Range
Set killRNG = ws.Rows(Rows.Count)
Dim i As Long
For i = 1 To ws.UsedRange.Rows.Count
If Application.WorksheetFunction.CountIf(Range(ws.Cells(i + 1, 1), ws.Cells(Rows.Count, 1)), ws.Cells(i, 1)) > 0 Then
Set killRNG = Union(killRNG, ws.Rows(i))
End If
Next i
killRNG.EntireRow.Delete
End Sub
I have three columns E(insufficient QTY) F(Too Slow) and G(Not Listed) They all have checkboxes in them. I need to link
E to H
F to I
G to J
The following code works nicely if there was only 1 column of checkboxes but I don't know how to improve the code to run by checkboxes in a certain column. Right now it just searches the entire sheet for checkboxes and links them to the desired column.
Sub LinkChecks()
'Update 20150310
i = 2
For Each cb In ActiveSheet.CheckBoxes
cb.LinkedCell = Cells(i, "I").Address
i = i + 1
Next cb
End Sub
Edit
Ok... let's try again:
Since the Check Box object does not have cell information for the cell it's located in, we will have to use the Offset property more creatively.
Since we know there are 3 check boxes per row, we can find the total number of check boxes and divide by 3 to find out how many rows there are.
Then by setting a Range to a single cell at the top of column "E", you can use the offset property on that cell.
Assuming you placed your Check Boxes on the sheet down column "E" sequentially, and then down column "F" next, then down "G", we can reset the offsets once we get to the last row of each column. (If you place the check boxes on the sheet in row order, you'll have to invert the loop logic.) (If you placed the check boxes on randomly, you are out of luck and will have to set your linked cells manually.)
Sub LinkChecks()
Dim rng As Range
Dim strColumn As String
Dim i As Integer
Dim intCount As Integer
Dim intRowCnt As Integer
Dim intRowOffset As Integer
Dim intColumnOffset As Integer
Dim dCnt As Double
i = 1 ' Your initial row offset
intCount = 0 ' A counter for total number of check boxes
intRowCnt = 0 ' A Row counter to find last row
intRowOffset = i ' Current Row offset from initial rng cell
intColumnOffset = 3 ' Current Column Offset (3 over from first check box column)
strColumn = "E" ' Set a starting Column of your first check box
Set rng = ActiveSheet.Cells(1, strColumn) ' Set initial rng cell
' Count how many check boxes are on the active sheet
For Each cb In ActiveSheet.CheckBoxes
intCount = intCount + 1
Next cb
' Since you know you have 3 check boxes per row,
' you can divide by 3 to get your row count
dCnt = intCount / 3
' *** Put test for remainder problems here ***
For Each cb In ActiveSheet.CheckBoxes
cb.LinkedCell = rng.Offset(intRowOffset, intColumnOffset).Address
intRowOffset = intRowOffset + 1
' Increment your row count until you get to last row
intRowCnt = intRowCnt + 1
If intRowCnt >= dCnt Then
intRowCnt = 0 ' Reset you row counter
intColumnOffset = intColumnOffset + 1 ' Increment Offset to the next column
intRowOffset = i ' Reset Row offset back to top row
End If
Next cb
End Sub
As long as your check boxes were placed on the sheet down each column, the above program should find the correct Linked Cell for each box.
If they were placed in a different order, then at least this code shows you how to set an initial Range cell and how you can reference other cells with an offset.
Hopefully this code or a combination of these ideas will help you with your problem. :)
I have the following grid of data:
---------Header 1 Header 2 Header 3 Header 4
Row 1 x x x
Row 2 x x
Row 3 x
Row 4 x x x x
I then have a second sheet that looks like this:
Row 1 Row 2 Row 3 Row 4
I would like the second sheet to end up looking like this:
Row 1 Row 2 Row 3 Row 4
Header 1 Header 2 Header 3 Header 1
Header 3 Header 3 Header 2
Header 4 Header 3
. Header 4
Ignore that last period, I just used it to format it properly.
I've been playing with MATCH and INDEX for a couple hours and while I can get pieces of it, I can't seem to get it to all work together.
EDIT:
I use 'Header 1' and 'Row 1' as examples only. The actual data is text in Column A and Row 1, respectively. Also, since the source data will be modified, I'd prefer to have something that would automatically update the second sheet.
Here is one way to do it with a VBA function:
In the Developer Tab(*) Click on Visual Basic, then click on the "Insert" menu there and choose "Module" to insert a new module. Then paste in the Following code:
Option Explicit
Public Function GetHeaderMatchingRow(RowText As String, _
SearchRange As Range, _
iHdrNo As Integer) As String
Dim rng As Range
Set rng = SearchRange
Dim cel As Range
'Get the Row to scan
Dim i As Long, rowOff As Long
For i = 2 To rng.Rows.Count
Set cel = rng.Cells(i, 1)
If cel.Value = RowText Then
rowOff = i
Exit For
End If
Next i
'Now, scan horizontally for the iHdrNo'th non-blank cell
Dim cnt As Integer
For i = 2 To rng.Columns.Count
Set cel = rng.Cells(rowOff, i)
If Not CStr(cel.Value) = "" Then
cnt = cnt + 1
If cnt = iHdrNo Then
GetHeaderMatchingRow = rng.Cells(1, i).Value
Exit Function
End If
End If
Next i
GetHeaderMatchingRow = ""
End Function
Click on the "Debug" menu and select "Compile VBAProject".
Now go back to Excel and in your first sheet define a Named Range to cover all of your data in the grid. The Row names should be the first column in this range and the Header text should be the first row in it.
Now go to your second sheet and enter a formula like this in every output cell:
=GetHeaderMatchingRow(A$1, RowHeaderRange, 1)
Where the First parameter is the Row text that it will try to match in the first column of the range. I have "A$1" here because the in my test, my second sheet's column headers are also the Row-names in my first sheet, just like yours.
The second argument is the range to search (in this case, the Named Range we defined earlier), and the third argument is the count of the match that it is looking for (1st, 2nd, 3rd, etc.).
Note that the first and third parameters should change based on what column and row the output is for.
Does it have to use worksheet functions? It would be quite a bit simpler to create a macro to do it (I've made an example)
Edited the function to work with row headers in col a and column headers in row 1 and changed it to read from "Source" sheet and write the result to "Output" sheet
Public Sub Example()
Dim Output As Worksheet
Dim Sheet As Worksheet
Dim Row As Integer
Dim Column As Integer
Set Sheet = ThisWorkbook.Worksheets("Source")
Set Output = ThisWorkbook.Worksheets("Output")
Output.Cells.Clear ' Since were going to rebuild the whole thing, just nuke it.
For Row = Sheet.UsedRange.Rows(Sheet.UsedRange.Rows.Count).Row To 2 Step -1
Output.Cells(1, Row - 1).Value = Sheet.Cells(Row, 1).Value
For Column = Sheet.UsedRange.Columns(Sheet.UsedRange.Columns.Count).Column To 1 Step -1
If Not IsEmpty(Sheet.Cells(Row, Column)) Then
Sheet.Cells(1, Column).Copy
Output.Cells(2, Row - 1).Insert xlShiftDown
End If
Next Column
Next Row
End Sub
I had a look at doing it with worksheet functions and as others have said its going to be pretty tricky to do it without some vba mixed in there.
If you add this to a new module then you can access it as a workbook function. (not that this is the best way to do it, just fancied having a go)
'Return The Column Header of the Nth Non-Blank Cell on Specified Row
Public Function NonBlankByIndex(ByVal Row As Integer, ByVal Index As Integer) As Range
Dim Sheet As Worksheet
Dim Column As Integer
Dim Result As Range
Set Sheet = ThisWorkbook.Worksheets("Source") ' Change to your source sheet's name
Set Result = Nothing
Column = 2 ' Skip 1 as its the header
Do
If Column > Sheet.UsedRange.Columns(Sheet.UsedRange.Columns.Count).Column Then
Exit Do
End If
If Sheet.Cells(Row, Column) = "" Then
Column = Column + 1
Else
If Index = 1 Then
Set Result = Sheet.Cells(1, Column)
Exit Do
Else
Column = Column + 1
Index = Index - 1
End If
End If
Loop
Set NonBlankByIndex = Result
End Function
If you are happy with blanks in the listing try this in sheet2!A2:
=IF(INDEX(Sheet1!$B$2:$E$5,MATCH(A$1,Sheet1!$A$2:$A$5,0),ROW()-1)="x",INDEX(Sheet1!$B$1:$E$1,1,ROW()-1),"")
Just copy the formula over range A2:D5
I have been trying to write a search function in VBA to search for a specific word - or rather the lack thereof - in cells. I need to go through a range of cells. There are two columns, if neither one of the cells a row contains the word, the row should be flagged - e.g. change colors. I am able to search one column, but when I try to search across two columns Excel bombs out. The columns are contained in a Pivot Table.
I have attached my code below. Any recommendations?
Private Sub AnalyseCTCN_Click()
Dim CT2CFN1 As Range 'CT2CFN1 is column 1
Dim CT2CFN2 As Range 'CT2CFN2 is column 2
Dim CT2CFN As Range
Set CT2CFN1 = Intersect(PivotTables("PivotTableCTCN").PivotFields("CUST_TYPE").PivotItems("2").DataRange.EntireRow, _
PivotTables("PivotTableCTCN").PivotFields("CUST_FULL_NAME_1").DataRange)
Set CT2CFN2 = Intersect(PivotTables("PivotTableCTCN").PivotFields("CUST_TYPE").PivotItems("2").DataRange.EntireRow, _
PivotTables("PivotTableCTCN").PivotFields("CUST_FULL_NAME_2").DataRange)
Set CT2CFN = Union(CT2CFN1, CT2CFN2)
Dim d As Range
Dim c As Range
For Each d In CT2CFN1
For Each c In CT2CFN2
If InStr(1, d, "T/A", 1) = 0 And InStr(1, c, "T/A", 1) = 0 Then
d.Interior.Color = RGB(255, 0, 0)
End If
Next c
Next d
End Sub