I want to be able to build a table based upon the answer to 2 questions, with Question 1 "How many risks are in the current process?" to determine the number of columns (i.e. one per risk) and the second question "How many controls in the current process?" determines the number of rows to use (i.e one per control). Is there an easy way to build a table to show a matrix that can then be used for risk/control relationships?
This was a fun one, so I'll plan to make a YouTube tutorial on this later tonight and come back here to post my link. But here's a quick explanation of what I did.
I started by using A2 cell for # of rows, and the B2 cell for # of columns. Then I created a button of which I will assign it to a macro, explained in a bit.
Next I created the base layout for the table. This includes:
D5 to hold "Control ID"
E5 to hold "Control Description"
F3 to hold "Risk ID"
F4 to hold "Risk Description"
Also, let's start by adding the first Control and Risk blocks, as every table would presumably start with at least 1 control and 1 risk. So
D6 will have "C1"
E6 will have "Control 1 Description"
G3 will have "R1"
G4 will have "Risk 1 Description"
Then to keep consistent with your table, I put a black highlight on cells F5 and G5.
Now we can create the macro, using the following code:
Sub CreateTable()
' create variable x to hold # of rows from A2
Dim x As Integer
x = Range("A2").Value
' calculate the row range from x
Let RowRange = "7:" & 7 + x - 2
' copy row 6 into each row in the calculated range
Rows("6:6").Copy Range(RowRange)
' update the text in the newly created rows to match
' the corresponding control number
For i = 2 To x
Let ControlIdRow = 5 + i
Cells(ControlIdRow, 4).Value = "C" & i
Cells(ControlIdRow, 5).Value = "Control " & i & " Description"
Next i
Dim y As Integer
y = Range("B2").Value
Columns("G:G").Copy Range(Columns(8), Columns(8 + y - 2))
For i = 2 To y
Let RiskIdCol = 6 + i
Cells(3, RiskIdCol).Value = "R" & i
Cells(4, RiskIdCol).Value = "Risk " & i & " Description"
Next i
End Sub
Basically what's happening is that we create a variable x and assign it to be the number of rows, as input from cell A2. Then we use that to calculate the row range starting at row 7 (which will start as Control 2 since Control 1 is on row 6). Then we just copy the entire row 6 up until the specified range. Now (starting at the For loop), we need to edit the text in the cells to match the corresponding names (i.e. the second control will be C2, Control 2 Description, the third will be C3, Control 3 Description 3, and so on). Then just essentially repeat that same block, changing the respective values to match the Risks.
Then don't forget to assign the button to the new macro. Now you should be able to enter in a value for rows (A2) and a value for columns (B2) and click the button, and it should automatically create the table for you.
As mentioned, I'll create a YouTube video later tonight detailing this more and update this answer to point there.
Update
I noticed your picture has a checkmark in it, so here's the code to be able to just select a cell and have a green checkmark appear in the cell. Then clicking the cell again will remove the checkmark.
Open up the visual basic editor, and click the Sheet 1 code from the left. Then apply the following code in there:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If ActiveCell.Row > 5 And ActiveCell.Column > 6 Then
If ActiveCell.Value = "" Then
Target.Value = ChrW(&H2713)
Else
If ActiveCell.Value = ChrW(&H2713) Then
Target.Value = ""
End If
End If
End If
End Sub
Link to YouTube Tutorial on this answer
Related
I want to update some blank cell with preceding values in a range of P4 to P22, Q4 to Q22 & R4 t R22. In my case I have values in P4, Q4 & R4 if the cells below are blank and if corresponding column values of X4 matches with the corresponding blank cell values in X5 then the blank cells will automatically get updated. If it doesn't natch then the cells will stay blank. Can some one share a VBA based solution in excel?
Let me show you, using some screenshots, how I handle this (you can record it into a VBA macro):
Start with a sheet with some data:
Select the range I want to work with:
Press Ctrl+G (Goto), and choose "Special". In there, choose "Blanks":
Start typing a formula in the formula bar (you might need to press F2 for focusing on the formula bar), and type =B3 (the upper left value):
Press Ctrl+ENTER (don't forget the Ctrl button):
Voila:
Have fun!
Next time please share code that you have tried so far so it will easier for you and for the one who answering. Here is quick code example I tried to write according to your instructions. It can be improved and adjusted, but it still should do the task required.
Sub UpdateCells()
Dim sh As Worksheet
Dim y As Long
Set sh = Sheets("Your Sheet Name")
For y = 5 To 22 'starting from 5 to 22 since you have asked for this. It can be changed to your liking
If sh.Cells(y, "X") = sh.Cells(y - 1, "X") Then 'compares values in column X
sh.Cells(y, "P").Value = sh.Cells(y - 1, "P").Value 'copies values from above if values in column X matches
sh.Cells(y, "Q").Value = sh.Cells(y - 1, "Q").Value
sh.Cells(y, "R").Value = sh.Cells(y - 1, "R").Value
End If
Next y
End Sub
I have a file with a bunch of rows that contains data for certain part numbers from different configurations. Some of these part numbers are repeated throughout the file, and in those duplicated part numbers may contain certain data and some may not. I am trying to find the best way to determine the commonalities in the file for certain data. So for the commonalities, if one row has a value and another row is blank, the value for the nonblank row would be put into the blank row. And if the data on those two rows were different it would change the font color on the cell indicating that this part number two different unique values and should be checked.
Dim i, j, n As Long
Dim lr As Long
Dim moaf As Workbook
Dim sht As Worksheet
Application.ScreenUpdating = False
Set moaf = Workbooks("MOAF3.xlsb")
Set sht = moaf.Worksheets("Wire Data")
n = InputBox("What column # are you trying to fill in?: ")
lr = Cells(Rows.count, 2).End(xlUp).Row
For i = 2 To lr
lkup = Cells(i, 2).Value 'sets first lookup value
Fill = Cells(i, n).Value 'sets the first data value to compare
If Len(Fill) > 0 Then
For j = 2 To lr
lkup2 = Cells(j, 2).Value 'sets the second lookup value
Fill2 = Cells(j, n).Value 'sets the second value to compare
If lkup2 = lkup Then 'checks to see if p/ns are same
If Len(Fill2) = 0 Then 'checks to see if second value is blank
Cells(j, n).Value = Fill 'if value is blank the cell takes value of non blank cell
ElseIf Fill <> Fill2 Then 'checks to see if the values are non matching and non zero
Cells(i, n).Font.ColorIndex = 3 'changes font color of two cells
Cells(j, n).Font.ColorIndex = 3 'changes font color of two cells
End If
End If
Next j
End If
Next i
Application.ScreenUpdating = True
End Sub
Doing this generally freezes my excel, where my computer has 32GB of RAM and is Windows10. Is there a better approach for my problem, or is it something that can be done without using a vba? I've done some research on a method without using vba, but with like sumifs, countifs but haven't really done any deep dives.
So, if I understand your question correctly, you start with following data:
ID Column_header
2 a
3 _BLANK_
4 _BLANK_
5 b
6 _BLANK_
And you want to turn this into:
ID Column_header
2 a
3 a
4 a
5 b
6 b
I know a very simple trick for that (I have put everything in column 'A' for explanation):
Select every cell inside that column
Goto (Ctrl+G) Special, Blanks
In the formula bar, type =A2 (you are currently located in 'A3', and you want to copy there the value of the cell just above it)
Press Ctrl+ENTER
You'll see that 'A2' gets copied into 'A3', 'A3' into 'A4' and 'A5' into 'A6' (the fact that this is done for all blank cells, is due to the Ctrl+ENTER).
Record this into a macro, and it will go much faster.
I already see a question popping up : "Ok, but what about the font colour I want to change?". Well, the newly filled cells are based on a formula, so the length of =FORMULATEXT() won't be zero. You use this as a basis for conditional formatting.
Good luck
The inner for loop just needs to start at i, that is:
for j = i to lr
This should roughly half the runtime.
Further performance enhencements:
Use .Value2 instead of .Value property.
Or even better, read in the entire columns into an array, work on that in VBA, then write the result back.
What method would be best if I had the following.
3 checkboxes that if selected will populate 3 cells A1, A2 & A3. I need code that will check the 3 cells for data and create a singular string of info. I can use this with formula but need VBA to do the following to the cells as per below.
So my end result will be "cell1" (if only 1 checkbox is selected) or "cell1 & cell2" (if 2 checkboxes are selected) but if all 3 are selected I want the string to read "cell1, cell2 & cell3". There will only ever be 3 cells in this section.
I want the Concatenate Text to then go into a single cell. AA13 where it can be copied at a later stage.
I have another scenario where I have 6 checkboxes and I need the same thing as above, the 6 checkboxes populate 6 cells B1:B6. but, I need some code that makes sure a minimum of 2 check box's are selected. I can't work out how to get VBA to check for the number of tick boxes and report an error if it doesn't meet the criteria of 2 minimum.
I will have an active x button to run the code as per above to concatenate but when a check box is selected the cell will auto populate with the string i need.
Help would be much appreciated. I'm struggling to find code online to help me for these two scenarios
Consider:
Sub konkat()
Dim N As Long
With Application.WorksheetFunction
N = .CountA(Range("A1:A3"))
Select Case N
Case 0
Exit Sub
Case 1
Range("AA13").Value = Range("A1") & Range("A2") & Range("A3")
Case 2
Range("AA13").Value = .TextJoin(" & ", True, Range("A1:A3"))
Case 3
Range("AA13").Value = Range("A1").Value & " , " & Range("A2").Value & " & " & Range("A3").Value
End Select
End With
End Sub
A little Help again please.
Codes below work for hiding columns that do not match B5.
Now my problem is, I want to unhide column that matches values
from B6 and B7 at the same time.
Reference Values from Command Sheet Column B Row 5,6,7.
Let B5 is MARCH
Let B6 is JANUARYsample picture
Let B7 is FEBRUARY
Sheet Name (GRA_NewGen CI) Note that All Data's per row/column are here.
Range from Sheet Name to Match B5,B6,B7 is Column C Row 4 up to End of Column in row with Values.
Below is the 'Code
'Sub GRA_NewGen_CI()
Dim cell As Range
Application.ScreenUpdating = False
With Sheets("GRA_New Gen CI")
For Each cell In .Range("C4", .Range("XFD4").End(xlToLeft))
cell.EntireColumn.Hidden = cell.Value <> Sheets("Command").Range("B5") And Not IsEmpty(cell)
Next cell
End With
Application.ScreenUpdating = True
'End Sub
If all you want is to hide all rows marked "JANUARY", "FEBRUARY" etc. you will have more flexibility and faster action by using Excel's Filter functionality. Learn here about Filters.
That gave me quite a ride. All those hidden columns are tricky. But now it's your turn. Please follow the instructions.
On your 'Command' sheet, find a blank column and enter "Show All" in one of the cells and this function in the cell below that:
="Show "& B5
I prefer you to have all the 12 months in B5:B16, but if you have only Jan to Mar or prefer to change the content on the fly that is OK as well. Copy the formula down for as long as you have relevant data (month names or column captions) in column B. Give the range I just described a name. I gave it the name "DropdownList". Make sure the named range has a 'Scope' of "Workbook" (meaning, it is visible from all parts of the workbook).
Place a command button on the GRA_New sheet in position A4. Perhaps you already have a button elsewhere. In that case I will ask you to play along and make another one for now. Later you can move this button to any other location, including another sheet, but not in a column to be possibly hidden. This command button will be a Validation drop-down. Enter
"Allow" = List and
"Source" =DropdownList (including the = mark.
You should now have a validation dropdown showing "Show All" in first position, "Show January" in second, and more "Show ..." depending upon the size of the named range DropdownList. Make sure that there is a single space between in "Show January" and "Show all", not more and not less, and every line consisting of 2 words, the second of which is relevant.
Now add the following procedure to the code sheet of the "GR_New ..." sheet.
Private Sub Worksheet_Change(ByVal Target As Range)
'17 Mar 2017
If Target.Address = Range("A4").Address Then
SetDisplay_GRA_NewGen Split(Target.Value)(1)
End If
End Sub
In this procedure, please change the reference to "A4" to the cell where you have the validation dropdown.
The next procedure goes into a normal code module. By default its name would be "Module1", but you can give it any name you like.
Sub SetDisplay_GRA_NewGen(ByVal Cmd As String)
' 17 Mar 2017
Dim Spike As String
Dim CountHidden As Integer
Dim FirstColumn As Long, LastColumn As Long
Dim CapRow As Long, Cap As String
Dim C As Long
CapRow = 4
FirstColumn = 3 ' = column C
With Worksheets("GRA_New_Gen_CI")
LastColumn = .UsedRange.Columns.Count
If StrComp(Cmd, "all", vbTextCompare) Then
With Range("DropdownList")
For C = 2 To .Rows.Count
Cap = Split(.Cells(C).Value)(1)
Spike = Spike & "|" & Cap
Next C
End With
For C = FirstColumn To LastColumn ' count hidden columns
Cap = .Cells(CapRow, C).Value
If .Columns(C).Hidden Then
If .Columns(C).Hidden Or InStr(1, Spike, Cap, vbTextCompare) = 0 Then
' if Cap can't be selected it is counted as not hidden
CountHidden = CountHidden + 1
End If
End If
Next C
Application.ScreenUpdating = False
If CountHidden = 0 Then
' hide all except the specified column
.Range(.Columns(FirstColumn), .Columns(LastColumn)).Hidden = True
End If
For C = FirstColumn To LastColumn
With .Columns(C)
If .Hidden Then
Cap = .Cells(CapRow).Value
If StrComp(Cap, Cmd, vbTextCompare) = 0 Then .Hidden = False
End If
End With
Next C
Else
.Range(.Columns(FirstColumn), .Columns(LastColumn)).Hidden = False
End If
End With
Application.ScreenUpdating = True
End Sub
Look for the two declarations in this procedure:
CapRow = 4
FirstColumn = 3
Row 4 is the row on your data sheet in which the program will look for the months names. Column 3 (= "C") will be the first column in which the program will expect to find a month's name. Columns A:B will never be touched.
Now your system is ready. You will need to know how to operate it.
1. When you select "Show All" from the dropdown all columns starting from FirstColumn will be shown. Call this a reset.
2. When you select any of the items from the dropdown columns with that name in CapRow will be shown.
3. When you select another month it will be added to the one already shown.
4. When all columns are shown already, only the selected one will be displayed.
You can modify the range DropdownList anytime, make it longer or shorter. The important thing is that the names in the dropdown are available in the CapRow. The program compares them as text, meaning "show all" is the same as "SHOW ALL".
Started with a data validation list and I like that it is in the cell where I want the data to appear. Tried combo box and active X combo box and don't like that they do not reside in the cell. This is very different than Access. This is what I am trying to accomplish:
My named range (Employee) is A4:C100, 3 columns, with headings Title, MI, and LN on a sheet named "Emp".
My form location is C6. I wanted to be able to show 3 columns and end up with data from the three columns. For example, Officer J. Doe.
Currently I am using data validation list entering data into one column as Doe, J., Officer and it works. The list can be long and I will need it to be in alphabetical order.
Is this the best way or am I confused with combo box and active X combo box?
The only way to show a combination of all 3 columns in a dropdown list is to concatenate the data in a 4th column e.g. use the following formula in Cell D4
=A4&" "&B4&" "&C4
...then you can name the range D4:D100. You may wish to hide this column for presentational reasons
Actually, you will probably want to avoid naming the whole range as the bottom cells may be blank/make scrolling more awkward than strictly necessary. I would recommend dynamic ranges
The next extension exercise might be to develop your formula to allow for e.g. a missing middlename e.g.
=A4&" "&IF(B4<>"",B4&" ","")&C4
The above assumes you can sort the data manually. If the data is not being sorted manually, you will need to use VBA e.g. ensure Column D gets completed and the named range created each time a user moves off Sheet("Emp"). You can embed the following code in the Emp sheet...
Private Sub Worksheet_Deactivate
For n = 4 to 100
If Cells(n, 1) <> "" Then
Cells(n, 4) = Cells(n, 1) & " " & Cells(n, 2) & " " & Cells(n, 3)
End If
Next n
Range(Cells(4,4),Cells(100,4)).Sort Key1:=Cells(4,4), Order1:=xlAscending, Header:=xlNo
LastRow = 4
Do Until Cells(LastRow + 1, 4) = ""
LastRow = LastRow + 1
Loop
ActiveWorkbook.Names.Add Name:="Employee", RefersTo:=Range(Cells(4,4),Cells(LastRow,4))
End Sub
The expressions for sorting/adding range names can be found by recording macros and eliminating code as in this expert Excel support video. Your data validation would refer to 'Employee' which is the range name created in the 4th column
There are a number of assumptions made above such as the idea that all employees have data in the first column and you would need to add logical tests if you do not always have data in all three columns
It may also be that you would prefer to create the Employee range when a user clicks in cell C6 of your form, as this may be more robust. My assumption in using Worksheet_Deactivate is that 'Employee' may be used elsewhere in your spreadsheet
Something like this. Put this code in your worksheet where the comboBox is
Private Sub Worksheet_Change(ByVal Target As Range)
Dim topY As Integer, leftX As Integer
topY = ComboBox1.top
leftX = ComboBox1.left
Dim c As Range
Set c = Cells(5, 5)
c.Left = topY
c.Top = leftX
c.Width = ComboBox1.Width
c.Height = ComboBox1.Height
End Sub
It should keep it locked in place if you move things around. Or you could try it in your Private Sub Worksheet_SelectionChange(ByVal Target As Range) event.