Archive for the ‘решения’ Category
Как отключить скролинг записей формы
Для некоторых пользователей колесико мышки является проблемой. Так как они как правило не смотрят на экран, когда меняют что-либо. Нечаянно крутанулось колесико и поменяли не ту запись.
MS Access 2003 поддерживает событие MouseWheel, но, увы, не позволяет отменить это. Даже DoCmd.CancelEvent не помогает.
Private Sub Form_MouseWheel(ByVal Page As Boolean, ByVal Count As Long)
DoCmd.CancelEvent '-- не работает
End Sub
Одно из решений это хук событий формы описанный на сайте microsoft (How to Detect and Prevent the Mouse Wheel from Scrolling Through Records in a Form). И тут не обошлось без недостатков. Если не использовать внешней dll, то периодически форма тормозит, т.к. все события проваливаются через медленный VBA. Возможно, если вынести данный код в dll, то будет работать на порядок быстрее. Мне не захотелось возится с отдельной dll, т.к. я нашел метод попроще (об этом ниже). Пока я исследовал пример, немного расширил его. Результат трудов можно скачать тут, вдруг кому понадобится.
Более простым решением на мой взгляд является фильтр по ключевому полю. При этом нужно отключить добавление записей. У этой идеи тоже есть недостатки:
- для ленточных форм, естественно, это не работает.
- нужно реализовывать добавление записи, а потом уже открывать ее на редактирование.
Какой вариант выбрать зависит от ситуации. Я же стараюсь свести дизайн программы ко второму варианту. Уж больно он мне по душе.
До встречи!
(с) Скоков Сергей
Подписаться на: RSS или e-mail рассылку или добавить в ЖЖ друзья.
Как из номера квартала получить дату или период для поиска
Недавно на моем любимом форуме проскакивал одноименный ответ. Сперва я сам как-то задумался, а как же. Но как оказалось задача “По номерам недель получить временной промежуток для фильтра по датам” оказалось проще простого. Для этого я воспользовался любимым методом “Разделяй и властвуй”.
И так, задача разбилась на три части:
1. Функция, которая вернет дату начала недели по номеру недели и году.
Public Function GetDateOfStartWeek(ByVal nWeek As Byte, ByVal nYear As Integer) As Date
Dim dt1Jan As Date
Dim dayFirstWeek As Integer
Dim dtResult As Date
dt1Jan = DateSerial(nYear, 1, 1)
dayFirstWeek = Weekday(dt1Jan, vbMonday)
dtResult = DateAdd("ww", nWeek - 1, dt1Jan)
If dayFirstWeek > 1 And nWeek > 1 Then
dtResult = DateAdd("d", -1 * (7 - Weekday(dt1Jan, vbMonday) + 2), dtResult)
End If
GetDateOfStartWeek = dtResult
End Function
2. Функция, которая вернет дату окончания недели по номеру недели и году.
Public Function GetDateOfEndWeek(ByVal nWeek As Byte, ByVal nYear As Integer) As Date
GetDateOfEndWeek = DateAdd("d", 7 - 1, GetDateOfStartWeek(nWeek, nYear))
End Function
3. Фнункция, которая вернет условие за период по датам:
public frunction GetWherePeriod(ByVal imFld as string, ByVal dtFrom as Date, ByVal dtTo as Date) as String GetWherePeriod = BuildCriteria(imFld, dbDate, "Between " & dtFrom & " And " & dtTo & "") End Public '-- Проверка Debug.Print GetWherePeriod(GetDateOfStartWeek(1, 2010), GetDateOfEndWeek(1, 2010))
Как-то уж очень просто получилось… Функцию GetDateOfStartWeek() я активно не тестировал, проверял только для января за 2010 и 2007 года. Если будут ошибки, пишите.
До встречи!
(с) Скоков Сергей
Подписаться на: RSS или e-mail рассылку или добавить в ЖЖ друзья.
Как получить значение поля таблицы
Очень часто новички сталкиваются с задачей прочитать значение из поля таблицы. Например, они знают о существовании библиотек DAO или ADO и пишут свою функцию для вычисления этой операции (или не пишут, а идут на форумы спрашивать
). Но ведь есть встроенные. Знакомьтесь:
- DLookUp
- DFirst
- DLast
- DCount
- DMax
- DMin
У всех этих функций одинаковые параметры: имя поля или выражение, таблица, условие отбора. Возвращать они могут Null (кроме DCount), по этому использование nz() не будет лишним.
И напоследок пара примеров использования:
Public Sub test()
Dim stKlient As String
Dim lK_KLIENT As Long
Dim count As Long
lK_KLIENT = 123
stKlient = Nz(DLookup("T_KLIENT", "b_klient", "K_KLIENT = " & lK_KLIENT))
count = DCount("*", "b_klient", "T_KLIENT LIke '" & Left(stKlient, 3) & "*'")
End Sub
До встречи!
(с) Скоков Сергей
Подписаться на: RSS или e-mail рассылку или добавить в ЖЖ друзья.
Как создать меню в MS Access
Всем привет!
Как оказалось очень полезно смотреть ключевые слова, по которым пользователи переходят на мой сайт. В них можно найти новые тему. Например, как эту. Оказывается, у некоторых это вызывает трудности.
Как создать меню
В прошлой статье про меню я забыл дорасказать, как же все же добавляеть новые пункты меню в строку меню. По-этом рекомендую сперва ознакомится с прошлым постом и пеерходить к чтению этого.
Как создать пункты меню

Чтобы сюда попасть можно выбрать пункт меню MS Access “Сервис – Настройка” или кликнуть в пустом месте меню и в контестном меню выбрать пункт “Настройка”.

Теперь все внимание на вкладку “Команды” нового окошка.
В категории “Файл” вы найдете пункт меню “Специальная”. Это рабочая лошадка меню. На самом деле это кнопка.

В категории “Новое меню” найдете выпадающее меню “Новое меню”. На самом деле это поле с выпадающим списком.
Еще в категории “Все формы” и т.п. вы найдете соостветственно готовые пункты меню. У них есть свои особенности. Об этом ниже.
Это все “многообразие” элементов можно при помощи перетаскивания перенести на свою строку меню или панель инструментов (я надеюсь вы прочитали предыдущую статью и знаете как это делать).

Как выполнить действие
Тут самое интересное. Только не закрывайте чудесное окошко “Настройка”. Кликните правой кнопочкой мыши на любом пункте меню (не список!) и в контекстном меню выберете пункт “Свойства”. У вас откроется следующее окошко:

Все поля содержат описание и понятны. При помощи этого пункта меню можно вызвать любую функцию из модуля, написав так =MyFunction(). Чаще всего я использую именно этот способ, т.к. частенько формы открываются с какими-то параметрами.
Особенности открытия форм
А теперь давайте таким же образом откроем пункт меню, созданный перетаскиванием пункта из группы “Все формы”, например.

В свойстве “Параметр” вы обнаружите имя формы. Так вот, если прописать это же имя в пункте меню из группы “File”, то форма не откроется. Вот такой чудесный нюанс.
В следующий раз опишу о программном создании и управлении меню.
До встречи!
(с) Скоков Сергей
Подписаться на: RSS или e-mail рассылку или добавить в ЖЖ друзья.
Недостатки DoCmd.GoToRecord и достоинства CodeContextObject
Пара камней в огород DoCmd
Как-то мне понадобилось перейти к последней записи в подчиненной форме. Перейти к последней записи можно разными способами, простейший – это вот эта команда: DoCmd.GoToRecord , , acLast. Вот с подчиненненными формами есть нюанс – нужно вызывать ее в обработчике события или методе подчиненной формы. В моем случае события не подходили, метод было реализовывать влом и очень хотелось реализовать это на главной форме.
Поэтому пришлось написать свою функцию. Благодаря тому, что функция принимает ссылку на форму, а не имя, то можно ее использовать для любой формы, подчиненной, созданной при помощи DoCmd.OpenForm или оператора New.
Public Sub CM_FormGoToLastRecord(Optional ByRef frm As Form = Nothing)
' перейти на последнюю запись
' frm можно не передавать, в этом случае она будет получена при помощи CodeContextObject
On Error GoTo Err_
If frm Is Nothing Then
Set frm = CodeContextObject
End If
If frm Is Nothing Then Exit Sub
Dim rst As DAO.Recordset
Set rst = frm.RecordsetClone
If Not rst.EOF Then rst.MoveLast
frm.Bookmark = rst.Bookmark
Exit_:
Exit Sub
Err_:
mc_Log.MC_LogErr ("CM_FormGoToLastRecord()")
Resume Exit_:
End Sub
mc_Log.MC_LogErr ("FormGoToLastRecord()") - фукнция, которая логирует ошибку в текстовом файле. О ведении лога я как-нибудь расскажу попозже.
CodeContextObject
В процедуре CM_FormGoToLastRecord() используется такая штука, как CodeContextObject, которое в справке называют свойством. Это свойство позволяет получить ссылку на объект, в рамках которого выполняется данный код. Если вызвать процедуру CM_FormGoToLastRecord() в событии подчиненной формы, то CodeContextObject выдаст ссылку на подчиненную форму. Этот же способ использует DoCmd.GoToRecord, когда не указывается имя объекта.
До встречи!
(с) Скоков Сергей
Подписаться на: RSS или e-mail рассылку или добавить в ЖЖ друзья.
Как получить доступ к свойствам диаграммы и других ActiveX объектов
MS Access поддерживает технологию ActiveX. Но те возможности, которые предоставляет стандартный элемент управления OLE объектами весьма скудны. А иногда бывает необходимо управлять скрытыми свойствами ActiveX объекта. Как же добраться до свойств объектов ActiveX?
Рассмотрим эту задачку на примере диаграмм.
На форму (или отчет) вставим диаграмму с именем “OLE_PRODAZHA_CHART” и изменим название диаграммы программно.
Далее необходимо подключить библиотеку “Microsoft Graph X.0 Object Library”, чтобы тип “Chart” (диаграмма) стал доступен (ниже узнаете зачем он нужен). Увы для диаграмм Access почему-то не добавляет ссылку на библиотеку.
Также добавим на форму кнопку с названием “Изменить название диаграммы”. В обработчике событий разместим следующий код:
Dim obj As Graph.Chart Set obj = Me.OLE_PRODAZHA_CHART.Object obj.ChartTitle.Text = "Мое название"
Весь фокус заключается в типе “Graph.Chart” и свойстве “Object” элемента управления. Свойство “Object” элемента управления возвращает ссылку на созданный объект. Библиотека “Microsoft Graph X.0 Object Library” в коде называется Graph и предоставляет класс типа Chart. Теперь можно пользоваться справкой и просмотреть все свойства и методы класса Graph.Chart.
Как быть с другим ActiveX объектами?
После вставки ActiveX элемента на форму, Access сам добавить Reference на библиотеку типов. Найти библиотеку и тип ActiveX элемента можно найти в Object Browser (в редакторе кода можно нажать кнопку F2).
Чтобы точно определить имя нужной библиотеки и типа ActiveX элемента, давайте посмотрим на свойства уже ранее созданной диаграммы. А именно “Класс” и “Класс OLE”.
Для диаграммы свойства имеют значения:
- Класс = MSGraph.Chart.8
- Класс OLE = Microsoft Graph Chart
“Класс OLE” содержит описание для пользователя. По этому имени можно найти библиотеку типов списке ссылок (Tools->References). “Класс” содежит имя класса, при помощи которого можно создать этот объект.
Возвращаясь к злосчастной диаграмме, в Object Browser имя библиотеки будет Graph, а имя класса Chart.
В примере вы также найдете ActiveX элемент TreeView, посмотрите код в модуле формы. Там добавлена обработка события, которого нет в списке событий в свойствах элемента управления.
До встречи!
(с) Скоков Сергей
Подписаться на: RSS или e-mail рассылку или добавить в ЖЖ друзья.
Как получить значение ключевого поля для добавленной записи
Всем привет!
Как оказалось вопросы по этой теме все еще возникают. Рассказываю
.
Значение ключевого поля новой записи при помощи библиотеки DAO можно получить следующим образом (K_KLIENT – ключевое поле):
Dim rst as DAO.Recordset
Dim lPk as Long
' ....
rst.AddNew
rst("T_KLIENT")="ОАО Рога и копыта"
' ...
А дальше есть несколько вариантов:
- 1-й вариант:
lPk = rst("K_KLIENT")
rst.Update
- 2-й вариант:
rst.Update
rst.Bookmark = rst.LastModified
lPk = rst("K_KLIENT")
1-й вариант работает только с таблицами MS Access, т.к. значение счетчика вычилсяется сразу же после AddNew. На всех остальных нужно проверять. Например, с ODBC таблицами 1-й вариант точно не прокатит, т.к. значение счетчика вычисляет сервер.
C библиотекой ADO получится только 1-й вариант.
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset
rst.Open "tbl1", CurrentProject.Connection, adOpenKeyset, adLockOptimistic
rst.AddNew
MsgBox rst("key")
rst.Update
А с серверами БД придется что-то придумывать, например, использовать TimeStamp, чтобы найти только что добавленную запись.
До встречи!
(с) Скоков Сергей
Как показать первое значение в поле со списком
Здравствуйте, уважаемые читатели.
Поделюсь с вами еще одной удобной функцией. У меня часто возникает ситуация, когда при открытии формы в поле со списком необходимо показать первое значение.
Например, на форме списка пользотваль хочет видеть данные за текущий год. Для этого я добавил на форму поле со списком “Год” (Имя элемента управления P_GOD). В нем выводятся года, за которые есть данные в обратном порядке. А при открытии формы вызвал специальную фукнцию:
Call CM_ListSelectFirst( Me.P_GOD_FLT ) '-- мега функция '-- увы при программном изменении значения поля события не происходят, '-- то придется еще вызвать фукнцию соотвествующую фукнцию Call SetFilter()
А вот код той самой функции CM_ListSelectFirst():
Public Sub CM_ListSelectFirst(ByRef ctl As Control)
' Скоков С.А. 2009-02-25
' Выбрать первое значение в поле со списком или списке
With ctl
If Not (.ControlType = acListBox Or .ControlType = acComboBox) Then
Exit Sub
End If
If .ListCount > 0 Then
.Value = Nz(.ItemData(0))
End If
End With
End Sub
P.S. Эта же функция будет работать для списков
.
До встречи!
(с) Скоков Сергей
Хитрость печати отчетов
Здравствуйте, уважаемые читатели.

В одной из программ реализована функциональность печати адресов клиентов на конверты, в результате пользователь получает готовенький документ MS Word. Клиент попросил добавить свой логотип на каждый конверт. Существующая реализация формирования документа в Word не позволяет дублировать картинку для каждой страницы. Переделывать полностью не хотелось. Поэтому я искал как обойтись меньшей кровью.
Идея пришла из ниоткуда. Я мониторил меню Word 2007 в поисках пункта записи макросов (может они и могли мне помочь, но увы
) и наткнулся на создание подложки – “Разметка страницы – Подложка – Настраиваемая подложка” (“Формат – Фон – Подложка” для Word 2003).
Как оказалось это отличная идея. Рисунок располагается на странице за текстом, размер и положение можно изменить в режиме изменения колонтитулов.
Супер. Просто и со вкусом. Пример можно посмотреть тут.
До встречи!
(с) Скоков Сергей
Округления
Здравствуйте, уважаемые читатели.

До Access 2002 (или 2000) встроенной функции округления не было. Поэтому все пользовались самописными. Потом появилась функция Round(). Тут можно было вдохнуть с облегчением и избавится от собственной.
Как-то заказчик прислал мне скриншот с неправильными расчетами. Анализ кода показал, что функция Round() дала сбой и округляла следующим образом (проверял в окне отладки):
9.5 =10
8.5 = 8
7.5 = 8
6.5 = 6
5.5 = 6
4.5 = 4
3.5 = 4
2.5 = 2
1.5 = 2
0.5 = 0
Разбираться было лень. В результате заменил на собственное творение, т.к. это деньги:
Public Function CM_Okrugl(ByVal dbl As Double) As Long
' округляет число dbl до целого
Dim lng As Long
lng = Fix(dbl)
If (dbl - lng) < 0.5 Then
CM_Okrugl = lng
Else
CM_Okrugl = lng + 1
End If
End Function
До встречи!
(с) Скоков Сергей