Четверг, 28.03.2024, 15:05
Приветствую Вас Гость | RSS

Лекции

Меню сайта
Форма входа
Категории раздела
ТАУ (Теория автоматического управления) [31]
лекции по ТАУ
Экология [151]
учебник
Бухгалтерский учет и налогообложение в строительстве [56]
Дементьев А.Ю. - Практическое пособие
Психология [104]
Пип
информатика [80]
с# Карли Ватсон
современные стулья [0]
новинки
Поиск

Главная » 2010 » Февраль » 11 » SDI- и MDI-приложения
00:33
SDI- и MDI-приложения
SDI- и MDI-приложения
Традиционно существует три разновидности приложений, которые могут созда-
ваться ДДЯ MS Word, а именно:
• Приложения, основанные на диалоговом окне. Такие приложения
предоставляются пользователям в виде единого диалогового окна,
с помощью которого может быть осуществлен доступ ко всем
функциональным возможностям.
• Однодокументные интерфейсы (Single Document Interface, SDI),
Такие приложения предоставляются пользователям в виде меню,
одной или нескольких линеек инструментов и одного окна,
в котором пользователь может выполнять определенные действия.
• Многодокументные интерфейсы
(Multi-Document Interface, MDI).
Такие приложения представляются
пользователям в. таком же виде,
что SDI-приложения, однако обладают
способностью одновременно поддерживать
несколько открытых окон.
Приложения, основанные на диалоговом окне,
обычно представляют собой небольшие одноцелевые
приложения, которые ориентированы либо для реше-
ния конкретной задачи, требующей ввода небольшого
количества данных, либо для работы с какими-то нео-
бычными типами данных. В качестве примера такого
приложения можно привести Calculator (калькулятор),
поставляемый вместе с MS Windows (см. рис. справа).
364 Глава 14
Однодокументные интерфейсы, как
правило, предназначаются для решения
какой-то одной конкретной задачи, при
этом они позволяют пользователю за-
гружать в приложение единственный
документ, с которым он и будет вести
работу. Эта задача предполагает выпол-
нение пользователем большого количе-
ства действий, и зачастую пользователю
могут потребоваться возможности, по-
зволяющие сохранять или загружать
плоды своего труда. Хорошим примером
SDI-приложений могут служить MS Paint
и WordPad (см. рис. справа), также по-
ставляемые совместно с MS Windows.
CIEffiw^u.iSfiJl^S. , - *«*& L JL-'Л-г,* .;,-.".'.* ' v
7:.•:! • $• t » 3 ' i -Щ- 5 -11 •
Vot НсГр. pjcii f 1
Однако такие приложения допускают открытие только одного документа в каж-
дый конкретный момент времени, поэтому если пользователю требуется открыть
второй документ, то ему будет необходимо открывать новый экземпляр SDI-прило-
жения, у которого будет отсутствовать связи с первым документом и, следователь-
но, конфигурация, созданная для первого экземпляра, не окажет никакого влияния
на конфигурацию второго. Например, вы в MS Paint выбрали красный цвет в ка-
честве цвета рисования, затем открываете второй экземпляр MS Paint, а здесь
в качестве цвета, используемого для рисования, выбирается цвет по умолчанию.
Он будет черный.
Многодокументные интерфейсы почти полностью аналогичны SDI-приложени-
ям за исключением того, что они обладают возможностью поддерживать более
одного открытого документа в различных окнах, которые могут быть открыты
одновременно. Одним из простых признаков MDI-приложения является наличие
пункта Window на правой стороне линейки меню перед пунктом Help. Примерами
MDI-приложений служат Adobe
Acrobat Reader (см. рис. слева) и
MS Word 97.
Четвертый тип приложений
представлен MS Office 2000. Этот
тип является смесью SDI- и MDI-
приложений: окна, предоставляе-
мые пользователю, имеют различ-
ное местоположение, и каждое
окно отображается в линейке за-
даний. Такое приложение пред-
ставляет собой несколько MDI-
приложений, поскольку основное
приложение не будет закрыто до
тех пор, пока не будут закрыты
все окна, а с помощью пункта
меню Windows можно выбирать,
какой именно из открытых.доку-
ментов будет просматриваться,
хотя при этом собственно пользо-
вательский интерфейс представ-
ляет собой SDI-интерфейс.
Дополнительные возможности Windows Form 365
В этой главе мы сосредоточимся на создании MDI- приложений и тех задачах,
которые при этом возникают. Дело в том, что любое SDI-приложение в основном
представляет собой подмножество MDI-приложения, поэтому, если вы умеете
создавать MDI-приложения, вы также сможете создавать и SDI-приложения.
В главе 15 мы создадим простое SDI-приложение, которое будет использоваться
для демонстрации Windows Common Dialogs (общие диалоговые окна Windows).
Создание MDI-приложений .
Для создания MDI, во-первых, необходимо, чтобы решаемая пользователем
задача требовала одновременно несколько открытых документов. Примером задач
такого рода является текстовый редактор или, как в приведенном выше примере,
программа просмотра документов. Во-вторых, необходимо предусмотреть панели
инструментов для наиболее часто выполняемых в приложении операций, таких как
изменение стиля шрифта, загрузка и сохранение документов. В-третьих, обязатель-
но следует предусмотреть пункт меню Window, который позволял бы пользователю
изменять положение открытых окон друг относительно друга (налагая их друг на
друга в виде черепицы или каскада) и предоставлял бы ему список всех открытых
окон. Еще одной особенностью MDI-приложений является то, что если имеется
некоторое открытое окно и в этом окне существует некоторое меню, то оно дол-
жно быть интегрировано в основное меню приложения.
Любое MDI-приложение состоит по крайней мере из двух разных окон. Первое
окно называется MDI-контейнером, а окно, которое может быть открыто в кон-
тейнере, называется дочерним MDI-окном. При упоминании первого окна будут
использоваться взаимозаменяемые термины "MDI-контейнер" и "основное окно",
а при упоминании второго — термины "дочернее MDI-окно" ИЛИ просто "дочернее
окно".
При создании MDI-приложения следует начинать с того же, с чего начинается
создание любого другого приложения,— с создания Windows Application в Visual
Studio. Чтобы превратить основное окно приложения из формы в MDI-контейнер,
достаточно просто присвоить свойству формы isMdiContainer значение true. При
этом произойдет изменение цвета фона, указывающее на то, там не следует раз-
мещать видимые управляющие элементы, хотя такая возможность по-прежнему
существует и может при определенных обстоятельствах оказаться полезной.
Для создания дочернего окна следует добавить в проект новую форму, выбрав
Windows Form из диалогового окна, которое открывается при выборе пункта меню
Project | Add New Item. Эта форма становится дочерним окном, когда его свойству
MdiParent присваивается ссылка на основное окно. Этому свойству нельзя присва-
ивать значение с помощью панели Properties, это необходимо выполнять програм-
мным путем.
До того, как появится возможность выводить MDI-приложение на экран в его
основном виде, нужно выполнить еще две вещи. Необходимо передать MDI-KOH-
тейнеру информацию о том, какие окна должны выводиться, а затем вывести их,
для чего следует просто создать новый экземпляр формы, которую вы собираетесь
выводить, а затем вызвать для нее метод show(). Конструктор формы, предназна-
ченной для вывода в качестве дочернего окна, должен привязаться к родительско-
му контейнеру. Это достигается за счет присваивания его свойства MdiParent
экземпляру MDI-контейнера.
Прежде чем переходить к выполнению более сложных задач, разберем неболь-
шой пример, в котором выполняются все эти шаги.
366 Глава Ы
Практикум: создание MDI-приложения
Свойство Значение
Name
IsMdiContainer
Text
WindowState
frmContainer
True
MDI Basic
Maximized
1. Создайте новое Windows Application
И назовите его MdiBasic.
2. Выберите форму и задайте свойства, сведенные
в таблицу справа.
3. Добавьте новую форму к решению, выбрав пункт
Windows Form из меню Project | Add New Item.
Назовите Эту форму frmChild.
Весь код, необходимый для того, чтобы вывести дочернюю форму, располагает-
ся в конструкторах форм. Сначала мы займемся конструктором дочернего окна:
public frmChild (MdiBasic. frmContainer parent)
{
InitializeComponent();
••••/•/ Присваивание контейнеру родителя данной формы
this.MdiParent = parent;
}
Чтобы привязать дочернюю форму к MDI-контейнеру, ее необходимо зарегист-
рировать в контейнере. Это достигается присваиванием свойству MdiParent данной
формы соответствующего значения, как показано в приведенном выше коде.
Заметьте, что используемый конструктор включает в себя параметр parent.
Поскольку в С# не предусмотрены конструкторы по умолчанию для класса,
в котором описывается его собственный конструктор, приведенный выше код
позволяет предотвратить создание экземпляра данной формы, не привязанного
к MDI-контейнеру.
Выполним вывод формы на экран в конструкторе MDI-контейнера:
public frmContainer()
{
InitializeComponent();
// Создание нового экземпляра дочерней формы
MdiBasic.frmChild child = new MdiBasic.frmChild(this);
// Вывод созданной формы .
child.Show();
MDI Basic

Мы создаем новый экземпляр дочерне-
го класса и передаем его конструктору по-
средством ключевого слова this, которое
представляет собой текущий экземпляр
класса MDI-контейнера. Затем вызываем
метод show о для вывода нового экземпля-
ра дочерней формы. Если нужно вывести
более одного дочернего окна, то потребу-
ется повторить две выделенные выше
в коде строки для каждого окна.
Запустите код, и вы увидите приблизи-
тельно то, что показано на рисунке слева.
Это не самый впечатляющий пользова-
тельский интерфейс, однако он вполне
Дополнительные возможности Windows Form 367
подходит в качестве первоосновы. Следующий пример представляет несложный
текстовый редактор, использующий меню и панели инструментов.
Практикум: создание текстового редактора с использованием MDI
Свойства
Name
IsMdiContainer
Text
WindowState
Значение
Давайте сначала создадим основной проект, а затем обсудим происходящее:
1. Создайте новый проект Windows Application
И назовите его SimpleTextEditor.
Присвойте свойствам формы значения,
сведенные в таблицу слева.
2. Переименуйте файл, в котором содержится форма
(именно файл, а не СВОЙСТВО) на frmContainer.cs
и внесите следующие изменения в метод Maino:
frmContainer
True
Simple Text Editor
Maximized
static void Main()
Application.Run(new frmContainer());
3. Вставьте существующий объект в проект (Project | Add Existing Item).
Найдите образец панели инструментов и вставьте форму
(ее можно назвать Formi.cs).
4. Назовите, вновь вставленную форму frmEditor. Измените имя файла,
в котором она находится, на frmEditor.cs. Присвойте свойству Text
значение Editor.
Эти шаги очень похожи на шаги, предпринимавшиеся при создании
приложения MDI Basic; отличие заключается только лишь
в использовании уже существующей формы вместо создания новой.
Однако самое интересное следует далее.
5. Перейдите к коду формы, в которой содержится редактор.
Измените имя пространства имен на SimpleTextEditor и замените
ВСе Экземпляры Forml на frmEditor.
6. Найдите метод Main о формы и удалите его.
7. Измените конструктор формы, в которой содержится редактор,
следующим образом:
public frmEditor (SimpleTextEditor. frmContainer parent)
{
InitializeComponent();
// Привязка к родителю
this.MdiParent = parent;
}
8. Измените конструктор формы frmContainer следующим образом:
public frmContainer()
{
InitializeComponent();
SimpleTextEditor.frmEditor newForm = new frmEditor(this);
newForm.Show();
368 Глава 14
*и\
При запуске данного приложения полу-
чается что-то похожее на изображенное на
рисунке слева.
Произошло нечто необъяснимое. Мы не
создавали никаких меню для контейнера,
и тем не менее меню Files находится справа
вверху. Почему так получилось? Ответ за-
ключается в том, что было создано меню
для формы, содержащей редактор, а так как
эта форма является в настоящий момент
дочерней формой контейнера, то пункты
основного меню оказались объединенными.
Поскольку в контейнере пока отсутствуют
свои собственные меню, то в этом меню
всего один пункт.
Меню, содержащиеся в дочерних окнах, являются специфическими для данного
окна. В отличие от них меню Files должно быть общим для всех окон — оно не
должно содержаться в дочернем окне. Причина этого условия станет очевидной,
если вы закроете окно редактора,— меню Files исчезнет! Чтобы изменить такое
поведение, следует предпринять несколько дополнительных шагов:
9. Удалите меню с именем MainMenuFiies из формы, содержащей
редактор, щелкнув на нем правой кнопкой мыши и выбрав Delete.
В появившемся окне с сообщением нажмите Yes, чтобы его закрыть.
10. Перейдите к коду и удалите следующие обработчики событий
для данного меню:
• menuItemFiles_Popup
• menuItemSave_Click
• menuItemNew_Click
• menuItemOpen__Click
• menuItemExit_Click
11. Удалите из конструктора строку, в которой происходит подписка
на событие menuItemFiles_Popup.
Более подходящим для формы, содержащей редактор,
будет меню Edit. Для его создания выполните следующие шаги:
12. Добавьте новое меню в форму, содержащую редактор.
Назовите ЭТОТ управляющий Элемент MainMenuEdit.
13. Добавьте в него семь пунктов и присвойте значения свойствам:
Имя Текст Порядок объединения
menu11emEdi t
menuItemUndo
menuItemRedo
menultemSeparatorEdit0
menuItemCut
menultemCopy
menuItemPaste
&Edit
&Undo .
&Redo
-
Cu&t
&Copy
&Paste
0
He задан
He задан
He задан
He задан
He задан
He задан
Дополнительные возможности Windows Form 369
Имя
14. Присвойте свойству Menu формы, содержащей редактор, значение
MainMenuEdit. Это меню будет объединено с меню основного окна.
Теперь можно приступить к созданию меню, которое будет
выводиться на экран независимо от того, какое именно окно выбрано
в настоящий момент. Для того чтобы добавить меню в контейнер,
необходимо выполнить следующие шаги:
15. Добавьте К форме frmContainer меню MainMenu.
Назовите его MainMenuContainer.
16. Добавьте в это меню указанные ниже пункты. Обратите внимание
на то, что пункт Menuitemwindow должен быть пунктом меню
самого верхнего уровня — то есть он должен располагаться
справа от меню Files:
Текст Свойство
MdiList
Самый верхний
уровень
Порядок
объединения
menuItemFiles
menuItemNew
menuItemOpen
menuItemClose
menuItemSepFilesO
menuItemSave
menultemSaveAl1
menuItemSepFilesl
menuItemExit
menuitemwindow
menultemTile
menuItemCascade
menul t emSepWindowO
menuItemWindowsOpen
&Files
&New
&Open
fcClose
-
&Save
Save &A11
-
E&xit
fcWindow
&Tile
&Cascade
-
Open Windows
False
False
False
False
False
False
False
False
False
False
False
False
False
True
Да
Нет
Нет
Нет
Нет
Нет
Нет
Нет
Нет
Да
Нет
Нет
Нет
Нет
О
Не задан
Не задан
Не задан
Не задан
Не задан
Не задан
Не задан
Не задан
1
Не задан
Не задан
Не задан
Не задан
Files WmdoW
Если запустить приложение, то
можно увидеть, что меню Edit поме-
щено между меню Files и Window.
Причина произошедшего заключает-
ся в том, что мы задали 1 в качестве
Значения СВОЙСТВа MergeOrder управ-
ЛЯЮЩегО элемента Menuitemwindow,
а в качестве же значения свойства
MergeOrder меню Edit — 0. ПоСКОЛЬ-
ку свойство мегдетуре имеет значе-
ние Add, то меню Edit добавляется
справа от всех пунктов меню самого
верхнего уровня, у которых значение
свойства MergeOrder меньше значе-
НИЯ СВОЙСТВа MergeOrder меню Edit
или равно ему (см. рис. слева).
370 Глава 14
Еще один момент, достойный вни-
мания, заключается в том, что пункт
Open меню Windows содержит под-
пункт с окошком выбора. Текст этого
подпункта совпадает с текстом формы,
содержащей редактор. В названном
меню будут перечислены все откры-
тые окна — для это не потребуется
написать ни единой строки кода. Та-
кое положение является результатом
присвоения свойству MdiList пункта
меню menuItemWindowsOpen значения
true (см. рис. справа).
Добавление обработчиков событий
Для написания кода сначала нужно создать обработчик меню для меню Edit,
щелкнув два раза мышью на всех'пунктах этого меню за исключением пункта са-
мого верхнего уровня и разделителя и введя следующий код:
private void menu It emUndo_C lick (object sender, System. Event Args e)
rtfText.Undo();
private void menu It emRedo_C lick (object sender, System. Event Args e)
rtf Text. Redo () ;
private void menu I temCut_C lick (object sender, System. Event Args e)
rtfText.Cut();
private void menuItemCopy_Click( object sender, System. Event Args e)
rtf Text. Copy () ;
private void menuItemPaste_Click( object sender, System. Event Args e)
rtfText.Paste ();
В RichTextbox есть методы, точно соответствующие всем пунктам меню, поэто-
му просто вызываем эти функции. Как и в примере с MenuExampie, рассматривав-
шемся ранее, необходимо проверить, должно ли данное меню быть отключенным.
Эта проверка осуществляется в обработчике событий Popup. Сначала мы вносим
В КОНСТрукТОр формы frmEditor!
public frmEditor(SimpleTextEditor.frmContainer parent)
{
InitializeComponent();
// Привязка к родителю
this.MdiParent = parent;
Дополнительные возможности Windows Form 371
f I Подписка на событие Popup меню Edit
this.menuItemEdit.Popup += new EventHandler(this.menuItemEdit_Popup);
}
Затем добавляем собственно обработчик событий:
private void menuItemEdit_Popup(object sender, System.EventArgs e)
: {
// Если выбранный текст отсутствует, то его нельзя вырезать.
this.menuItemCut.Enabled = rtfText.SelectedText.Length > 0 ? true : false;
// Если выбранный текст отсутствует, его нельзя копировать.
this.menuItemCopy.Enabled = rtfText.SelectedText.Length > 0 ? true : false;
// Метод CanPaste управляющего элемента RichTextbox сообщает о том,
// имеется ли какая-либо информация, предназначенная для вставки
this.menuItemPaste.Enabled =
rtfText.CanPaste(DataFormats.GetFormat(DataFormats.Rtf));
// Свойство CanUndo управляющего элемента RichTextbox сообщает о том,
//имеется ли возможность отмены какого-либо действия
this.menuItemUndo.Enabled = rtfText.CanUndo; :
// Свойство CanRedo управляющего элемента RichTextbox сообщает о том,
// имеется ли возможность выполнения какого-либо отмененного действия
this.menuItemRedo.Enabled = rtfText.CanRedo; ;:
Управляющий элемент RichTextbox оказывает исчерпывающую помощь. Мож-
но проверить все пункты меню, чтобы выяснить, должны они быть отключены или
нет, простым вызовом функции или просмотром значения свойства. Единственное,
что требует пояснений,— это canPasteO. Этот метод получает текст, который вы
собираетесь вставлять, и возвращает логическое значение, которое равно true
в том случае, если такая вставка оказывается возможной.
Теперь обратимся к меню в MDI-контейнере и начнем с пункта New меню Files.
Когда происходит щелчок мышью на этом пункте, требуется создание нового окна.
Подобная процедура выполняется в конструкторе. Но при этом возникает пробле-
ма. На данный момент все окна обладают одним и тем же заголовком — Editor, что
не позволяет различать их в списке MdiList. Для того чтобы исправить ситуацию,
добавим в конструктор формы Editor еще один параметр, в котором будем переда-
вать текст, предназначенный для вывода в качестве заголовка вновь создаваемого
окна:
public frmEditor(SimpleTextEditor.frmContainer parent, string caption)
{
InitializeComponent();
// Привязка к родителю
this.MdiParent = parent;
//Задание заголовка
this.Text = caption;
// Подписка на событие Popup меню Edit
this.menul temEdit. Popup += new EventHandler(this.menuItemEdit_Popup);
this.rtfText.SelectionChanged += new
EventHandler (this. rtfText__SelectionChanged) ;
}
Это изменение означает, что необходимо изменить вызов конструктора, который
создается в конструкторе контейнера:
372 Глава 14
public frmContainer()
InitializeComponent();
SimpleTextEditor.frmEditor newForm = new frmEditor(this, "Editor 1");
newForm.Show();
Теперь можно создать новые окна. Щелкните два раза мышью на пункте меню
New и добавьте следующий код:
private void menuItemNew__Click (object sender. System.EventArgs e)
string caption = "Editor • + nextFormNumber++; // Заголовок
// Создание нового окна
SimpleTextEditor.frmEditor newForm = new
SimpleTextEditor.frmEditor(this, caption);
newForm.Show(); // Вывод формы
Прежде всего мы создаем новый заголовок. Переменная nextFormNumber опи-
сывается в самом начале класса следующим образом:
private int nextFormNumber = 2;
Если бы мы решили использовать количество открытых окон в массиве форм
(Mdichiidren), то столкнулись бы с проблемой, если были открыты две формы,
а затем форма с номером 1 закрыта. А так мы просто прибавляем единицу к теку-
щему номеру каждый раз, когда открывается новая форма. Причина, по которой
номер следующей формы равен 2, а не 1, заключается в том, что для инициализа-
ции ПерВОГО ОКНа В КОНСТруКТОре ИСПОЛЬЗуеТСЯ Текст "Editor Iй .
После этого мы создаем новый экземпляр ре-
дактора и выводим его.
Если запустить код и открыть новое окно, то
можно будет заметить, что события в меню Edit
относятся к нужному окну; это большая удача, по-
скольку мы не предпринимали никаких действий,
для того, чтобы этого добиться. Поскольку для
каждого экземпляра формы описываются свои
обработчики событий, то сообщения о событиях
каждый раз будут отправляться в определенное
место. В списке Open Windows выведено два окна
(см. рис. слева).
Теперь нужно закрыть открытые окна. Форма
frmContainer обладает СВОЙСТВОМ ActiveMdiChild,
которое позволяет идентифицировать то дочернее окно, которое мы хотим за-
крыть. Щелкните два раза мышью на меню Close и добавьте следующий код:
private void menuItemClose_Click( object sender, System. Event Args e)
// Определение активного дочернего MDI-окна
SimpleTextEditor.frmEditor frm =
•(SimpleTextEditor.frmEditor)this.ActiveMdiChild;
if (frm != null) // Перед тем как использовать потолок,
// необходимо убедиться в том, что он доступен
frm,Close(); // Закрытие окна
Дополнительные возможности Windows Form 373
Сначала мы изменяем тип формы, содержащейся в свойстве ActiveMdichild
на класс simpieTextEditor. Затем, прежде чем выполнять какие-либо действия
с данным экземпляром, мы убеждаемся, что его значение не равно null, а потом
вызываем метод close о для данного окна.
Мы не будем разбирать реализацию других пунктов меню Files, поскольку пунк-
тами Open, Save и SaveAII используются стандартные файловые диалоговые окна
Open и Save, которые обсуждаются в следующей главе. Для пункта меню Exit ис-
пользуется тот же самый код, который встречался в предшествующих примерах.
Весь код можно загрузить с сайта www.wrox.com.
Перейдем к двум оставшимся меню в меню Window — Tile и Cascade. Сущест-
вует два возможных способа располагать вновь открываемые документы поверх
уже открытых — вертикально и горизонтально; в данном примере мы будем рас-
полагать окна горизонтально. Для последовательного расположения нескольких
окон с документами имеется только одна возможность. Щелкните два раза мышью
на меню Tile и на меню Cascade и введите следующий код:
private void menuItemTile_Click(object sender, System.EventArgs e)
this.LayoutMdi(MdiLayout.TileHorizonal);
private void menuItemCascade__Click( object sender, System. Event Args e)
this.LayoutMdi(MdiLayout.Cascade);
| ™
IB
Г& Wind»*
rlnl
dacimzct 2
fljH
A
A
i
Метод LayoutMdi основного окна позволя-
ет изменять порядок расположения всех до-
черних MDI-OKOH.
Если запустить созданный код, то рисунок
слева покажет, как выглядят три налагаю-
щихся друг на друга документа.
Создание
управляющих элементов
Вполне вероятна ситуация, при которой
управляющие элементы, поставляемые со-
вместно с Visual Studio.NET в готовом виде,
не будут удовлетворять вашим требованиям.
На то может существовать много причин: управляющие элементы имеют другой
внешний вид, эти элементы налагают какие-либо ограничения, вам требуется
включить в интерфейс управляющих элементов дополнительные возможности,
или, наконец, необходимого вам управляющего элемента просто не существует.
Признавая это, компания Microsoft предоставляет средства для создания элемен-
тов, которые удовлетворяли бы требованиям пользователя. В Visual Studio.NET
имеется тип проекта Windows Control Library (библиотека управляющих элементов
Windows), к помощи которого следует прибегнуть при необходимости создать свой
собственный управляющий элемент.
Возможно разработать два различных вида собственных элементов, которые
называются пользовательскими управляющими" элементами (или составными
управляющими элементами) и специализированными управляющими элементами
(custom controls):
374 Глава 14
• Пользовательские, или составные управляющие элементы:
Они строятся на основе функциональных возможностей уже существующих
управляющих элементов, в результате чего создается новый управляющий
элемент. Такие элементы обычно создаются либо для объединения
функциональных возможностей с пользовательским интерфейсом
управляющего элемента, либо для расширения возможностей интерфейса
управляющего элемента путем объединения нескольких различных
элементов в одну логическую единицу.
• Специализированные управляющие элементы: Эта разновидность
управляющих элементов создается, когда ни один из существующих
элементов не удовлетворяет вашим нуждам и управляющий элемент
создается с нуля. Он воспроизводит весь пользовательский интерфейс,
и при его создании не используются готовые управляющие элементы.
Обычно необходимость в таких элементах возникает в тех случаях,
когда пользовательский интерфейс создаваемого управляющего элемента
существенно отличается от имеющихся в наличии.
В настоящей главе мы сосредоточим внимание на создании пользовательских
управляющих элементов, поскольку вторая возможность — разработка и создание
управляющих элементов с нуля — выходит за рамки данной книги. В главе 16,
посвященной GDI-f, представляются средства, необходимые для самостоятельного
создания графических элементов. Познакомившись с GDI + , вы получите возмож-
ность без затруднений перейти к разработке специализированных управляющих
элементов.
Управляющие элементы ActiveX (например, использующиеся
в Visual Studio 6) находятся в специальном файле
с расширением осх. На самом деле, такой файл представляет
собой COM DLL. В .NET управляющие элементы существуют
в таком же виде, что и любой произвольный модуль,
поэтому расширение осх исчезло, а управляющие элементы
существуют в виде обычных DLL.
Пакет
Возможность А Возможность В
I
Компонент 1 Компонент 2 Возможность С
Компонент 3
Пользовательские управляющие эле-
менты наследуются от класса usercontroi.
Это базовый класс предоставляет вновь
создаваемому элементу все базовые воз-
можности, которыми должен обладать
управляющий элемент в .NET — на
долю программиста остается только со-
здание самого элемента. В качестве управ-
ляющего элемента может создаваться все,
что угодно, начиная от надписи с каким-
либо оригинальным дизайном и до зани-
мающего весь экран табличного управ-
ляющего элемента (см. рис. слева).
В отличие от пользовательских элементов, специально
создаваемые управляющие элементы обычно являются
производными от класса control, а не от класса usercontroi.
Дополнительные возможности Windows Form 375
При работе с управляющими элементами следует принимать ряд положений
как должное, и если создаваемый вами элемент не отвечает им, то высока вероят-
ность того, что такой элемент не будет использоваться другими пользователями.
Вот эти критерии:
• Поведение управляющего элемента в период разработки должно
как можно точнее соответствовать поведению этого управляющего
элемента в период выполнения. Это означает, что если элемент
СОСТОИТ ИЗ наДПИСИ И текСТОВОГО ОКНа И Называется LabeiTextbox,
то и надпись, и текстовое окно, и вводимый для надписи текст
должны быть видимыми в процессе разработки. Хотя в приведенном
выше примере это не представляет никаких трудностей,
то в более сложных случаях это может создавать определенные
проблемы, что потребует от вас находить приемлемый компромисс.
• Необходимо обеспечить логический доступ к свойствам управляющего
элемента из формы, используемой для разработки. Хорошим примером
здесь может служить элемент imageList, который представляет
диалоговое окно, позволяющее осуществлять поиск рисунков
для включения в элемент, а также выводящее в диалоговом режиме
список импортированных рисунков.
Ниже рассматривается процесс создания управляющих элементов на конкрет-
ном примере. Мы создадим элемент LabeiTextbox и продемонстрируем основы
создания проекта по разработке пользовательского управляющего элемента, его
свойств и событий и отладке управляющего элемента.
Практикум: пример создания управляющего элемента LabeiTextbox
Как следует из самого названия элемента, для создания нового элемента в нем
используются два уже существующих, что позволяет за один шаг максимально
просто решить задачу, которая довольно часто возникает при программировании
в Windows: добавить к форме надпись, далее добавить к той же самой форме тек-
стовое окно, а затем определить местоположение этого окна относительно надпи-
си. Посмотрим, чего может ожидать от такого элемента пользователь:
• Возможность размещать текстовое окно как справа от надписи,
так и под надписью. Если текстовое окно располагается справа
от надписи, то должна быть возможность задавать фиксированное
расстояние между левым краем управляющего элемента и текстовым
окном, для того чтобы расположить несколько текстовых окон
одно под другим.
• Обычные свойства и событий текстового окна и надписи, которые
доступны в обычном режиме.
Поставленная задача понятна — самое время запустить Visual Studio и создать
новый проект:
1. Создайте новый проект С# в Visual Studio. Выберите проект
с названием Windows Control Library. Назовите вновь создаваемый
проект LabeiTextbox (см. След. рис.).
376 Глава 14
Project Types Templates:
•• -_| Vis i Вт PrqecU
•_J Visual СЙ Projects
_J Visual С ^Projects
£ j Sekip and Deployment Pcojects :
+! „ J Olber Project
_J Visual Studio Solution*
I P
Windows
Application
Ш$
WASePb.N АEрT...
ШШ
Class Library
ASP.NET
Web Service
r—"3 II
|щй .
Windows
Control library
Web Control
Lhtwy
A project for dealing controls to use к» Windows applications
] Label Textbox
С Add to Solution (* Close Solution
Project will be created at C:4Ary\8e9CSharp'\Chapter14MebelTextbc«!
Broiv.se...
Cancel .Help
Программа разработки форм
представляет рабочую поверхность,
которая несколько отличается
используемой до сих пор.
Во-первых, ее площадь оказывается
существенно меньше, чем обычно,
а, во-вторых, она вообще не выглядит
как диалоговое окно. Это не должно
обескураживать вас никоим образом —
все работает в нормальном режиме.
Основное отличие заключается в том,
что там, где мы до сих пор размещали
управляющие элементы на форме,
UserContfoli.cs {Design} |
' П.: •. . . . . . . Р . . . . . . . . а
• О
птШ-
теперь будем создавать управляющий элемент, чтобы
затем поместить его на форму (см. рис. справа).
2. Щелкните мышью на рабочей поверхности,
и вы увидите свойства управляющего элемента.
Измените ИМЯ Элемента На ctlLabelTextBox.
3. Щелкните два раза мышью на надписи в окне
с инструментами и перенесите ее в форму. Измените
ее ИМЯ На lblTextbox. Присвойте СВОЙСТВУ Text
Значение Label. Присвойте СВОЙСТВУ AutoSize
значение true. Это приведет к тому, что размеры надписи
будут определяться в соответствии с размерами текста.
4. Щелкните два раза мышью на элементе Textbox в окне
с инструментами. Измените его свойство Name на txtLabelText
и очистите свойство Text.
На этапе разработки нам не известно, каким образом пользователь пожелает
расположить эти управляющие элементы. Поэтому нужно написать код, который
будет отвечать за расположение надписи и текстового окна. Этот же код будет
определять местоположение управляющих элементов и тогда, когда элемент
LabeiTextbox будет помещен в форму.
Структура управляющего элемента выглядит пока весьма разочаровывающе —
Textbox свободно перемещается по рабочей поверхности, а сама поверхность
слишком велика. На самом деле, это не имеет значения, поскольку, то, что вы
видите, это совсем не то, что вы получите в итоге! Код, который мы
сейчас добавим к управляющему элементу, изменит его внешний вид,
но только после добавления элемента к форме (см. рис. слева).
Сначала следует определить взаимное расположи ние управляющих
элементов друг относительно друга. Поскольку пользователь должен
самостоятельно принимать решение об этом расположении, мы доба-
вим элементу не одно, а два свойства. Одно из них будет называться
Position (положение) — оно предоставляет пользователю возмож-
ность выбора: Right (справа) и Below (внизу). Если пользователь
выбирает значение Right, то в дело вступает второе свойство. Оно
-3 ' . . . . .
Label
Дополнительные возможности Windows Form 377
называется TextBoxMargin (поля текстового окна) и представляет собой целое чис-
ло, соответствующее числу пикселей между левым краем управляющего элемента
и тем местом, в котором должно быть размещено текстовое окно. Если пользова-
тель задает значение, равное 0, то текстовое окно размещается так, чтобы его
правый край располагался вровень с правым краем управляющего элемента.
Добавление свойств
Для того чтобы пользователь мог выбирать между Right и Below, прежде всего
потребуется перечислимый тип с этими значениями. Вернитесь в проект по созда-
нию управляющего элемента, перейдите в редактор кода и введите следующий код:
public class ctlLabelTextbox : System.Windows.Forms.UserControl
ттжтш-шттжтт {
//Нумерация двух возможных позиций
• public enumPositionEnum
X • . • . ; . . ; . . . : . . • • { ; • : ' ' ' :Г
Right/
Below
} : ^ -. - 4::!|
Это всего лишь обычный перечислимый тип, с которым мы познакомились
в главе 5. Теперь о необычном — нам требуется, чтобы местоположение пред-
ставляло собой свойство, которое может задаваться пользователем как програм-
мным путем, так и в процессе разработки. Этого можно добиться, включив это
свойство в класс ctiLabeiTextBox. Сначала, однако, потребуется создать два поля,
в которых будут храниться значения, выбранные пользователем:
// Поле — элемент данного класса, в котором будет храниться
// информация о выборе, сделанном пользователем
private PositionEnum mPosition — PositionEnum.Right;
private int mTextboxMargin•= 0; \
public ctlLabelTextbox(••)
{ Щ I II
Затем МЫ добавляем СВОЙСТВО Position!
public PositionEnum Position
{
, •::•:• : • . • : • • • • • get : ' ' ,• • ' ' • • • • ••• . •••: : ': ' ^ •' ' ' ••' •' ..
{
return mPosition;
mPosition = value;
MoveControls();
Это свойство добавлено в класс точно так же, как и любое другое. Если возникнет
необходимость вернуть его* значение, мы вернем поле mPosition, а если потребует-
ся изменить СВОЙСТВО Position — ПрИСВОИМ ПОЛЮ mPosition соответствующее ЗНа-
чение и вызовем метод MoveControls (). К методу MoveControls () мы вернемся чуть
ниже, а пока достаточно просто знать, что этот метод располагает два управляю-
щего Элемента В Соответствии СО Значениями СВОЙСТВ mPosition И mTexBoxMargin.
378 Глава 14
Аналогично создается свойство TexBoxMargin за исключением того, что оно ра-
ботает с целыми числами:
public int TextboxMargin
{ • •'. '•:.;:'•' ••:
get
{ • . ; . , ; - . • • • ' • • ; ; • ' . : : ' i
return mTextboxMargin;
}
set
-;:-.v :";•••;•••• / :' • W-±^ .. ,.; ' ^ . ;' ;
:.'--: ::':^-Ц;:К:/• ; • \-^\-.<:\;\:l.-^}^: :-.vV
":. .•/;:.• ., '..: •' mTextboxMargin - value; '••:•.:'•'••' v .; . :.. .••'•;••'' .
MoveControls();
| | | Щ • . •• • • • Щ ) •• ••" •• ••• • • • • i • •• • Щ •••• • • Ш i - : i 'i : Ш Ш •••• • ' : ' " • •••: " " : -
}
Добавление обработчиков событий
Прежде чем перейти к тестированию этих двух свойств, необходимо добавить
два обработчика событий. Когда управляющий элемент помещается в форму, вы-
зывается событие Load. Это событие следует использовать для инициализации
управляющего элемента и всех ресурсов, которые могут им использоваться. Мы
обрабатываем это событие, для того чтобы перемещать управляющие элементы
и подгонять размер всего управляющего элемента под размер содержащихся в нем
элементов. Мы будем обрабатывать еще одно событие — sizechanged. Оно возни-
кает всегда, когда изменяются размеры управляющего элемента, и необходимо об-
рабатывать это событие, чтобы управляющий элемент изображался надлежащим
образом. Мы подписываемся на события в конструкторе управляющего элемента:
// Обработка события SizeChanged
this.SizeChanged += new System.EventHandler(this.OnSizeChanged);
// Обработка события Load
this.Load +- new EventHandler(this.OnLoad);
Затем добавляем обработчики событий:
private void OnLoad(object sender, EventArgs e)
{
lblTextBox.Text = this.Name; //Добавляет текст к надписи
//Задание высоты управляющего элемента
this.Height = txtLabelText.Height + lblTextBox.Height;
MoveControls();
} . ...:... l ШШШё.
private void OnSizeChanged(object sender, System.EventArgs e)
{
MoveControls();
} • % ШШШШШ.
Метод MoveControls о вызывается для изменения местоположения управляю-
щих элементов. Следует рассмотреть этот метод, после чего протестировать рабо-
ту управляющего элемента еще раз:
private void MoveControls()
{ . -,;: • Щ \ : щ Ш -:Ш
switch (mPosition)
{ • .. . ' •
. ' . . • • . • . - . : • . • . • •. . :' c a s e P o s i t i o n E n u m . B e l o w : . • . • • . : . • . : : . • • : • • . : • • . • • . , • • • • • • • . : • • • • . • • . • • • • .
// Располагаем Textbox таким образом, чтобы его верхний
// край находился непосредственно под надписью ; •
Дополнительные возможности Windows Form 379
this.txtLabelText.Top = this.lblTextBox.Bottom;
this.txtLabelText.Left = this.lblTextBox.Left;
: • ••,.. • ••// Изменяем ширину Textbox, делая ее равной ширине
// управляющего элемента
,.:• this. txtLabelText. Width = this. Width;
this.Height = txtLabelText.Height + lblTextBox.Height;
case PositionEnum.Right:
// Располагаем Textbox таким образом, чтобы его верхний
// край совпадал с верхним краем надписи
txtLabelText.Top = lblTextBox.Top;
// Если поля обладают нулевым значением, то Textbox
// располагается непосредственно рядом с надписью
if (mTextboxMargin == 0)
< ' ' ' ' : • ' ' ' '• • - • ••••• ' • ' ' • ' • :' " :
int width = this.Width-lblTextBox.Width-3;
txtLabelText.Left = lblTextBox.Right + 3 ;
txtLabelText.Width = width;
//Если поля обладают ненулевым значением/ то Textbox
//располагается в месте, определенном пользователем
txtLabelText.Left = mTextboxMargin;
txtLabelText.Width = this.Right-mTextboxMargin;
}
break;
} •: \ ,' -. 4 '
} II .. ., .,.. I . .: : I : •
Значение, хранящееся в mPosition, используется для определения того, следует
ли располагать текстовое окно снизу или справа от надписи. Если пользователь
выбирает вариант Below, то мы перемещаем текстовое окно таким образом, чтобы
его верхняя граница совпала с нижней границей надписи. После этого мы переме-
щаем левый край текстового окна на уровень левого края всего управляющего
элемента и устанавливаем его ширину, равной ширине управляющего элемента.
Если пользователь выбирает вариант Right, то в этом случае имеются две воз-
можности. Если TexBoxMargin равно 0, то мы начинаем с того, что определяем ши-
рину незанятой части управляющего элемента, предназначен
Категория: информатика | Просмотров: 3957 | Добавил: basic | Рейтинг: 0.0/0
Всего комментариев: 0
Имя *:
Email *:
Код *:
Календарь
«  Февраль 2010  »
ПнВтСрЧтПтСбВс
1234567
891011121314
15161718192021
22232425262728
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

krutoto.ucoz.ru
Бесплатный конструктор сайтов - uCoz