Найти открытую подчиненную форму

#vba #ms-access

#vba #ms-access

Вопрос:

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

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

Вызов формы, передача дескриптора Windows

     OpenCCCustAddr [CustFID], "CCInt", Me.hWnd
 

В событии закрытия формы я пытаюсь установить значения адресов в вызывающей форме,
но GetFormByHWND возвращает null .

 Set frm = GetFormByHWND(Me!txtCallingHWND)
          // Me!txtCallingHWND here is populated and looks reasonable
    frm!BillStreet = strAddr  // This blows up since frm is null
    frm!HolderZipCode = strZip
    frm!AddressUpdated = -1
Set frm = Nothing


Public Function GetFormByHWND(lngHWND As Long) As Form
   Dim frm As Form
   Dim nm As String

   Select Case lngHWND
        Case 0
        Case Else
            For Each frm In Forms
            nm = frm.NAME   // the name of the parent form shows, but not my calling subform
               If frm.hWnd = lngHWND Then
                  Set GetFormByHWND = frm
                  Exit For
               End If
            Next
   End Select
End Function
 

Для каждого и для I = 0 для Count-1 оба дают одинаковые результаты. Форма просто не в формах. Возможно, это потому, что это подчиненная форма.

Я попытался выполнить поиск по подчиненным формам, но это происходит при проверке ctl.hWnd с «Объект не поддерживает это свойство»

 Public Function GetFormByHWND(lngHWND As Long) As Form
   Dim frm As Form
   Dim ctl As Access.Control

   Dim nm As String
   
   Select Case lngHWND
        Case 0
        Case Else
            For Each frm In Forms
nm = frm.NAME
               If frm.hWnd = lngHWND Then
                  Set GetFormByHWND = frm
                  Exit For
               End If
            Next

            Rem If we didn't find the form, check for a subform
            If GetFormByHWND Is Nothing Then
                For Each frm In Forms
nm = frm.NAME
                    For Each ctl In frm.Controls
                        If ctl.Properties("ControlType") = acSubform Then
nm = ctl.NAME
                            If ctl.hWnd = lngHWND Then  // Error: "Object doesn't support this property" 
                                Set GetFormByHWND = ctl
                                Exit For
                            End If
                        End If
                    Next
                Next
            End If
   End Select
End Function
 

Комментарии:

1. Правильно, подчиненной формы нет в коллекции Forms. Ссылка на подчиненную форму через элемент управления контейнером подчиненной формы. Точный синтаксис зависит от того, что нужно сделать с подчиненной формой. Чтобы задать значение элемента управления, например: Forms!mainformName.subformcontainerName.Form.BillStreet . Я никогда не использовал дескрипторы Windows для ссылок на формы. Я просто передаю имя формы. Если вы не открываете несколько экземпляров одной и той же формы, я не вижу необходимости в дескрипторах Windows. Зачем вам нужно устанавливать значения в подчиненной форме с данными из всплывающего окна?

2. @June7 Ваш комментарий привел к ответу. Но я не вижу никакого способа отдать вам должное.

3. Я должен был бы создать ответ для вас, чтобы проголосовать и / или принять. Рад, что помог.

Ответ №1:

Как указал @June7, моя ошибка заключалась в предположении, что элемент управления был формой. Вместо этого имеет форму.

Таким образом, правильное решение

 Rem If we didn't find the form, check for a subform
  If GetFormByHWND Is Nothing Then
      For Each frm In Forms
          For Each ctl In frm.Controls
              If ctl.Properties("ControlType") = acSubform Then
                  If ctl.Form.hWnd = lngHWND Then // note the change here
                      Set GetFormByHWND = ctl.Form
                      Exit For
                  End If
              End If
          Next
      Next
  End If
 

Ответ №2:

Во-первых, совсем не ясно, зачем нужен весь этот код и hwn?

Мы предполагаем, что у вас есть форма.

В этой форме у вас есть кнопка, и она запускает 2-ю форму.

итак, в первом классе у нас есть это:

   ' write data to table before launching form
  If Me.Dirty = True Then Me.Dirty = False
  
  DoCmd.OpenForm "formB"
  
 

Хорошо, теперь в событии FormB при загрузке у нас есть это:

   Option Compare Database
  Option Explicit
  
  Dim frmPrevious      As Form
  Dim frmPreviousSub   As Form
  
  Private Sub Form_Load()
  
     Set frmPrevious = Screen.ActiveForm
     
     Set frmPreviousSub = frmPrevious.MySubFormControl.Form
     
     
     ' do whatever
     
  End Sub
 

Итак, теперь у нас есть как ссылка на предыдущую форму, так и вспомогательная форма.

Допустим, пользователь выбирает некоторый адрес и нажимает кнопку ok.

Затем код выполняет следующее:

 frmPreviousSub!AddressID = me!ID    ' get/set the PK address ID
docmd.Close acForm, me.name
 

Так что нет необходимости во всей этой мировой бедности, захвате и зацикливании hwnd или любых подобных подставках для рук.

Всего несколько хороших чистых строк кода.

Теперь У МЕНЯ ЕСТЬ рекурсивный цикл, который ВСЕГДА возвращает дескриптор формы в качестве ссылки на объект, и вам, таким образом, даже не нужно жестко кодировать имя формы (форм).

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

Адрес компании и адрес отправки. Итак, вы хотите запустить форму B из ЛЮБОЙ из этих двух подчиненных форм, и когда вы выбираете адрес, вы возвращаете это значение, и, таким образом, две или даже потенциальные 3 подчиненные формы могут фактически вызывать таким образом всплывающую форму выбора адреса.

Способ, которым вы это делаете, аналогичен приведенному выше коду, но нам НЕ нужно жестко кодировать подчиненную форму.

Теперь код будет выглядеть следующим образом:

   Private Sub Form_Load()
        
           Dim f       As Form
           Set f = Screen.ActiveForm     ' pick this up RIGHT away - 
                                         ' previous active form only valid
                                         ' in open/load event
           
           ' we have the previous active form, get the sub form.
           
           Set frmPrevous = GetSubForm(f)
    
   End Sub
 

Обратите внимание, что вложенная форма может иметь глубину 3 или даже 5 уровней. Эта процедура является «рекурсивной». Он захватывает предыдущую форму, затем проверяет, имеет ли подчиненная форма фокус. Если подчиненная форма имеет фокус, то она получает этот элемент управления, и если этот элемент управления является подчиненной формой, то он просто продолжает работать до тех пор, пока мы не просверлим эту кроличью нору, и БОЛЬШЕ детализация не может произойти.

Эта процедура должна быть размещена за пределами формы и помещена в ваш стандартный «глобальный» модуль подпрограмм.

   Public Function GetSubForm(f As Form) As Form
  
     Static fs         As Form
     
     If f.ActiveControl.Controltype = acSubform Then
        GetSubForm f.ActiveControl.Form
     Else
        Set fs = f
     End If
  
     Set GetSubForm = fs
  
  End Function
 

Итак, обратите внимание, как, если он обнаружит, что подчиненная форма имеет фокус? Ну, тогда он просто снова вызывает себя с этой формой и продолжает детализацию. В результате не имеет значения, имеет ли форма глубину 1 или 5 уровней. Результирующий «frmPrevous» будет действительной ссылкой на эту подчиненную форму и, следовательно, после выбора или выполнения чего-либо в предполагаемой всплывающей форме? Вы можете установить значение некоторого PK или любого другого, а затем закрыть форму.

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

Если у вас нет общего столбца идентификатора адреса? Тогда наша всплывающая форма должна предполагать, что у вас всегда есть общедоступная переменная, определенная в вызывающей форме.

Скажите ReturnAddressID как долго

Убедитесь, что вы указали значение как общедоступное, скажем, так:

 Public ReturnAddressID as long
 

Итак, теперь в нашей всплывающей форме мы можем сделать это:

 frmPrevious.ReturnAddressID = me!PD
frmPrevous.MyUpdate
 

(мы предполагаем, что все формы, вызывающие всплывающее окно, также имеют общедоступные функции с именами MyUpdate .

Таким образом, теперь у нас есть обобщенный подход и 2 или 10 различных адресных форм, даже если подформы теперь могут вызывать средство выбора одного адреса. Пока любая из этих форм принимает этот общедоступный ReturnAddressID и общедоступную функцию MyUpdate , мы можем передать обратно значения, и MyUpdate вставит / примет / установит ReturnAddressID в любой столбец и значение, которые вы используете для идентификатора адреса в этой вспомогательной форме.

И, конечно, если вложенных форм нет, процедура просто вернет самую верхнюю форму, которая вызвала всплывающую форму.

Комментарии:

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

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

3. «совсем не понятно, зачем требуется все это hwn?» Я тоже не уверен, но все приложение написано именно так.

4. первый фрагмент кода демонстрирует выбор предыдущей формы, а не вспомогательной формы. Большинство не понимают, что ТЕКУЩАЯ форма НЕ становится ScreenActive. Формируйте ДО ТЕХ ПОР, ПОКА события события открытия (первое), а затем события загрузки (второе) не будут завершены на 100% и не выйдут из заглушки кода. Таким образом, вы можете безопасно получить предыдущую форму таким образом. Но, как показывает мой первый фрагмент кода, вы хотите получить / захватить / использовать / установить активную форму экрана как можно БЫСТРЕЕ и как можно РАНЬШЕ. Это связано со многими вещами, такими как формы таймера и т. Д., Которые могут захватывать / принимать / изменять активную форму. Как только предыдущая форма будет захвачена, переходите к гонкам

5. И последнее, но не менее важное? Вы можете перенести ОДНУ и ТУ ЖЕ форму в качестве вспомогательной формы 5 раз в основную форму. Таким образом, элемент управления вложенной формой — это всего лишь указатель на экземпляр формы. На самом деле вы даже можете открыть 5 копий основной формы — это просто экземпляры класса, и вы можете создать столько экземпляров данного класса form и отобразить его. Единственная часть, которая сбивает с толку, — это то, что коллекция forms() не может быть использована — и это также справедливо для вложенных форм (на самом деле они не добавляются в коллекцию forms () таким образом, чтобы мы могли использовать коллекцию forms().