4

Я пытаюсь создать макрос, который будет считывать даты из ячеек в моей рабочей таблице и сравнивать их с датой (месяц и год), введенной в пользовательскую форму. Чтобы сделать это, у меня есть UserForm вызов Sub Report найденного в Sheet1 Целые числа, которые представляют введенные пользователем месяц и год, передаются вместе с вызовом.

Ошибка всегда возникает на линии: Call Sheet1.Report(intMonth, intYear) , который вызывает Report . Ошибка гласит: Run-time error '1004': Application-defined or object-defined error .

Вот мой сокращенный код, начиная с пользовательской формы:

Private Sub cmdOK_Click()

    'Transform month field into an integer (1-12)
    Dim intMonth As Integer
    Select Case cboMonth.Value
        Case Is = strJan 'January - 01
            intMonth = 1
        Case Is = strFeb 'February - 02
            intMonth = 2
        Case Is = strMar 'March - 03
            intMonth = 3
        'and so on...
    End Select

    'Read year field as an Integer
    Dim intYear As Integer
    intYear = txtYear.Value

    Call Sheet1.Report(intMonth, intYear)

End Sub

Далее приведен код из Report . Это еще не завершено, так как я не смог закончить вызов. Как я упоминал ранее, я всегда сталкиваюсь с ошибкой в строке вызова : Call Sheet1.Отчет (intMonth, intYear) .

Public Sub Report(myMonth As Integer, myYear As Integer)

    'Some incomplete code...
    'Like I said, the macro never gets past the call.

End Sub

Любые идеи, как это исправить? Буду признателен за любую оказанную помощь. Спасибо!

3 ответа3

3

Запуск экземпляра UserForm умолчанию для запуска шоу, пожалуй, самый простой способ, но он также является прямой причиной многих проблем - от простых, но трудно обнаруживаемых ошибок до проблем с обслуживанием и расширяемостью: "nice" & quick, works "решение - это шаблон" Smart UI ", который отлично работает для прототипа. Крупные проекты, которые постоянно растут с течением времени, требуют более умной архитектуры.

Программисты называют это «модель-представление-презентатор». Вид это форма. Данные - это модель, а затем есть презентатор, который координирует все это.

Вызов Sub Sheet из пользовательской формы

Правда в том, что вы этого не делаете. Модальная пользовательская UserForm - это диалоговое окно, роль которого - не что иное, как сбор информации от пользователя. Делая его ответственным только за манипулирование данными, и оставляя макрос / вызывающую сторону ответственным за поток управления, вы делаете код более надежным и простым в обслуживании - особенно если форма может делать много вещей.

Начните с простого модуля класса MonthlyReportParams :

Option Explicit
Public Month As Integer ' encapsulate into properties to implement 
Public Year As Integer  ' logic for validation on assignment.

Public Property Get IsValid() As Boolean
    IsValid = Month >= 1 And Month <= 12 And _
              Year >= 1900 And Year <= 2100
End Property

Теперь все, что нужно сделать пользовательской UserForm - это работать с этими данными, с этой моделью.

Option Explicit
Private params As MonthlyReportParams
Private cancelled As Boolean

Private Sub Class_Initialize()
    Set params = New MonthlyReportParams
End Sub

Public Property Get Model() As MonthlyReportParams
    Set Model = params
End Property

Public Property Set Model(ByVal value As MonthlyReportParams)
    Set params = value
    MonthBox.value = params.Month
    YearBox.value = params.Year
End Property

Public Property Get IsCancelled() As Boolean
    IsCancelled = cancelled
End Property

Private Sub MonthBox_Change()
    ' make sure the textboxes contain numeric values before assigning to Integer
    If IsNumeric(MonthBox.Value) Then params.Month = CInt(MonthBox.Value)
    OnValidate
End Sub

Private Sub YearBox_Change()
    ' make sure the textboxes contain numeric values before assigning to Integer
    If IsNumeric(YearBox.Value) Then params.Year = CInt(YearBox.Value)
    OnValidate
End Sub

Private Sub OkButton_Click()
    Me.Hide
End Sub

Private Sub CancelButton_Click()
    OnCancel
End Sub

Private Sub OnCancel()
    cancelled = True
    Me.Hide
End Sub

Private Sub OnValidate()
    OkButton.Enabled = Model.IsValid
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
' runs when form is just about to close
    If CloseMode = VbQueryClose.vbFormControlMenu Then
        ' user clicked the [X] button
        Cancel = True ' don't destroy the form
        OnCancel
    End If
End Sub

И теперь макрос, который вызывает эту форму, может вернуть контроль над происходящим: форма больше не запускает шоу, и мы можем прочитать все, что происходит, в одном месте:

Public Sub RunMonthlyReport(Optional ByVal targetSheet As Worksheet = Nothing)

    If targetSheet Is Nothing Then
        ' no sheet was specified; work of the ActiveSheet
        Debug.Assert Not ActiveSheet Is Nothing
        Set targetSheet = ActiveSheet
    End If

    ' create the model
    Dim m As MonthlyReportParams
    Set m = New MonthlyReportParams
    m.Month = Month(Now)
    m.Year = Year(Now)

    ' create the dialog, assign the model
    With New MonthlyReportParamsDialog
        Set .Model = m
        .Show ' next line only runs after dialog has closed

        If Not .IsCancelled Then
            ' run the report with the values in the model
            targetSheet.Report m.Month, m.Year
        End If
    End With

End Sub

В этой статье можно найти дополнительную информацию о преимуществах такого "перестановки обязанностей", а также дополнительную логику обратного вызова в этой статье - отказ от ответственности: я написал оба; этот блог является официальным блогом для проекта OSS надстройки Rubberduck VBIDE, которым я владею.

2

Теперь, когда вы доказали, что ваш код работает с моим предложением:

ThisWorkbook.Worksheets("Sheet1").Report intMonth, intYear

Давайте возьмем совет, предоставленный Матье. Нажмите на объект листа в окне Project вашего окна VBE:

VBAProject> Объекты Microsoft Excel> Лист1 (Лист1)

Первая часть - это имя объекта листа, вторая часть в скобках - это имя листа, как показано на вкладке Excel. Откройте окно "Свойства" в раскрывающемся списке "Вид" в VBE или нажмите клавишу F4. Первым делом в окне свойств Worksheet должно быть (name), и это имя объекта, которое вы бы назвали в своем коде. Измените его на что-то описательное, как "Отчет". Затем используйте описательное имя для вашего макроса, например "Обновить".

Теперь вы можете сделать новый отчет по телефону:

Report.Update intMonth, intYear

Я использую это соглашение об именах, потому что я предполагаю, что ваш макрос обновляет лист отчета. Вы также можете воспользоваться его советом по стилю кода «модель-представление-презентатор», но это немного выходит за рамки вашего вопроса.

0

HackSlash ответил на мой вопрос:

Ваш пример кода работал на меня. Помните, что вы используете "Лист1" в качестве объекта как имя объекта листа, а не как имя рабочего листа, отображаемое на вкладке фактического листа Excel. Если вы хотите назвать его по имени листа, попробуйте это: ThisWorkbook.Worksheets("Лист1").Report intMonth, intYear ПРИМЕЧАНИЕ. Оператор Call не требуется, если вы удалите скобки. - HackSlash 7 минут назад

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .