简体   繁体   中英

VBA/Excel Speed up macro adding checkboxes

I need to add checkbox for each row in few documents and I have script for that which working, and that's ok but... If I have 10k rows then this script it's very slowly. How I can speed up it?CODE:

Sub AddCheckBoxes()
    Dim chk As CheckBox
    Dim myRange As Range, cel As Range
    Dim ws As Worksheet

    Set ws = Sheets("") 'adjust sheet to your need
    Set myRange = ws.Range("A65:A75") ' adjust range to your needs

    For Each cel In myRange
        Set chk = ws.CheckBoxes.Add(cel.Left, cel.Top, 30, 6) 'you can adjust left, top, height, width to your needs
        With chk
            .Caption = "Valid"
            .LinkedCell = cel.Range("B65:B75").Address
        End With
    Next 
End Sub

Thanks!

Let's try this and see if it fits. Please paste the following code into a normal code module (by default 'Module1') of a blank workbook you create for this purpose. It is a module which doesn't exist in a new workbook. Don't use any of the existing.

Option Explicit

Enum Nws                            ' Worksheet rows & columns
    ' 20 Apr 2017
    NwsFirstDataRow = 2             ' adjust as required
                                    ' Columns:
    NwsMainData = 1                 ' (= A) Test for used range
    NwsCheck = 7                    ' (= G) column for Check cell
End Enum

Enum Nck                            ' CheckBox
    ' 20 Apr 2017
    NckFalse
    NckTrue
    NckNotSet                       ' any value other than True or False
End Enum

Sub SetCheckCell(Target As Range)
    ' 20 Apr 2017

    Dim TgtVal As Nck

    With Target
        If Len(.Value) Then
            Select Case .Value
                Case True
                    TgtVal = NckFalse
                Case False
                    TgtVal = NckTrue
                Case Else
                    TgtVal = NckNotSet
            End Select
        Else
            TgtVal = NckNotSet
        End If

        If TgtVal = NckNotSet Then
            SetBorders Target
            TgtVal = NckFalse
        End If

        .Value = CBool(Array(0, -1)(TgtVal))
        With .Interior
            .Pattern = xlSolid
            .PatternColorIndex = xlAutomatic
            .ThemeColor = Array(xlThemeColorAccent6, xlThemeColorAccent3)(TgtVal)
            .TintAndShade = 0.399945066682943
            .PatternTintAndShade = 0
        End With
        .Offset(0, -1).Select
    End With
End Sub

Private Sub SetBorders(Rng As Range)
    ' 12 Apr 2017

    Dim Brd As Long

    For Brd = xlEdgeLeft To xlInsideHorizontal
        SetBorder Rng, Brd
    Next Brd
    Rng.Borders(xlDiagonalDown).LineStyle = xlNone
    Rng.Borders(xlDiagonalUp).LineStyle = xlNone
End Sub

Private Sub SetBorder(Rng As Range, _
                      Brd As Long)
    ' 12 Apr 2017

    With Rng.Borders(Brd)
        .LineStyle = xlContinuous
        .ThemeColor = 1
        .TintAndShade = -0.349986266670736
        .Weight = xlMedium
    End With
End Sub

In column A, enter something - anything - in row 10 (or thereabouts). This is the last "used" row in your worksheet.

Now paste the following code in the code sheet of the worksheet on which you created a last "used" row. It must be exactly that code sheet - no other. It is a sheet which already exists. You recognise it by the tab's name in the VBE's project explorer window.

Option Explicit

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    ' 20 Apr 2017

    Dim Rng As Range                    ' used range (almost)
    Dim Rl As Long                      ' last row

    Application.EnableEvents = False
    With Target.Worksheet
        Rl = .Cells(.Rows.Count, NwsMainData).End(xlUp).Row
        Set Rng = .Range(.Cells(NwsFirstDataRow, NwsCheck), .Cells(Rl, NwsCheck))
        If Not Application.Intersect(Target, Rng) Is Nothing Then
            SetCheckCell .Cells(Target.Row, NwsCheck)
        End If
    End With
    Application.EnableEvents = True
End Sub

Now you are all set to test but understand the mechanics first. At the top of the first batch of code you have Enum Nws which specifies one row and two columns. The specified row is NwsFirstDataRow with an assigned value of 2. It means that row 1 is outside the scope of this code. The code will not run when you click in row 1 (presumably a caption row). You could set NwsFirstDataRow to a value of 3, thereby creating 2 header rows which the code won't touch.

The two columns are NwsMainData and NwsCheck . NwsMainData is the column where the last row is measured. If you click below the last row the code will not run. You might find that column A doesn't suit your needs. You can set any other value, thereby specifying another column. The number you set is used for no other purpose but to find the last row. In your test, make sure the column actually has a used row.

NwsCheck is the column where you will have your "check box". You can specify any column. Try it out in a little while. The point is that the code will not run if you click any other column. Therefore, the code will run if you click in the NwsCheck column, at or below NwsFirstDataRow and at or above the last "used" row. Go ahead an click.

Since the cell is empty it will be coloured as a checkbox and filled with the word "False". Click it again and it will change colour and value will be True. It continues to toggle. The cursor moves to the left to allow you to toggle.

You could move the cursor right or up or down. You could change the colours to any colour Excel has on offer. You can change the frame from the one I have selected. You can change the words which are shown. In fact, there is very little you couldn't change - and it isn't difficult.

The question is whether the idea can be adapted to do the job you want a check box to do.

Here is a variation of the above. Instead of writing TRUE or FALSE, it actually gives you a checkbox-character which is either checked or not. The code displays a message box informing you of the status, but the idea is to execute whatever code you want to run instead, based on whether the box is checked or not.

To test this code, add this procedure to the normal code module. Some of the above code will be required for this solution. For the purpose of testing, just have the entire previous code installed. Then add this.

Function SetCheck(Cell As Range) As Boolean
    ' 21 Apr 2017

    Dim Fun As Integer
    Dim Chars() As Variant
    Dim Mark As Integer                     ' character current displayed

    Chars = Array(168, 254)                 ' unchecked / checked box
    With Cell
        If Len(.Value) Then Mark = AscW(.Value)
        Fun = IIf(Mark = Int(Chars(0)), 1, 0)
        With .Font
            .Name = "Wingdings"
                .Size = 11
        End With
        .Value = ChrW(Chars(Fun))
        .Offset(0, 1).Select
    End With

    SetCheck = CBool(Fun)
End Function

Replace the existing event procedure with the one below. The difference is minute, but for quick testing, just replace all of it.

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    ' 21 Apr 2017

    Dim Rng As Range                    ' used range (almost)
    Dim Rl As Long                      ' last row
    Dim Chk As Boolean

    Application.EnableEvents = False
    With Target.Worksheet
        Rl = .Cells(.Rows.Count, NwsMainData).End(xlUp).Row
        Set Rng = .Range(.Cells(NwsFirstDataRow, NwsCheck), .Cells(Rl, NwsCheck))
        If Not Application.Intersect(Target, Rng) Is Nothing Then
'            SetCheckCell .Cells(Target.Row, NwsCheck)
            Chk = SetCheck(Target.Cells(1))
            MsgBox "The checkbox is now " & IIf(Chk, "", "un") & "checked"
        End If
    End With
    Application.EnableEvents = True
End Sub

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM