Forum Discussion

Michael1835's avatar
Michael1835
Copper Contributor
Apr 17, 2024

Using a drop down to hide/unhide columns in a table (VBA Code)

 

I would like to use a drop down to hide/unhide columns based on a persons name, the columns in orange are the columns I want to hide/unhide. When I choose a name from the drop down, I would like for the columns of other peoples names to be hidden(column1, column2 etc.. will be names. 

The names will change over time and I would like not to update the code every time this happens, is this possible? 

 

Many Thanks in advance

 

  • Michael1835 To show/hide columns based on a selected employee, first open Name Manager and define a named range for the list of employees (referring to the applicable column headers). For example, define a new name called Employee_List that refers to: =Sheet1!$I$6:$DD$6. Next, create a Data Validation List in cell G6, with Source: =Employee_List. Then, open the Visual Basic Editor and use the following code in the Worksheet_Change event of the applicable worksheet module (Sheet1, in this example):

     

    Private Sub Worksheet_Change(ByVal Target As Range)
    
        Dim rgEmployee As Range
        Set rgEmployee = Me.Range("G2")
    
        If Not Intersect(Target, rgEmployee) Is Nothing Then
    
            Dim rgList As Range, str As String, n As Variant
            Set rgList = Range("Employee_List")
            str = rgEmployee.Value
            n = Application.Match(str, rgList, 0)
    
            If IsError(n) Then
                rgList.EntireColumn.Hidden = False
            Else
                rgList.EntireColumn.Hidden = True
                rgList.Cells(, n).EntireColumn.Hidden = False
            End If
    
        End If
    
    End Sub

     

    To filter rows based on the "Check Training Status" selection in cell D2, I highly recommend looking into the Range.AutoFilter method, which will vary slightly depending on if your data is setup as a worksheet range or an Excel table (ListObject). As an example, the following code demonstrates how this could be achieved with an Excel table (ListObject), where the "Training Records out of Date" and "Retraining Required" columns are part of the table:

     

    Sub CheckTrainingStatus()
    
        Dim tbl As ListObject, x As Long, y As Long
        Set tbl = Sheet1.ListObjects("Table1")
        x = tbl.ListColumns("Training Records out of Date").Index
        y = tbl.ListColumns("Retraining Required").Index
    
        With tbl.Range
            Select Case Sheet1.Range("D2").Value
                Case "Refresh"
                    .AutoFilter Field:=x
                    .AutoFilter Field:=y, Criteria1:="<>0"
                Case "Obsolete"
                    .AutoFilter Field:=x, Criteria1:="<>0"
                    .AutoFilter Field:=y
                Case Else 'All or blank
                    .AutoFilter Field:=x
                    .AutoFilter Field:=y
            End Select
        End With
    
    End Sub

     

    To apply the filter to the selected employee only, modify the worksheet formulas used in columns DE and DF to reference the matching column only. For example, if the original formula in cell DE7 was:

     

    =SUM(I7:DD7)

     

    ...you could change it to:

     

    =IF(ISBLANK($G$2), SUM(I7:DD7), INDEX(I7:DD7, MATCH($G$2, Employee_List, 0)))

     

    Then, modify the Worksheet_Change event to call CheckTrainingStatus as needed. For example:

     

    Private Sub Worksheet_Change(ByVal Target As Range)
    
        Dim rgEmployee As Range
        Set rgEmployee = Me.Range("G2")
    
        If Not Intersect(Target, rgEmployee) Is Nothing Then
    
            Dim rgList As Range, str As String, n As Variant
            Set rgList = Range("Employee_List")
            str = rgEmployee.Value
            n = Application.Match(str, rgList, 0)
    
            If IsError(n) Then
                rgList.EntireColumn.Hidden = False
            Else
                rgList.EntireColumn.Hidden = True
                rgList.Cells(, n).EntireColumn.Hidden = False
            End If
    
            Call CheckTrainingStatus
    
        ElseIf Not Intersect(Target, Me.Range("D2")) Is Nothing Then
    
            Call CheckTrainingStatus
    
        End If
    
    End Sub

     

    Note: this is an example only, intended to help point you in the right direction. If your data is not formatted as an Excel table (ListObject), additional steps may be required to identify the AutoFilter Range and/or the Field Numbers of the columns to be filtered.

  • djclements's avatar
    djclements
    Bronze Contributor

    Michael1835 To show/hide columns based on a selected employee, first open Name Manager and define a named range for the list of employees (referring to the applicable column headers). For example, define a new name called Employee_List that refers to: =Sheet1!$I$6:$DD$6. Next, create a Data Validation List in cell G6, with Source: =Employee_List. Then, open the Visual Basic Editor and use the following code in the Worksheet_Change event of the applicable worksheet module (Sheet1, in this example):

     

    Private Sub Worksheet_Change(ByVal Target As Range)
    
        Dim rgEmployee As Range
        Set rgEmployee = Me.Range("G2")
    
        If Not Intersect(Target, rgEmployee) Is Nothing Then
    
            Dim rgList As Range, str As String, n As Variant
            Set rgList = Range("Employee_List")
            str = rgEmployee.Value
            n = Application.Match(str, rgList, 0)
    
            If IsError(n) Then
                rgList.EntireColumn.Hidden = False
            Else
                rgList.EntireColumn.Hidden = True
                rgList.Cells(, n).EntireColumn.Hidden = False
            End If
    
        End If
    
    End Sub

     

    To filter rows based on the "Check Training Status" selection in cell D2, I highly recommend looking into the Range.AutoFilter method, which will vary slightly depending on if your data is setup as a worksheet range or an Excel table (ListObject). As an example, the following code demonstrates how this could be achieved with an Excel table (ListObject), where the "Training Records out of Date" and "Retraining Required" columns are part of the table:

     

    Sub CheckTrainingStatus()
    
        Dim tbl As ListObject, x As Long, y As Long
        Set tbl = Sheet1.ListObjects("Table1")
        x = tbl.ListColumns("Training Records out of Date").Index
        y = tbl.ListColumns("Retraining Required").Index
    
        With tbl.Range
            Select Case Sheet1.Range("D2").Value
                Case "Refresh"
                    .AutoFilter Field:=x
                    .AutoFilter Field:=y, Criteria1:="<>0"
                Case "Obsolete"
                    .AutoFilter Field:=x, Criteria1:="<>0"
                    .AutoFilter Field:=y
                Case Else 'All or blank
                    .AutoFilter Field:=x
                    .AutoFilter Field:=y
            End Select
        End With
    
    End Sub

     

    To apply the filter to the selected employee only, modify the worksheet formulas used in columns DE and DF to reference the matching column only. For example, if the original formula in cell DE7 was:

     

    =SUM(I7:DD7)

     

    ...you could change it to:

     

    =IF(ISBLANK($G$2), SUM(I7:DD7), INDEX(I7:DD7, MATCH($G$2, Employee_List, 0)))

     

    Then, modify the Worksheet_Change event to call CheckTrainingStatus as needed. For example:

     

    Private Sub Worksheet_Change(ByVal Target As Range)
    
        Dim rgEmployee As Range
        Set rgEmployee = Me.Range("G2")
    
        If Not Intersect(Target, rgEmployee) Is Nothing Then
    
            Dim rgList As Range, str As String, n As Variant
            Set rgList = Range("Employee_List")
            str = rgEmployee.Value
            n = Application.Match(str, rgList, 0)
    
            If IsError(n) Then
                rgList.EntireColumn.Hidden = False
            Else
                rgList.EntireColumn.Hidden = True
                rgList.Cells(, n).EntireColumn.Hidden = False
            End If
    
            Call CheckTrainingStatus
    
        ElseIf Not Intersect(Target, Me.Range("D2")) Is Nothing Then
    
            Call CheckTrainingStatus
    
        End If
    
    End Sub

     

    Note: this is an example only, intended to help point you in the right direction. If your data is not formatted as an Excel table (ListObject), additional steps may be required to identify the AutoFilter Range and/or the Field Numbers of the columns to be filtered.

    • Michael1835's avatar
      Michael1835
      Copper Contributor

      djclements Thank you for your post! The first part works perfectly for filtering the employee's. I am trying to get the second bit working (Still very new to the VBA code) and this is what I have:

      Can you tell me where I am going wrong for trying to use the Range.AutoFilter method?

       

      For reference the formula used for counting the rows with your suggestion is now:

      =IF(ISBLANK($G$2), COUNTIF(BG7:DD7,">0"), INDEX(I7:DD7, MATCH($G$2, Employee_List, 0))) 

      This is returning me the date of the training and not a count number? The bold is what is was originally.

       

      • djclements's avatar
        djclements
        Bronze Contributor

        Michael1835 Sorry, I forgot to mention that I placed the CheckTrainingStatus sub routine in a separate module, which is why I referenced the sheet code (Sheet1). Since you've placed it in the worksheet module (which is fine), just change the two Sheet1 references to Me:

        • Set tbl = Me.ListObjects("Template_Table")
        • Select Case Me.Range("D2").Value

         

        Regarding the worksheet formula, it's not entirely clear to me why you would only be referencing columns BG to DD, when the employee names span from columns I to DD. Perhaps there's more to the design than I'm understanding, but you could try something along these lines:

         

        =COUNTIF(IF(ISBLANK($G$2), I7:DD7, INDEX(I7:DD7, MATCH($G$2, Employee_List, 0))), ">0")
  • SnowMan55's avatar
    SnowMan55
    Bronze Contributor

    Michael1835 

    The dropdown list can use the column headings themselves as the data validation list.  (And if those data column headers of people's names are followed by one or more empty columns, you can include them in the validation list to use the resulting "blank" entry to reset visibility, as my code will do.)


    Put the following code into the Worksheet_Change event handler for the worksheet that contains the cell with the dropdown list.  (I assumed that the cell with the dropdown list is on the same worksheet as the data whose visibility you are controlling.  If it is on a different worksheet, then the Range references in lines 18-21 will need to be qualified appropriately.)  Change the Const values as is appropriate for the structure of your worksheet(s).

    Const strCONTROLLING_CELL_ADDR = "F2"
    Const strCONTROLLED_COLUMNS = "HIJKLMN"
    Const strROW_WITH_COL_HDRS = "4"
    
    Dim strSelection    As String
    Dim in4RelativeColumn   As Long
    Dim strColumn       As String
    
    '----   Show/hide worksheet columns depending on the selected
    '       column heading.
    If Not Intersect(Target, Range(strCONTROLLING_CELL_ADDR)) Is Nothing Then
        strSelection = Range(strCONTROLLING_CELL_ADDR).Value
        Application.ScreenUpdating = False
        For in4RelativeColumn = 1 To Len(strCONTROLLED_COLUMNS)
            strColumn = Mid$(strCONTROLLED_COLUMNS, in4RelativeColumn, 1)
            '  --
            If strSelection = "" _
            Or Range(strColumn & strROW_WITH_COL_HDRS).Value = strSelection Then
                Range(strColumn & "1").EntireColumn.Hidden = False
            Else
                Range(strColumn & "1").EntireColumn.Hidden = True
            End If
        Next in4RelativeColumn
        Application.ScreenUpdating = True
    End If

     

    • Michael1835's avatar
      Michael1835
      Copper Contributor

      SnowMan55 thanks for your reply. I have inputted the code but it doesn't appear to work at the moment. 

      The controlling drop-down is on the same worksheet, this is how it looks with the rows and columns visible: 

      Here is the code with the const values updated:

      A quick query, the rows end at DD but will excel understand "IJKLMNOPQRSTUVWXYZAAABAC etc..."?

       

       

       

       

      • Michael1835's avatar
        Michael1835
        Copper Contributor

        Sorry I also need to add another drop down that will hide rows. Here is the layout:

        The drop down in D2 has the following options:

        "All" - Hide nothing

        "Refresh" - hide rows that contain 0 in column DF

        "Obsolete" - hide rows that contain 0 in column DE

         

        I would like this to work in conjunction with the drop down for employee and on its own. So you can check for refresher/obsolete training for all or for a specific employee. 

         

        I understand that this is probably not possible as rows for other employees will show if refresher/obsolete training exists. So what would be most useful, if all is not achievable, is to be able to filter refresher/obsolete training for all employees. 

         

        Going back to my original query, filtering columns would work best if rows that do not contain any data are also hidden when the employee is select. I realise this I may be asking to much of excel here but I am new to VBA and I'm interested to find out it's capabilities and limits.

         

        So to summarize: 

         

        Select employee, columns of other employees are hidden and rows that do not contain data are also hidden. 

        And/or

        Select status of training and hide rows based on values in column's DE & DF or hide nothing for "All".

Resources