пятница, 23 октября 2009 г.

Улучшенный макрос для вставки формул с нумерацией

Следуя замечанию Иероглиф, я изменил первый вариант макроса. К сожалению, полностью избавиться от Selection невозоможно, потому что нужен метод InsertStyleSeparator.
1 Sub NumberEquation()
2 '
3 'Вставка формулы с нумерацией.
4 '
5 Dim oRng As Range
6 Set oRng = Selection.Range
7 With oRng.ParagraphFormat
8 .SpaceAfterAuto = False: .SpaceBeforeAuto = False
9 .FirstLineIndent = 0
10 .TabStops.ClearAll
11 .TabStops.Add (oRng.Sections(1).PageSetup.PageWidth - oRng.Sections(1).PageSetup.LeftMargin - oRng.Sections(1).PageSetup.RightMargin - .LeftIndent - .RightIndent) / 2, _
12 wdAlignTabCenter, wdTabLeaderSpaces
13 .TabStops.Add oRng.Sections(1).PageSetup.PageWidth - oRng.Sections(1).PageSetup.LeftMargin - oRng.Sections(1).PageSetup.RightMargin - .LeftIndent - .RightIndent, _
14 wdAlignTabRight, wdTabLeaderSpaces
15 End With
16 With oRng
17 .InsertBefore vbTab
18 .Collapse wdCollapseEnd
19 .InsertParagraphAfter
20 .Select
21 With Selection
22 .InsertStyleSeparator
23 .TypeText vbTab & "("
24 .Fields.Add .Range, wdFieldSequence, "formula", True
25 .TypeText ")"
26 End With
27 .InlineShapes.AddOLEObject "Equation.3", , False
28 .InlineShapes(1).Select
29 End With
30 End Sub

Динамическое создание пользовательской формы

Иногда возникает необходимость создать форму не вручную, используя встроенный редактор, а программно, в зависимости от внешних условий. К счастью, VBA предоставляет и такую возможность. Здесь я приведу пример как можно создать форму, поместить на нее заданное количество текстовых полей и кнопку. А также указать обработчики событий для каждого элемента.
Основная идея состоит в том, что сначала нужно программно добавить в проект форму, затем на эту форму добавить компоненты, и связать компоненты с событиями. Нам придется создать в проекте три модуля: обычный программный модуль и два модуля класса. В одном модуле класса мы определим коллекции, в которые будем добавлять компоненты по мере их добавления на форму. Второй класс будет отвечать за события, происходящие с нашими компонентами.
1 Option Explicit
2 Dim COF As ControlsOnForm, EOC As EventsOfControls
3
4 Sub CreateForm(NumberOfTextBoxes As Integer)
5 'Добавляем пустую форму и код процедуры, которая выполняется при запуске формы
6 Dim oNewForm 'Новая форма
7 Dim sCode 'Переменная для хранения кода формы
8 ' Application.VBE.MainWindow.Visible = False
9 'Создаем форму
10 Set oNewForm = ThisDocument.VBProject.VBComponents.Add(3) 'vbext_ct_MSForm
11 'Добавляем конструкторы классов для событий формы
12 sCode = ""
13 'Создаем процедуру инициализации формы
14 sCode = sCode & "Sub UserForm_Initialize()" & vbCrLf
15 sCode = sCode & vbTab & "GetControlsForForm Me, " & NumberOfTextBoxes & vbCrLf
16 ' sCode = sCode & vbTab & "UpdateControls" & vbCrLf
17 sCode = sCode & "End Sub" & vbCrLf
18 'Вставляем код формы
19 oNewForm.CodeModule.InsertLines oNewForm.CodeModule.CountOfLines + 1, sCode
20 'Показываем форму
21 VBA.UserForms.Add(oNewForm.Name).Show
22 'Удаляем форму из рабочей книги
23 ThisDocument.VBProject.VBComponents.Remove oNewForm
24
25 End Sub
26 'Процедура, создающая указанное количество текстовых полей на указанной форме, и кнопку
27 Sub GetControlsForForm(ByVal oForm As Object, ByVal NumberOfTextBoxes As Integer)
28 'Переменные для объектов на форме
29 Dim oBtn As MSForms.CommandButton
30 Dim oTxt As MSForms.TextBox
31 'Верхний левый угол компонента
32 Dim iLeft As Integer, iTop As Integer
33 'Счетчик
34 Dim i As Integer
35 'Максимальный размер формы
36 Dim iMaxWidth As Integer, iMaxHeight As Integer
37
38 'Заполняем форму компонентами
39 Set COF = New ControlsOnForm
40 iLeft = 6: iTop = 6 'Верхний левый угол первого текстового поля
41 For i = 1 To NumberOfTextBoxes
42 'Создаем кнопку
43 Set oTxt = oForm.Controls.Add("Forms.TextBox.1", "txtNumber" & i)
44 With oTxt
45 .Text = .Name
46 .Left = iLeft: .Top = iTop
47 iTop = iTop + .Height
48 iMaxWidth = iLeft + .Width + 6
49 iMaxHeight = iTop - .Height + 20
50 End With
51 'Связываем текстовое поле с обработчиком событий
52 Set EOC = New EventsOfControls: Set EOC.TextBoxEv = oTxt
53 'Добавляем в коллекцию
54 COF.TextBoxes.Add EOC
55 Next i
56 'Теперь добавляем кнопку
57 Set oBtn = oForm.Controls.Add("Forms.CommandButton.1", "btnTest")
58 With oBtn
59 .Caption = "Моя кнопка"
60 .Left = iLeft
61 .Top = iTop
62 iMaxWidth = iLeft + .Width + 6
63 iMaxHeight = iTop + .Height + 20
64 End With
65 'Связываем кнопку с обработчиком событий
66 Set EOC = New EventsOfControls: Set EOC.ButtonEv = oBtn
67 'Добавляем в коллекцию
68 COF.Buttons.Add EOC
69 'Подгоняем размеры формы
70 oForm.Height = iMaxHeight: oForm.Width = iMaxWidth
71 End Sub
72
73 'Основная процедура, запускающая весь процесс
74 Sub СоздатьФорму()
75 Dim TBCount As Integer
76 Dim sInput As String
77 Do
78 sInput = InputBox("Введите число нужных текстовых полей", "Динамическое создание формы", 5)
79 If Len(sInput) = 0 Then Exit Sub 'Если ничего не ввели, или нажали отмену
80 'Если ввели не число, предлагаем ввод еще раз
81 If IsNumeric(sInput) Then TBCount = CInt(sInput) Else sInput = ""
82 Loop Until Len(sInput) > 0
83 CreateForm (TBCount)
84 End Sub

Думаю, что дополнительные комментарии излишни. После отработки кода на экране появится форма, но в документе ее уже не будет, потому что она удаляется из документа после показа.
1 Option Explicit
2 'Класс, для хранения в коллекциях элементов формы
3 Public Buttons As New Collection
4 Public TextBoxes As New Collection

1 Option Explicit
2 'Этот класс содержит в себе обработчики событий для различных элементов формы
3 'Элемент, ответсвтвенный за обработку событий в текстовом поле
4 Public WithEvents TextBoxEv As MSForms.TextBox
5 'Обработчик событий кнопки
6 Public WithEvents ButtonEv As MSForms.CommandButton
7
8 Private Sub ButtonEv_Click()
9 Dim oForm As Object
10 Set oForm = Me.ButtonEv.Parent
11 MsgBox "Вы кликнули на кнопке " & Me.ButtonEv.Name & vbCr & _
12 "На форме расположено " & Me.ButtonEv.Parent.Controls.Count - 1 & " текстовых полей", vbInformation, _
13 "Динамическое создание формы"
14 End Sub
15 'Обработчик двойного нажатия в текстовом поле
16 Private Sub TextBoxEv_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
17 MsgBox "Вы дважды кликнули мышкой в текстовом поле " & Me.TextBoxEv.Name & "!", vbInformation, _
18 "Динамическое создание формы"
19 End Sub

Здесь можно описать любые события, доступные для того или иного компонента. Это очень удобно.
Подобным образом можно создавать довольно сложные формы. Например, такую форму, которая автоматически генерировалась на основе документа Word, в котором содержались экзаменационные вопросы по информатике для слушателей курсов. Документ Word был специальным образом отформатирован. Пользователь последовательно проходил по вопросам, ставя галочки на правильных ответах. На последнем вопросе активировалась кнопка «Готово» и программа считала результат теста.