Основы офисного программирования и язык VBA

Приложение

Разбить на страницы
Показывать лекцию целиком

Вместо заключения

Итак, книга, посвященная, главным образом, языку VBA, закончена. Я начал работать над второй книгой, в которой, собираюсь подробно рассказать об объектах каждого из приложений Office 2000 - Word, Excel, Access, Outlook, сопровождая описание проектами, в которых участвуют объекты этих приложений. Особое внимание хочется уделить применениям в Интернет. Пока, это еще только планы. Пишу об этом по той причине, что надеюсь получить от Вас письма с предложениями, а, возможно, и готовыми проектами, небольшими, но интересными для многих, которые Вы хотели бы поместить в книгу.

Теперь несколько слов о законченной книге. Знаю по себе, что книги редко отвечают на вопросы, требующие сиюминутного решения. Цель книг другая. При прочтении хорошей книги число интересных вопросов, которые можно задать, по крайней мере, самому себе должно только возрастать. И вся прелесть состоит в том, что на эти вопросы удается найти ответы. В поисках ответов мы часто обращаемся к друзьям, коллегам, а иногда, когда есть такая возможность, и к автору книги. Я получаю достаточно много писем с различными вопросами по поводу офисного программирования. Не всегда, но, по возможности, стараюсь на них отвечать. Как правило, ответы на поставленные вопросы требуют знания чуть большего числа деталей, в сравнении с тем, что, по понятным причинам, удалось изложить в рамках книги. В качестве примера, приведу два вопроса из одного из последних писем, которое я получил от Ивана Кряжева - читателя нашей предыдущей книги по Офисному программированию. Полагаю возможным и полезным поместить мой подробный ответ на вопросы этого письма.

Вопрос первый: "Как сохранить в форме программно добавленные элементы управления так, чтобы они появлялись при повторном ее открытии?"

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

Первым делом я спроектировал форму с двумя элементами управления, включив ее в состав тестового документа. На самом тестовом документе я разместил 4 кнопки, управляющие работой формы. Кнопки Show и Hide показывают и прячут форму. Кнопка "Add Controls to Form" программно добавляет элементы управления на период существования формы, то есть до тех пор, пока форма не будет закрыта. Наконец, кнопка "Add Design Controls" решает поставленную задачу, добавляя элементы управления, как теперь принято говорить, на постоянной основе. Вот как выглядит тестовый документ с командными кнопками:

(рис П-1) Тестовый документ с командными кнопками

А вот как выглядит сама форма при ее открытии в ответ на щелчок кнопки Show:

(рис П-2) Спроектированная форма при ее открытии

