Отображение альтернативной пользовательской формы в зависимости от переменной - PullRequest
1 голос
/ 23 января 2020

Я хочу отобразить указанную c UserForm в зависимости от переменной среды UserName. Я обновляю пользовательскую форму в разных местах кода, поэтому я подумал, что было бы проще (и тривиально) создать две независимые пользовательские формы (с совершенно разными конструкциями), а затем с помощью logi c «установить» переменную объекта UserForm в начале код. Я явно что-то здесь неправильно понимаю, потому что, когда дело доходит до команды .Show, возникают ошибки VBA:

Dim usrForm As UserForm

If Environ("UserName") = "redacted" Then
    Set usrForm = LlamaForm 'for specific user, form styled differently including picture of Llama
Else
    Set usrForm = NormalForm 'for EVERYONE ELSE, normal professional looking form
End If

With usrForm 'initialize UserForm and display wait message
    .Cancelbutton.Visible = False
    .Proceedbutton.Visible = False
    .Exitbutton.Visible = False 
    .labmsg.Caption = Chr(10) & Chr(10) & Chr(10) & "Starting background processes, please wait..."
    .Show vbModeless
End With

Я делаю это слишком сложным? Я действительно надеялся просто изменить ссылочный объект формы в начале, а не вводить logi c с избыточным кодом каждый раз, когда мне нужно обновить пользователя. Любые идеи или улучшения будут оценены. Предостережение заключается в том, что, поскольку они сильно отличаются друг от друга макетом / дизайном, я бы действительно хотел бы сохранить две отдельные формы пользователя, а не манипулировать одной (что, я знаю, может быть сделано, но это больше работы на данном этапе по сравнению чтобы понять, почему мой метод выше не работает.)

Ответы [ 2 ]

4 голосов
/ 23 января 2020

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

Возникает ошибка 438, поскольку класс UserForm не имеет CancelButton, ProceedButton, ExitButton или labmsg членов.

Решение состоит в том, чтобы либо потерять раннее связывание и позднее связывание этих вызовов участников, сделав их всех go против Object (или Variant, но Object здесь более уместно), ... или переоценка того, кто за что отвечает.

Похоже, вы делаете что-то вроде индикатор прогресса ; когда вы начинаете нуждаться в заменяемых объектных компонентах и поддерживать раннее связывание - это необходимость , правильный инструмент для этого - полиморфизм , то есть интерфейсы.

Связанная статья описывает, как сделать многоразовый индикатор прогресса, рабочий код которого полностью отделен от формы индикатора. Вам нужно что-то вроде этого, за исключением упомянутого в конце статьи, вы хотите, чтобы ProgressIndicatorForm и LlamaIndicatorForm реализовали некоторый интерфейс IProgressView, и имели бы этот IProgressView воспринимается как зависимость от класса ProgressIndicator, а не встроена в код инициализации.

Сложной частью будет управление выставлением событий индикатора прогресса на интерфейсе - для этого потребуется формальный класс ProgressIndicatorEvents для пересылки событий Activated и Cancelled в класс ProgressIndicator, аналогично тому, как этот класс перенаправляет события из абстрактного представления в другой компонент.

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

Dim progressForm As IProgressView
If Environ$("username") = "redacted" Then
    Set progressForm = New LlamaProgressForm
Else
    Set progressForm = New StandardProgressForm
End If
With ProgressIndicator.Create("DoWork", Form:=progressForm)
    .Execute
End With

Где DoWork - ваш «рабочий код» - может быть любой Sub процедурой, которая принимает параметр ProgressIndicator.

Очевидно, что это намного больше, чем просто кодирование против Object, и я бы не стал винить вас за то, что вы пошли по простому маршруту. Но действующие здесь принципы заслуживают изучения, если изучение новых концепций программирования важнее, чем просто заставить его работать.

4 голосов
/ 23 января 2020

Вы можете использовать универсальный c объект, но вы потеряете ранние функции привязки

Попробуйте этот код

Option Explicit

Public Sub ShowUserForm()

    ' You can use a generic object, but you lose the early binding features
    Dim myUserForm As Object

    If Environ("UserName") = "redacted" Then
        Set myUserForm = New LlamaForm 
    Else
        Set myUserForm = New NormalForm 
    End If

    myUserForm.Show

End Sub

Дайте мне знать, если он работает

...