Вставьте поля ввода, чтобы сделать код более интерактивным

В настоящее время я работаю над следующим кодом, который просматривает все вкладки в книге Excel, выбирает все валюты, превышающие определенный порог в определенном столбце «J», и если критерии соблюдены, строка, содержащая валюту, превышающую порог, вставляется в новая созданная вкладка под названием «резюме».

Теперь мой вопрос: 1. Есть ли шанс сделать этот код более интерактивным? Что я хотел бы сделать, так это добавить поле ввода, в котором пользователь вводит свой порог (в моем примере 1000000), и этот порог используется для перебора всех вкладок. 2. Было бы здорово получить поле ввода типа «выбрать столбец, содержащий валюту», так как столбец «J» не будет установлен все время, это также может быть другой столбец («I», «M» и т. д.), однако это будет одинаковым для всех листов. 3. Есть ли шанс выбрать определенные листы в книге (STRG + "sheetx" "sheety" и т. д.), которые затем вставляются в мой цикл, а все остальные игнорируются?

Любая помощь, особенно по моим вопросам в вопросах 1 и 2, приветствуется. Вопрос 3 будет только "приятным"

Option Explicit

Sub Test()

Dim WS As Worksheet
Set WS = Sheets.Add
WS.Name = "Summary"

Dim i As Long, j As Long, lastRow As Long
Dim sh As Worksheet
With Sheets("Summary")
.Cells.Clear
End With

j = 2

For Each sh In ActiveWorkbook.Sheets
    If sh.Name <> "Summary" Then
        lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
        For i = 4 To lastRow
            If sh.Range("J" & i) > 1000000 Or sh.Range("J" & i) < -1000000 Then
                sh.Range("a" & i & ":n" & i).Copy Destination:=Worksheets("Summary").Range("A" & j)
                Sheets("Summary").Range("N" & j) = sh.Name
                j = j + 1
            End If
        Next i
    End If
Next sh
Sheets("Summary").Columns("A:N").AutoFit
End Sub

person Florian Schramm    schedule 18.02.2017    source источник
comment
В нынешнем виде это слишком широко. У вас есть 3 вопроса, которые следует задать отдельно. Попробуйте изменить свой код, чтобы ответить на первый вопрос, и в случае успеха перейдите к следующему. Если ваша попытка модификации не работает, опубликуйте этот конкретный вопрос, включая сообщения об ошибках или нежелательное поведение.   -  person Mark Fitzgerald    schedule 19.02.2017
comment
Возможно, вы правы, я попробую этот подход.   -  person Florian Schramm    schedule 19.02.2017
comment
У вас есть два возможных частичных ответа. Даже если они верны, пользователь вряд ли увидит их, выполняя поиск по запросам Ограничить зацикливание листа на определенных листах или Выбрать листы для зацикливания. Короткие лаконичные вопросы с четкими заголовками подойдут вам и сообществу SO.   -  person Mark Fitzgerald    schedule 19.02.2017


Ответы (3)


Вы можете установить пользовательскую форму в качестве входных данных в программу - что-то вроде того, что следует. Вам нужно только один раз запустить подпрограмму CreateUserForm, чтобы настроить обработчики событий UserForm1 в вашей электронной таблице. Как только это будет сделано, вы можете запустить «Тест», чтобы увидеть саму форму UserForm1. Вы можете отредактировать обработчики событий, чтобы проверить ввод пользователя или отклонить его, если это необходимо. Также после настройки UserForm1 вы можете перемещать различные метки и списки и, конечно же, создавать новые. Это должно выглядеть так:

изображение пользовательской формы

Вы можете выбрать столько листов, сколько требуется, из последнего списка, и выбор будет добавлен в коллекцию vba. Посмотрите MsgBox в начале вашего кода и поиграйте с вводом значений/выборов в поле пользователя, чтобы увидеть, что он делает.

Обработчик UserForm, который вызывается при нажатии кнопки «ОК», сохранит выбор в глобальные переменные, чтобы их можно было использовать в коде.

Option Explicit

' Global Variables used by UserForm1
Public lst1BoxData As Variant
Public threshold As Integer
Public currencyCol As String
Public selectedSheets As Collection

' Only need to run this once.  It will create UserForm1.
' If run again it will needlessly create another user form that you don't need.
' Once it's run you can modify the event handlers by selecting the UserForm1
' object in the VBAProject Menu by right clicking on it and selecting 'View Code'

' Note that you can select multiple Sheets on the last listbox of the UserForm
' simply by holding down the shift key.
Sub CreateUserForm()
  Dim myForm As Object
  Dim X As Integer
  Dim Line As Integer

  'This is to stop screen flashing while creating form
  Application.VBE.MainWindow.Visible = False

  Set myForm = ThisWorkbook.VBProject.VBComponents.Add(3)

  'Create the User Form
  With myForm
   .Properties("Caption") = "Currency Settings"
   .Properties("Width") = 322
   .Properties("Height") = 110
  End With

  ' Create Label for threshold text box
   Dim thresholdLabel As Object
   Set thresholdLabel = myForm.Designer.Controls.Add("Forms.Label.1")
   With thresholdLabel
     .Name = "lbl1"
     .Caption = "Input Threshold:"
     .Top = 6
     .Left = 6
     .Width = 72
   End With

  'Create TextBox for the threshold value
  Dim thresholdTextBox As Object
  Set thresholdTextBox = myForm.Designer.Controls.Add("Forms.textbox.1")
  With thresholdTextBox
    .Name = "txt1"
    .Top = 18
    .Left = 6
    .Width = 75
    .Height = 16
    .Font.Size = 8
    .Font.Name = "Tahoma"
    .borderStyle = fmBorderStyleSingle
    .SpecialEffect = fmSpecialEffectSunken
  End With

  ' Create Label for threshold text box
   Dim currencyLabel As Object
   Set currencyLabel = myForm.Designer.Controls.Add("Forms.Label.1")
   With currencyLabel
     .Name = "lbl2"
     .Caption = "Currency Column:"
     .Top = 6
     .Left = 100
     .Width = 72
   End With

  'Create currency column ListBox
  Dim currencyListBox As Object
  Set currencyListBox = myForm.Designer.Controls.Add("Forms.listbox.1")
  With currencyListBox
    .Name = "lst1"
    .Top = 18
    .Left = 102
    .Width = 52
    .Height = 55
    .Font.Size = 8
    .Font.Name = "Tahoma"
    .borderStyle = fmBorderStyleSingle
    .SpecialEffect = fmSpecialEffectSunken
  End With

  ' Create Label for sheet text box
  Dim sheetLabel As Object
  Set sheetLabel = myForm.Designer.Controls.Add("Forms.Label.1")
  With sheetLabel
    .Name = "lbl3"
    .Caption = "Select Sheets:"
    .Top = 6
    .Left = 175
    .Width = 72
  End With

  'Create currency column ListBox
  Dim sheetListBox As Object
  Set sheetListBox = myForm.Designer.Controls.Add("Forms.listbox.1")
  With sheetListBox
    .Name = "lst3"
    .Top = 18
    .Left = 175
    .Width = 52
    .Height = 55
    .Font.Size = 8
    .MultiSelect = 1
    .Font.Name = "Tahoma"
    .borderStyle = fmBorderStyleSingle
    .SpecialEffect = fmSpecialEffectSunken
  End With

  'Create Select Button
  Dim selectButton As Object
  Set selectButton = myForm.Designer.Controls.Add("Forms.commandbutton.1")
  With selectButton
    .Name = "cmd1"
    .Caption = "Okay"
    .Accelerator = "M"
    .Top = 30
    .Left = 252
    .Width = 53
    .Height = 20
    .Font.Size = 8
    .Font.Name = "Tahoma"
    .BackStyle = fmBackStyleOpaque
  End With

  ' This will create the initialization sub and the click event
  ' handler to write the UserForm selections into the global
  ' variables so they can be used by the code.
  myForm.CodeModule.InsertLines 1, "Private Sub UserForm_Initialize()"
  myForm.CodeModule.InsertLines 2, "   me.lst1.addItem ""Column I"" "
  myForm.CodeModule.InsertLines 3, "   me.lst1.addItem ""Column J"" "
  myForm.CodeModule.InsertLines 4, "   me.lst1.addItem ""Column M"" "
  myForm.CodeModule.InsertLines 5, "   me.lst3.addItem ""Sheet X"" "
  myForm.CodeModule.InsertLines 6, "   me.lst3.addItem ""Sheet Y"" "
  myForm.CodeModule.InsertLines 7, "   lst1BoxData = Array(""I"", ""J"", ""M"")"
  myForm.CodeModule.InsertLines 8, "End Sub"

  'add code for Command Button
  myForm.CodeModule.InsertLines 9, "Private Sub cmd1_Click()"
  myForm.CodeModule.InsertLines 10, "  threshold = CInt(Me.txt1.Value)"
  myForm.CodeModule.InsertLines 11, "  currencyCol = lst1BoxData(Me.lst1.ListIndex)"
  myForm.CodeModule.InsertLines 12, "  Set selectedSheets = New Collection"
  myForm.CodeModule.InsertLines 13, "  For i = 0 To Me.lst3.ListCount - 1"
  myForm.CodeModule.InsertLines 14, "    If Me.lst3.Selected(i) = True Then"
  myForm.CodeModule.InsertLines 15, "      selectedSheets.Add Me.lst3.List(i)"
  myForm.CodeModule.InsertLines 16, "    End If"
  myForm.CodeModule.InsertLines 17, "  Next"
  myForm.CodeModule.InsertLines 18, "  Unload Me"
  myForm.CodeModule.InsertLines 19, "End Sub"

  'Add form to make it available
  VBA.UserForms.Add (myForm.Name)

End Sub

' This is your code verbatim except for now
' the UserForm is shown for selecting the
' 1) currency threshold, 2) the column letter
' and 3) the sheets you want to process.
' The MsgBox just shows you what you've
' selected just to demonstrate that it works.

Sub Test()

Dim WS As Worksheet
Set WS = Sheets.Add
WS.Name = "Summary"

Dim i As Long, j As Long, lastRow As Long
Dim sh As Worksheet
With Sheets("Summary")
  .Cells.Clear 
End With

'**** Start: Running & Checking UserForm Output ****
UserForm1.Show

Dim colItem As Variant
Dim colItems As String
For Each colItem In selectedSheets:
 colItems = colItems & " " & colItem
Next
MsgBox ("threshold=" & threshold & vbCrLf & _
        "currencyCol=" & currencyCol & vbCrLf & _
        "selectedSheets=" & colItems)
'**** End: Running & Checking UserForm Output ****

j = 2

For Each sh In ActiveWorkbook.Sheets
    If sh.Name <> "Summary" Then
        lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).row
        For i = 4 To lastRow
            If sh.Range("J" & i) > 1000000 Or sh.Range("J" & i) < -1000000 Then
                sh.Range("a" & i & ":n" & i).Copy Destination:=Worksheets("Summary").Range("A" & j)
                Sheets("Summary").Range("N" & j) = sh.Name
                j = j + 1
            End If
        Next i
    End If
Next sh
Sheets("Summary").Columns("A:N").AutoFit
End Sub
person Amorpheuses    schedule 19.02.2017
comment
Ценю вашу помощь! К сожалению, я никогда раньше не работал с UserForms и не знаю, как заставить его работать в моей книге. - person Florian Schramm; 19.02.2017
comment
Это нормально. Во всяком случае, я добавил изображение того, как это выглядит для других, и если вы захотите вернуться к этому в будущем. - person Amorpheuses; 23.02.2017
comment
ЭТО ИМЕННО то, что я ищу! У вас есть идеи, как я могу реализовать это в своем проекте (тестовый файл xlsx, прикрепленный в Dropbox)? dropbox.com/s/ofngqkxz3accrso/Test.xlsx?dl=0< /а> - person Florian Schramm; 23.02.2017
comment
Я не могу открыть вашу электронную таблицу, так как все мои версии являются корпоративными, и я могу рискнуть быть немедленно законсервированным, если в ней будет вредоносное ПО. Вы можете запустить приведенный выше код. Чтобы заставить его работать, вы открываете новую электронную таблицу. Сохраните его с включенным макросом, который создаст новую электронную таблицу с расширением «.xlsm». Закройте обычную таблицу xls и откройте таблицу с расширением «.xlsm». Создайте новый макрос с любым именем и очистите приведенный выше код в всплывающем окне, перезаписывая пустую подпрограмму, которую предоставляет Excel (возможно, в «Module1» в модулях редактора VBA). - person Amorpheuses; 23.02.2017
comment
Вернитесь в редактор VBA, и теперь в окне проекта вы должны увидеть запись «Формы» с «UserForm1» в ней (как уже упоминалось, вам нужно запустить это только один раз). Если вы до сих пор в порядке, запустите макрос «Тест», и пользовательская форма должна появиться. Дайте мне знать, если у вас возникнут проблемы на любом из шагов в этих 3 комментариях. Также, когда появится пользовательская форма, введите значение для порога, иначе вы получите ошибку для переменной «порог». Код UserForm надежен, но я оставлю дополнительные детали реализации на ваше усмотрение. - person Amorpheuses; 23.02.2017
comment
Спасибо за подробную инструкцию по этому поводу! Еще кое-что. В качестве последнего шага я должен вставить переменные полей ввода (threshold, currencyCol и coItems) в мой текстовый подпункт, верно? Я должен заменить 1000000 на порог, j на CurrencyCol и if sh.Name <> "Summary" на if sh.Name = colItems Then я прав? Поскольку у меня нет доступа к моему ноутбуку в данный момент, я не могу решить это прямо сейчас... - person Florian Schramm; 23.02.2017
comment
Да, за то, что вы сказали для переменных «threshold» и «currencyCol», но не для коллитов. Вы хотите использовать selectedSheets, который представляет собой коллекцию VBA, которая содержит имена всех листов, которые вы выбрали в списке «Выбрать листы» пользовательской формы. Вы можете получить доступ к каждому из имен листов, используя индексацию на «selectedSheets». Чтобы получить первый, вы используете «selectedSheets (1)», второй «selectedSheets (2)» и т. д.). Во всяком случае, вы можете погуглить тип данных Collection VBA - есть масса примеров. - person Amorpheuses; 23.02.2017
comment
Хорошо, спасибо за вашу помощь в этом. Я попробую в эти выходные и приму ваш ответ, как только он заработает! - person Florian Schramm; 23.02.2017
comment
хорошо, пока мне удалось создать пользовательскую форму, запустив «createUserForm» один раз. После этого я попытался запустить «тест» (измененный, как указано выше), однако, если я нажимаю «Выполнить», я получаю сообщение об ошибке: «Переменная не определена, которая указывает на for each colItem In selectedSheets:? Есть идеи, что случилось? - person Florian Schramm; 25.02.2017
comment
Вы видели форму пользователя? Эта строка находится после команды «UserForm1.Show», которая должна показать вам форму. - person Amorpheuses; 27.02.2017
comment
Я попробовал это на своем другом ноутбуке, и это действительно сработало! Спасибо за это! Я просто не понимаю, почему я должен заранее определять все листы и столбцы? Возможно ли, что я могу ввести значение столбца и выбрать листы (таким образом, чтобы пользовательская форма показывала мне все листы, которые в данный момент находятся в моей книге?! Вот что я изначально имел в виду. - person Florian Schramm; 28.02.2017
comment
Рад, что у вас все заработало - трудно вернуться к полям ввода, как только у вас заработает пользовательская форма. Да, вы должны быть в состоянии сделать это - предварительно загрузить существующие значения по умолчанию, чтобы вы могли выбрать то, что вам нужно изменить. Вам придется изменить код в подразделе UserForm_Initialize UserForm1. - person Amorpheuses; 28.02.2017
comment
Я настроил пользовательскую форму так, как мне нужно. Если я запускаю подтест, я вижу форму пользователя с содержимым, которое я хочу иметь, однако, если я выбираю все, что хочу, и нажимаю кнопку «ОК», я получаю сообщение об ошибке, говорящее мне, что подпрограмма click не определена?!. .... Любая идея, что это может быть? Я не мог понять это?! - person Florian Schramm; 01.03.2017
comment
cmd1_click определен. Это один из обработчиков событий в UserForm1, если только вы не переименовали кнопку. Имя кнопки задается в строке кода '.Name = cmd1'. Вы также можете зайти в редактор пользовательской формы, чтобы увидеть, является ли имя «cmd1». По сути, обработчик — это просто имя кнопки с добавленным к нему «_click». Не уверен, поможет ли это с вашей проблемой или нет. - person Amorpheuses; 02.03.2017

Вы можете попробовать это

Option Explicit

Sub Test()
    Dim WS As Worksheet
    Dim i As Long, j As Long, lastRow As Long
    Dim sh As Worksheet
    Dim sheetsList As Variant
    Dim threshold As Long

    Set WS = GetSheet("Summary", True)
    sheetsList = Array("STRG","sheetx","sheety") '<--| fill this array with the sheets names to be looped through

    threshold = Application.InputBox("Input threshold", Type:=1)
    j = 2
    For Each sh In ActiveWorkbook.Sheets(sheetsList)
        lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
        For i = 4 To lastRow
            If sh.Range("J" & i) > threshold Or sh.Range("J" & i) < -threshold Then
                sh.Range("a" & i & ":n" & i).Copy Destination:=WS.Range("A" & j)
                WS.Range("N" & j) = sh.Name
                j = j + 1
            End If
        Next i
    Next sh
    WS.Columns("A:N").AutoFit
End Sub

Function GetSheet(shtName As String, Optional clearIt As Boolean = False) As Worksheet
    On Error Resume Next
    Set GetSheet = Worksheets(shtName)
    If GetSheet Is Nothing Then
        Set GetSheet = Sheets.Add(after:=Worksheets(Worksheets.count))
        GetSheet.Name = shtName
    End If
    If clearIt Then GetSheet.UsedRange.Clear
End Function
person user3598756    schedule 19.02.2017
comment
Спасибо за помощь! Это отлично работает, но можно ли также вставить поле ввода, в котором я могу определить столбец, содержащий значения моей валюты? - person Florian Schramm; 19.02.2017
comment
Пожалуйста. Да, это возможно, и вы можете сделать многое так же, как это было сделано для порога. Меня нет рядом с моим компьютером, но вы попробуйте сами и попросите помощи, если вы застряли. Наконец, вы можете отметить мой ответ как принятый. Спасибо. - person user3598756; 19.02.2017
comment
Я понял. Ценю вашу помощь в этом, спасибо! - person Florian Schramm; 19.02.2017
comment
Если у вас осталось немного времени, возможно, у вас появятся идеи для моей последней проблемы с этим кодом до того, как он будет завершен: ;) ​​stackoverflow.com/questions/42325891/ - person Florian Schramm; 19.02.2017

Следующий код работает для моих целей, за исключением выбора отдельных вкладок для цикла:

Option Explicit

Sub Test()
    Dim column As String
    Dim WS As Worksheet
    Dim i As Long, j As Long, lastRow As Long
    Dim sh As Worksheet
    Dim sheetsList As Variant
    Dim threshold As Long

    Set WS = GetSheet("Summary", True)

    threshold = Application.InputBox("Input threshold", Type:=1)
    column = Application.InputBox("Currency Column", Type:=2)
    j = 2
    For Each sh In ActiveWorkbook.Sheets
        If sh.Name <> "Summary" Then
            lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
            For i = 4 To lastRow
                If sh.Range(column & i) > threshold Or sh.Range(column & i) < -threshold Then
                    sh.Range("a" & i & ":n" & i).Copy Destination:=WS.Range("A" & j)
                    WS.Range("N" & j) = sh.Name
                    j = j + 1
                End If
            Next i
        End If
    Next sh
    WS.Columns("A:N").AutoFit
End Sub

Function GetSheet(shtName As String, Optional clearIt As Boolean = False) As Worksheet
    On Error Resume Next
    Set GetSheet = Worksheets(shtName)
    If GetSheet Is Nothing Then
        Set GetSheet = Sheets.Add(after:=Worksheets(Worksheets.Count))
        GetSheet.Name = shtName
    End If
    If clearIt Then GetSheet.UsedRange.Clear
End Function
person Florian Schramm    schedule 19.02.2017