Вот на какие моменты при решении этой задачи следует обратить внимание:

  • Спроектированная мной форма не является модальной. Это не является принципиальным в поставленной задаче, тем не менее, может быть важным, поскольку позволяет произвести изменение формы вне самой формы. О модальности форм следовало бы поговорить в основном тексте книги, но я этого, к сожалению, не сделал. Поэтому поговорим об этом сейчас. Формы Office 2000 обладает новым важным булевым свойством Modal, значение которого можно установить в момент проектирования. Если это значение равно True, то окно формы является модальным и его нельзя покинуть до закрытия. Если значение - False, то можно покинуть форму, поработать с документом, нажимать, например, другие кнопки в документе, модифицируя, возможно, саму форму. Заметим, что в Office 97 создать не модальное окно можно было только, используя объекты Balloon, входящие в состав объекта Assistant. Теперь сделан важный шаг в правильном направлении, - центральный объект интерфейса - UserForm получил возможность работать в немодальном окне. Главное, что для программиста это не требует никаких усилий, достаточно изменить значение одного свойства. Замечу, что в С++, по крайней мере, в версии 4 работа с немодальными окнами требовала профессиональных знаний, в частности, умения посылать сообщения операционной системе. Но вернемся к нашей задаче.
  • Элементы управления достаточно просто программным путем вставляются в форму в период выполнения программы, и я об этом рассказывал в главах, посвященных проектированию интерфейса. У объекта Form есть коллекция Controls, у которой есть метод Add, позволяющий добавить любой из элементов управления. В нашем примере мы уже спроектировали форму с двумя элементами управления - командной кнопкой и окном редактирования. В обработчиках события Click командной кнопки самой формы и командной кнопки "Add Controls to Form", встроенной в тестовый документ, вызывается процедура AddControls, которая добавляет в форму еще пару таких же элементов управления. Заметьте, поскольку наша форма не является модальной, то нажимать командные кнопки можно как в самой форме, так и вне формы. Вот текст процедуры, добавляющей в форму элементы управления:
    Public Sub AddControls()
    	'Эта процедура добавляет командную кнопку и окно редактирования в форму
    	With UserForm1
    		
    		'Добавляем окно редактирования
    		Set Mycmd = .Controls.Add("Forms.TextBox.1", "ProgramBox", True)
    		Mycmd.Left = .TextBox1.Left
    		Mycmd.Top = .TextBox1.Top + 100
    		Mycmd.Width = .TextBox1.Width
    		Mycmd.Height = .TextBox1.Height
    		Mycmd.Text = "New Control - "  Mycmd.Name
    		
    		'Добавляем командную кнопку
    		Set Mycmd = .Controls.Add("Forms.CommandButton.1", "ProgramButton", True)
    		Mycmd.Left = .CommandButton1.Left
    		Mycmd.Top = .CommandButton1.Top + 100
    		Mycmd.Width = .CommandButton1.Width
    		Mycmd.Height = .CommandButton1.Height
    		Mycmd.Caption = "ProgramButton"
    				
    	End With
    	
    End Sub
  • Вот как выглядит форма, после того как была нажата кнопка "Add Controls to Form" и ее обработчик вызвал процедуру (рис П-3) Форма с программно добавленными элементами управления
  • Добавленные таким путем элементы управления живут в форме только до ее закрытия. Но, заметьте, если скрыть форму, вызвав метод Hide, то при повторном ее открытии элементы управления сохраняются.
  • Перейдем теперь непосредственно к ответу на первый поставленный вопрос. Для его решения потребуются средства, ранее не упоминавшиеся в книге. Нам понадобятся объекты класса VBComponent - компоненты VB-проекта (объекта класса VBProject ). Чтобы можно было с ними работать, необходимо включить дополнительную ссылку на библиотеку VBIDE (Visual Basic Extensibility). Ну, а теперь отвечаю конкретно на первый поставленный вопрос. Можно программно добавить элементы управления так, чтобы они сохранялись в форме точно также как сохраняются элементы, созданные в период проектирования. Для этого нужно работать с объектом, представляющим форму в режиме проектирования, используя свойство Designer объекта VBComponent. Вот как это делается:
  • Public Sub AddDesignControls()
    	'Эта процедура добавляет командную кнопку и окно редактирования в форму
    	Dim MyForm As UserForm
    	'Создаем форму этапа проектирования
    
    	Set MyForm = ActiveDocument.VBProject.VBComponents("UserForm1").Designer
    	With MyForm
    
    		'Добавляем окно редактирования
    		Set Mycmd = .Controls.Add("Forms.TextBox.1", "ProgramBox", True)
    		Mycmd.Left = .TextBox1.Left
    		Mycmd.Top = .TextBox1.Top + 100
    		Mycmd.Width = .TextBox1.Width
    		Mycmd.Height = .TextBox1.Height
    		Mycmd.Text = "New Control - "  Mycmd.Name
    
    		'Добавляем командную кнопку
    		Set Mycmd = .Controls.Add("Forms.CommandButton.1", "ProgramButton", True)
    		Mycmd.Left = .CommandButton1.Left
    		Mycmd.Top = .CommandButton1.Top + 100
    		Mycmd.Width = .CommandButton1.Width
    		Mycmd.Height = .CommandButton1.Height
    		Mycmd.Caption = "DesignButton"
    
    	End With
    	
    	'Удаляем командную кнопку, вызывающую эту процедуру.
    	'Эта кнопка может работать лишь раз, по крайней мере,
    	'пока в режиме проектирования не будут удалены добавленные элементы.
    	'Заметьте, кнопку можно только удалить, но нельзя сделать ее невидимой.
    	
    	ActiveDocument.InlineShapes(4).Delete
    End Sub

    Обратите внимание, к нашей форме, рассматриваемой как объект класса VBComponent, мы добираемся через свойство VBProject нашего документа. Имея форму, в последний момент вызываем Designer, который и возвращает форму периода проектирования. Теперь добавление элементов управления будет постоянным. Само добавление делается, как и ранее, никаких изменений в этом процессе здесь нет.

    Однако есть некоторые естественные ограничения, на которые следует обратить внимание. Процедура AddDesignControls, добавляющая элементы периода проектирования, должна вызываться при закрытой форме и только один раз. Поэтому в конце этой процедуры добавлен текст, удаляющий из тестового документа командную кнопку (объект класса InlineShape ), обработчик которой вызывал процедуру AddDesignControls.

    Вот как выглядит форма, открытая после окончания работы этой процедуры:

    (рис П-4) Форма с постоянно добавленными элементами

    Вопрос второй: "Как импортировать VBComponent, если компонент с таким именем уже присутствует в программе (соответственно выдается ошибка при попытке импорта)?"

    Ответ, кажется, очевидным - нужно переименовать один из компонентов, поскольку, действительно не могут существовать два компонента с одним именем. Какой же из компонентов переименовывать? Здесь тоже все достаточно ясно, - импортируемый компонент программно в момент его добавления не переименуешь, поскольку он скрыт в файле. Поэтому остается переименовать компонент в самом проекте. Остается понять, какой именно компонент вызвал коллизию и требует переименования. Часто подсказкой может служить имя файла с импортируемым компонентом, поскольку по умолчанию имя файла совпадает с именем компонента. В моем примере я переименовываю все компоненты текущего проекта. Это одно из возможных решений. Главное, демонстрируется возможность переименования компонент.

    Но обо всем по порядку. Несколько слов о самом примере. В наш документ, содержащий форму со стандартным именем UserForm1, программно импортируются еще две формы. Обе они были экспортированы из другого документа, сохранены в виде файлов, и поскольку имели стандартные имена, то и файлы (по два файла на форму с уточнениями " frm " и " frx ") получили имена " UserForm1 " и " UserForm2 ". Вот процедура, осуществляющая импорт:

    Public Sub ImportForm()
    	'Эта процедура импортирует формы, сохраненные в файле
    	Documents("Answer1.doc").VBProject.VBComponents.Import 
    	("e:\O2000\cd2000\tests\UserForm1.frm")
    	Documents("Answer1.doc").VBProject.VBComponents.Import 
    	("e:\O2000\cd2000\tests\UserForm2.frm")
    
    End Sub

    Очевидно, что при запуске этой процедуры возникнет конфликт имен (два компонента - две формы имеют одно и то же имя " UserForm1 ") и при выполнении первого же оператора возникнет ошибка. Если же предварительно запустить процедуру Rename, переименовывающую объекты класса VBComponent, то импорт пройдет гладко. Разумно, конечно, запускать процедуру Rename в обработчике исключительной ситуации, когда конфликт реально возник. Вот возможный вариант процедуры Rename:

    Public Sub Rename()
    	Dim Comp As Object
    	
    	For Each Comp In Documents("Answer1").VBProject.VBComponents
    		Debug.Print Comp.Name
    		Comp.Name = Comp.Name  "New"
    		Debug.Print Comp.Name
    	Next Comp
    	
    End Sub

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

    ThisDocument
    ThisDocumentNew
    Sample
    SampleNew
    UserForm1
    UserForm1New

    После переименования импорт происходит без проблем.

    На два вопроса Ивана Кряжева ответы даны.

    Если Вы хотите задать свои вопросы, напоминаю мой адрес: Vladimir.Billig@tversu.ru

    Страницы:

    Вместо заключения

    Итак, книга, посвященная, главным образом, языку VBA, закончена. Я начал работать над второй книгой, в которой, собираюсь подробно рассказать об объектах каждого из приложений Office 2000 - Word, Excel, Access, Outlook, сопровождая описание проектами, в которых участвуют объекты этих приложений. Особое внимание хочется уделить применениям в Интернет. Пока, это еще только планы. Пишу об этом по той причине, что надеюсь получить от Вас письма с предложениями, а, возможно, и готовыми проектами, небольшими, но интересными для многих, которые Вы хотели бы поместить в книгу.

    Теперь несколько слов о законченной книге. Знаю по себе, что книги редко отвечают на вопросы, требующие сиюминутного решения. Цель книг другая. При прочтении хорошей книги число интересных вопросов, которые можно задать, по крайней мере, самому себе должно только возрастать. И вся прелесть состоит в том, что на эти вопросы удается найти ответы. В поисках ответов мы часто обращаемся к друзьям, коллегам, а иногда, когда есть такая возможность, и к автору книги. Я получаю достаточно много писем с различными вопросами по поводу офисного программирования. Не всегда, но, по возможности, стараюсь на них отвечать. Как правило, ответы на поставленные вопросы требуют знания чуть большего числа деталей, в сравнении с тем, что, по понятным причинам, удалось изложить в рамках книги. В качестве примера, приведу два вопроса из одного из последних писем, которое я получил от Ивана Кряжева - читателя нашей предыдущей книги по Офисному программированию. Полагаю возможным и полезным поместить мой подробный ответ на вопросы этого письма.

    Вопрос первый: "Как сохранить в форме программно добавленные элементы управления так, чтобы они появлялись при повторном ее открытии?"

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

    Первым делом я спроектировал форму с двумя элементами управления, включив ее в состав тестового документа. На самом тестовом документе я разместил 4 кнопки, управляющие работой формы. Кнопки Show и Hide показывают и прячут форму. Кнопка "Add Controls to Form" программно добавляет элементы управления на период существования формы, то есть до тех пор, пока форма не будет закрыта. Наконец, кнопка "Add Design Controls" решает поставленную задачу, добавляя элементы управления, как теперь принято говорить, на постоянной основе. Вот как выглядит тестовый документ с командными кнопками:

    (рис П-1) Тестовый документ с командными кнопками

    А вот как выглядит сама форма при ее открытии в ответ на щелчок кнопки Show:

    (рис П-2) Спроектированная форма при ее открытии

    Вот на какие моменты при решении этой задачи следует обратить внимание:

  • Спроектированная мной форма не является модальной. Это не является принципиальным в поставленной задаче, тем не менее, может быть важным, поскольку позволяет произвести изменение формы вне самой формы. О модальности форм следовало бы поговорить в основном тексте книги, но я этого, к сожалению, не сделал. Поэтому поговорим об этом сейчас. Формы Office 2000 обладает новым важным булевым свойством Modal, значение которого можно установить в момент проектирования. Если это значение равно True, то окно формы является модальным и его нельзя покинуть до закрытия. Если значение - False, то можно покинуть форму, поработать с документом, нажимать, например, другие кнопки в документе, модифицируя, возможно, саму форму. Заметим, что в Office 97 создать не модальное окно можно было только, используя объекты Balloon, входящие в состав объекта Assistant. Теперь сделан важный шаг в правильном направлении, - центральный объект интерфейса - UserForm получил возможность работать в немодальном окне. Главное, что для программиста это не требует никаких усилий, достаточно изменить значение одного свойства. Замечу, что в С++, по крайней мере, в версии 4 работа с немодальными окнами требовала профессиональных знаний, в частности, умения посылать сообщения операционной системе. Но вернемся к нашей задаче.
  • Элементы управления достаточно просто программным путем вставляются в форму в период выполнения программы, и я об этом рассказывал в главах, посвященных проектированию интерфейса. У объекта Form есть коллекция Controls, у которой есть метод Add, позволяющий добавить любой из элементов управления. В нашем примере мы уже спроектировали форму с двумя элементами управления - командной кнопкой и окном редактирования. В обработчиках события Click командной кнопки самой формы и командной кнопки "Add Controls to Form", встроенной в тестовый документ, вызывается процедура AddControls, которая добавляет в форму еще пару таких же элементов управления. Заметьте, поскольку наша форма не является модальной, то нажимать командные кнопки можно как в самой форме, так и вне формы. Вот текст процедуры, добавляющей в форму элементы управления:
    Public Sub AddControls()
    	'Эта процедура добавляет командную кнопку и окно редактирования в форму
    	With UserForm1
    		
    		'Добавляем окно редактирования
    		Set Mycmd = .Controls.Add("Forms.TextBox.1", "ProgramBox", True)
    		Mycmd.Left = .TextBox1.Left
    		Mycmd.Top = .TextBox1.Top + 100
    		Mycmd.Width = .TextBox1.Width
    		Mycmd.Height = .TextBox1.Height
    		Mycmd.Text = "New Control - "  Mycmd.Name
    		
    		'Добавляем командную кнопку
    		Set Mycmd = .Controls.Add("Forms.CommandButton.1", "ProgramButton", True)
    		Mycmd.Left = .CommandButton1.Left
    		Mycmd.Top = .CommandButton1.Top + 100
    		Mycmd.Width = .CommandButton1.Width
    		Mycmd.Height = .CommandButton1.Height
    		Mycmd.Caption = "ProgramButton"
    				
    	End With
    	
    End Sub
  • Вот как выглядит форма, после того как была нажата кнопка "Add Controls to Form" и ее обработчик вызвал процедуру (рис П-3) Форма с программно добавленными элементами управления
  • Добавленные таким путем элементы управления живут в форме только до ее закрытия. Но, заметьте, если скрыть форму, вызвав метод Hide, то при повторном ее открытии элементы управления сохраняются.
  • Перейдем теперь непосредственно к ответу на первый поставленный вопрос. Для его решения потребуются средства, ранее не упоминавшиеся в книге. Нам понадобятся объекты класса VBComponent - компоненты VB-проекта (объекта класса VBProject ). Чтобы можно было с ними работать, необходимо включить дополнительную ссылку на библиотеку VBIDE (Visual Basic Extensibility). Ну, а теперь отвечаю конкретно на первый поставленный вопрос. Можно программно добавить элементы управления так, чтобы они сохранялись в форме точно также как сохраняются элементы, созданные в период проектирования. Для этого нужно работать с объектом, представляющим форму в режиме проектирования, используя свойство Designer объекта VBComponent. Вот как это делается:
  • Public Sub AddDesignControls()
    	'Эта процедура добавляет командную кнопку и окно редактирования в форму
    	Dim MyForm As UserForm
    	'Создаем форму этапа проектирования
    
    	Set MyForm = ActiveDocument.VBProject.VBComponents("UserForm1").Designer
    	With MyForm
    
    		'Добавляем окно редактирования
    		Set Mycmd = .Controls.Add("Forms.TextBox.1", "ProgramBox", True)
    		Mycmd.Left = .TextBox1.Left
    		Mycmd.Top = .TextBox1.Top + 100
    		Mycmd.Width = .TextBox1.Width
    		Mycmd.Height = .TextBox1.Height
    		Mycmd.Text = "New Control - "  Mycmd.Name
    
    		'Добавляем командную кнопку
    		Set Mycmd = .Controls.Add("Forms.CommandButton.1", "ProgramButton", True)
    		Mycmd.Left = .CommandButton1.Left
    		Mycmd.Top = .CommandButton1.Top + 100
    		Mycmd.Width = .CommandButton1.Width
    		Mycmd.Height = .CommandButton1.Height
    		Mycmd.Caption = "DesignButton"
    
    	End With
    	
    	'Удаляем командную кнопку, вызывающую эту процедуру.
    	'Эта кнопка может работать лишь раз, по крайней мере,
    	'пока в режиме проектирования не будут удалены добавленные элементы.
    	'Заметьте, кнопку можно только удалить, но нельзя сделать ее невидимой.
    	
    	ActiveDocument.InlineShapes(4).Delete
    End Sub

    Обратите внимание, к нашей форме, рассматриваемой как объект класса VBComponent, мы добираемся через свойство VBProject нашего документа. Имея форму, в последний момент вызываем Designer, который и возвращает форму периода проектирования. Теперь добавление элементов управления будет постоянным. Само добавление делается, как и ранее, никаких изменений в этом процессе здесь нет.

    Однако есть некоторые естественные ограничения, на которые следует обратить внимание. Процедура AddDesignControls, добавляющая элементы периода проектирования, должна вызываться при закрытой форме и только один раз. Поэтому в конце этой процедуры добавлен текст, удаляющий из тестового документа командную кнопку (объект класса InlineShape ), обработчик которой вызывал процедуру AddDesignControls.

    Вот как выглядит форма, открытая после окончания работы этой процедуры:

    (рис П-4) Форма с постоянно добавленными элементами

    Вопрос второй: "Как импортировать VBComponent, если компонент с таким именем уже присутствует в программе (соответственно выдается ошибка при попытке импорта)?"

    Ответ, кажется, очевидным - нужно переименовать один из компонентов, поскольку, действительно не могут существовать два компонента с одним именем. Какой же из компонентов переименовывать? Здесь тоже все достаточно ясно, - импортируемый компонент программно в момент его добавления не переименуешь, поскольку он скрыт в файле. Поэтому остается переименовать компонент в самом проекте. Остается понять, какой именно компонент вызвал коллизию и требует переименования. Часто подсказкой может служить имя файла с импортируемым компонентом, поскольку по умолчанию имя файла совпадает с именем компонента. В моем примере я переименовываю все компоненты текущего проекта. Это одно из возможных решений. Главное, демонстрируется возможность переименования компонент.

    Но обо всем по порядку. Несколько слов о самом примере. В наш документ, содержащий форму со стандартным именем UserForm1, программно импортируются еще две формы. Обе они были экспортированы из другого документа, сохранены в виде файлов, и поскольку имели стандартные имена, то и файлы (по два файла на форму с уточнениями " frm " и " frx ") получили имена " UserForm1 " и " UserForm2 ". Вот процедура, осуществляющая импорт:

    Public Sub ImportForm()
    	'Эта процедура импортирует формы, сохраненные в файле
    	Documents("Answer1.doc").VBProject.VBComponents.Import 
    	("e:\O2000\cd2000\tests\UserForm1.frm")
    	Documents("Answer1.doc").VBProject.VBComponents.Import 
    	("e:\O2000\cd2000\tests\UserForm2.frm")
    
    End Sub

    Очевидно, что при запуске этой процедуры возникнет конфликт имен (два компонента - две формы имеют одно и то же имя " UserForm1 ") и при выполнении первого же оператора возникнет ошибка. Если же предварительно запустить процедуру Rename, переименовывающую объекты класса VBComponent, то импорт пройдет гладко. Разумно, конечно, запускать процедуру Rename в обработчике исключительной ситуации, когда конфликт реально возник. Вот возможный вариант процедуры Rename:

    Public Sub Rename()
    	Dim Comp As Object
    	
    	For Each Comp In Documents("Answer1").VBProject.VBComponents
    		Debug.Print Comp.Name
    		Comp.Name = Comp.Name  "New"
    		Debug.Print Comp.Name
    	Next Comp
    	
    End Sub

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

    ThisDocument
    ThisDocumentNew
    Sample
    SampleNew
    UserForm1
    UserForm1New

    После переименования импорт происходит без проблем.

    На два вопроса Ивана Кряжева ответы даны.

    Если Вы хотите задать свои вопросы, напоминаю мой адрес: Vladimir.Billig@tversu.ru

    Вернуться к учебному плану