#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().