Visual J++ и WFC позволяют создавать элементы управления, которые можно задействовать в
Java-программах, Visual Basic, Web-страницах и любых других хост-приложениях,
поддерживающих стандартные ActiveX-элементы. WFC предоставляет классы, содержащие
большую часть функциональности для Ваших элементов управления, в том числе средства
проектирования и реализации свойств, обработки и вызова событий и т. д.
В Visual
J++ имеются также визуальные средства для создания составных элементов управления
(они формируются из нескольких визуальных элементов, например флажков и списков).
Эти средства помогают разметить элемент управления и генерируют соответствующий
код, который Вы редактируете для реализации функциональных возможностей своего
элемента управления.
Подробнее
о создании элементов управления см. разделы, перечисленные в таблице.
Чтобы узнать
о |
См. раздел |
||
технологии элементов управления в WFC и написании кода для этих элементов работе со средствами
визуальной разработки составных элементов управления |
«Написание
WFC-элементов»
«Создание
составных WFC-элементов» |
||
WFC предоставляет
каркас для разработки нестандартных элементов управления. Следующая схема иллюстрирует
иерархию базовых классов визуальных WFC-элементов.
Класс com.ms.wfc.ui.Control
— базовый для всех элементов управления и обеспечивает большую часть функциональности
Ваших элементов управления.
Примечание
Для составных элементов управления (они формируются из других элементов
управления с добавлением новой логики) чаще всего используется пакет WFC com.ms.wfc.ui.
Их можно разрабатывать визуальными средствами (см. раздел «Создание составных
WFC-элементов» далее в этой главе).
Создавая
элемент управления, Вы расширяете базовый класс Control, a затем добавляете
нужные Вам методы. (Составные элементы управления разрабатываются визуальными
средствами, при этом расширяется класс UserControl.) Вы можете переопределять
члены, унаследованные от класса Control. Информация о создании элементов управления
приводится в следующих разделах:
Создание
базового элемента управления
Здесь рассматриваются
все этапы создания элемента управления. Вы узнаете, как разработать базовый
элемент управления и реализовать функциональность его основных свойств.
Определение
элемента управления
Нестандартный
элемент управления должен создавать подкласс WFC-класса Control. Чтобы Ваш элемент
управления появился в Toolbox среды Visual J+ + , Вы должны также расширить
класс com.ms.wfc.ui.Control. Classlnfo. Класс Classlnfo содержит данные, используемые
для предоставления информации о классе на этапе разработки и просмотра его свойств
и событий. Если Вы не хотите, чтобы Ваш элемент появился в Toolbox, не расширяйте
класс com.ms.wfc.ui.Control.Classlnfo. Но Вам все равно придется создать подкласс
класса Control.Classlnfo, реализующий интерфейс com.ms.wfc.core.IClassInfo.
Этому внутреннему классу следует присвоить имя Classlnfo.
Примечание
WFC Component Builder создает заготовку элемента управления, включая
свойства и методы, для которых Вы пишете код по своему усмотрению. В данном
разделе подробно описывается процесс создания элемента управления, — это поможет
Вам сделать все вручную и разобраться, что WFC Component Builder делает за Вас.
Ниже показан
пример заготовки. Если ее скомпилировать, элемент управления будет зарегистрирован
на Вашем компьютере и появится в диалоговом окне Customize Toolbox:
// MyControl.Java import com.ms.wfc.ui. *;
import com.ms.wfc.core.*;
public class MyControl extends Control
{
public static
class Classlnfo extends Control.Classlnfo { }
}
Добавив экземпляр
этого элемента управления на форму, Вы увидите его свойства и события в окне
Properties и убедитесь, что он уже поддерживает свойства backColor, foreColor,
anchor, dock, события мыши и др. Класс com.ms.wfc.ui.Control определяет стандартную
реализацию этих общих свойств и событий.
Добавление
описания элемента управления
Для элементов
управления, предоставляемых как ActiveX-элементы, можно добавить описание. Тогда
хост-приложения (приложения, использующие такие элементы) смогут запрашивать
и отображать это описание. Чтобы создать описание, добавьте объект DescriptionAttribute
в класс Classlnfo. Следующий пример показывает, как это делается:
public static class Classlnfo extends Control.ClassInfo
{
public void getAttributes(IAttributes attribs)
{
super.getAttributes(attribs);
attribs.add(new DescriptionAttribute("This describes MyControl"));
}
Функциональное
обеспечение событий класса
Класс com.ms.wfc.ui.Control
предоставляет универсальный набор членов со стандартной функциональностью. Если
Вы создадите базовый элемент управления по только что показанной методике, в
окне Properties появится, например, его свойство text.
Добавляя
какую-то функциональность, Вы, как правило, переопределяете доступные члены
базового класса. Большинство событий в WFC предоставляется в своем базовом классе
через защищенный метод оп<имя_события>, позволяющий подклассам
переопределять события, не назначая обработчики событий. Нужную функциональность
данного события Вы определяете в замещающем коде. В общем случае к стандартной
функциональности обращаются вызовом события над-класса. Поместив такой вызов
в свой код, Вы определяете последовательность, по которой генерируется это событие.
Примечание
Существуют дополнительные методы получения событий в том случае,
когда подкласс для события, определенного в надклассе, не создается. Подробнее
см. раздел «Работа с событиями элемента управления» далее в этой
главе.
Следующий
пример демонстрирует, как переопределить защищенный метод onPaint, чтобы сообщить
элементу управления, какую информацию он должен отображать в период выполнения.
Здесь сначала вызывается событие надкласса для выполнения его метода paint.
Затем код элемента управления отображает значение свойства text, вызывая метод
drawString объекта Graphics:
// MyControl.Java import com.ms.wfc.ui.*;
import com.oris.wfc.core.
*;
public class MyControl extends Control
{
protected void onPaint(PaintEvent p)
{
super.onPaint(p);
Graphics g =
p.graphics;
g.drawString(getText(), 0, 0);
}
public static class Classlnfo extends Control.Classlnfo
{
} }
В этом примере
Вы отображаете текст, вызывая метод getText элемента управления для получения
хранящегося в нем текста. После компиляции и добавления новой версии класса
элемент управления отображает текст, присвоенный свойству text. Так как класс
com.ms.wfc.ui.Control уже предоставляет текстовое свойство, нет нужды создавать
новое. Параметром метода onPaint служит объект PaintEvent, содержащий данные,
специфические для события. Когда Вы создаете подкласс защищенного члена, объект,
посылающий событие, становится известным в неявной форме, поскольку он — не
что иное, как this (текущий экземпляр данного класса). Данные зависят
от конкретного события. Здесь PaintEvent выглядит примерно так:
public class PaintEvent extends Event
{
// объект Graphics, с помощью которого следует выполнять прорисовку
public final
Graphics graphics;
// Rectangle
- прямоугольник, внутри которого следует выполнять
// все операции
прорисовки
public final Rectangle clipRect;
}
Член graphics
класса PaintEvent ссылается на объект com.ms.wfc.ui.Graphics. Это WFC-оболочка
поверхности рисования — контекста устройства Win32. Объект Graphics предоставляет
методы для вывода строк, линий, точек, эллипсов и т. п. Изменение значений свойств
font, fore-Color и backColor отражается на экране корректно, так как объект
Graphics, передаваемый через событие PaintEvent, устанавливает шрифты и кисти,
учитывая значения соответствующих свойств элемента. Подробнее об операции прорисовки
см. раздел «Обновление экранного представления элемента управления»
далее в этой главе.
Работа
с описателями окон
Базовый класс
Control инкапсулирует Win32 HWND (описатель) в закрытом члене, который расширяет
класс com.ms.wfc.app.Window. Класс Window создает подкласс оконной процедуры
для HWND Вашего элемента управления.
Метод Control.
getHandle, вызываемый для получения описателя, либо создает описатель (если
его не было), либо возвращает уже существующий. При создании описателя класс
Control сначала вызывает getCreateParams, чтобы определить корректные стили
окна для создаваемого описателя. Если Вы хотите задать свои стили или расширенные
стили окна, переопределите метод getCreateParams. Как только описатель создан,
генерируется событие createHandle. А событие destroyHandle возникает
непосредственно перед уничтожением описателя. Таким образом, описатель действителен
при обоих событиях — createHandle и destroyHandle.
Вре,мя жизни
любого описателя определяется реализацией Вашего элемента. Но время жизни экземпляра
любого элемента не зависит от времени жизни описателя.
WFC позволяет
манипулировать элементами управления по-разному, и неясно, в какой момент может
понадобиться повторное создание этого элемента в Windows. Допустим, Вы работаете
с таким элементом, как управляющая линейка (ruler). Изменение шкалы линейки
скорее всего потребует повторного создания элемента управления, 'что сделает
HWND недействительным, как в следующем примере:
{
h = x.getHandle();
х. units = "points";
// это изменение свойства приводит к повторному
// созданию элемента, и h становится недействительным
}
Как правило,
безопаснее получать описатель непосредственно перед его использованием.
Вы можете
запросить повторное создание описателя (т. е. уничтожение старого и немедленное
создание нового), вызвав метод recreateHandle. После него метод getRecreatingHandle
возвращает true до тех пор, пока описатель не будет создан заново. Это позволяет
добавить в событие destroyHandle специальный код, получающий состояние-HWND
при повторном создании описателя. Пример — список, в котором Вы хотите сохранять
все элементы при уничтожении описателя, происходящем в момент его повторного
создания. Если Вам надо изменить стили окна, но создавать описатель заново Вы
не желаете, вызовите метод update-Styles. Он обращается к методу getCreateParams
и применяет к HWND новый и расширенный стиль.
Вызовы этих
функций ни к чему не приведут, если описатель еще не создан. Таким нюансом можно
воспользоваться в методах установки свойств, когда принудительного создания
нового описателя не требуется, но, если он уже создан, его следует изменить.
По негласным правилам не принято форсировать создание описателя при простом
изменении свойств. Преждевременное его создание может оказаться бесполезным,
так как нельзя исключить, что пользователь не изменит и другие свойства, которые
потребуют повторного создания описателя. Поэтому оптимальнее запрашивать создание
описателя только для визуализации элемента управления.
Потоки
и WFC-элементы
Java поддерживает
многопоточность через классы и API-интерфейсы, позволяющие управлять созданием
и выполнением потоков. Win32-окна используют модель разделенных потоков. Это
означает, что их можно создавать в любом потоке, но они не могут переключать
потоки, и все вызовы, относящиеся к данному окну, должны быть в его главном
потоке. WFC-элементы (производные от com.ms.wfc.ui.Control) требуют использования
именно этой модели, тогда как большинство WFC-компонентов построено на модели
свободных потоков. В классе Control есть две функции (метода) — invoke и invokeAsync,
выполняющие маршалинг любого делегата в поток главного окна элемента управления
и запускающие его. Если Вы попытаетесь вызвать для элемента управления функцию,
которая манипулирует низкоуровневым HWND Win32, ее маршалинг в нужный поток
будет реализован средствами Windows. При прямом обращении к invoke можно накапливать
вызовы HWND, сокращая тем самым количество операций межпотокового маршалинга,
крайне отрицательно влияющих на быстродействие программы.
Примечание
Цикл обработки Windows-сообщений находится в потоке, где был создан
HWND. Если HWND элемента управления повторно создается в новом потоке, последний
уже не получит новых сообщений (см. раздел «Использование Java-потоков
с WFC» главы 12 «Концепции программирования на базе WFC»).
Хотя большинство
WFC-компонентов поддерживает свободные потоки, многие вызовы методов не синхронизируются.
Синхронизация приводит к чрезмерно высоким издержкам, и ее следует применять
только там, где она действительно нужна.
Определение
свойств элемента управления
Большинство
элементов управления обладает набором свойств, позволяющих управлять поведением
и внешним видом элемента. В WFC-эле-ментах Вы создаете свойства как члены с
функциями get и set. Но WFC предоставляет также классы и интерфейсы, помогающие
интегрировать элемент управления в среду программирования.
Как создавать,
предоставлять и настраивать свойства собственных элементов управления, поясняется
в следующих разделах.
Создание
и предоставление свойства
Создание
свойства у элемента управления аналогично введению свойства в любой Java-класс.
Но в данном случае, чтобы свойство появилось в окне Properties, Вы расширяете
еще и класс Classlnfo.
Добавление
определения свойства
Значение
свойства хранится в закрытой переменной-члене Вашего класса. Чтобы пользователи
могли считывать его значение, Вы создаете открытый метод get<свойство>.
Если Вы намерены сделать свойство доступным не только для чтения, но и записи,
создайте также открытый
метод set <свойство >, принимающий в качестве параметра новое значение
свойства.
Примечание
WFC Component Builder сам создает заготовки свойств.
Простое свойство
myProp типа integer в элементе MyControl можно реализовать так:
public class MyControl extends Control
{
private int myProp =0; // 0 - значение по умолчанию
public getMyProp()
{ return myProp;
}
public setMyProp(int newValue)
{
myProp = newValue;
} }
Примечание
Первая буква имени, используемого в определении свойства, должна
быть, по возможности, строчной, кроме тех случаев, когда в имени первые две
буквы прописные. В именах функций первую букву после get или set следует делать
прописной. Например, методы для свойства text — getText и setText, а для свойства
MDIChild — getMDIChild и setMDIChild.
Обеспечение
доступа к свойству на этапе разработки
Чтобы свойство
появилось в окне Properties:
Примечание
Метод getProperties — один из нескольких, позволяющих предоставлять
свойства, события, объект-«расширитель» (extender) и атрибуты класса.
Об остальных методах см. раздел «Работа с событиями элемента управления»
далее в этой главе.
Пример, демонстрирующий
класс Classlnfo для свойства myProp:
public static class Classlnfo extends Control.Classlnfo {
public static
final Propertylnfo myProp =
new PropertyInfo(MyControl.class, "myProp", int.class);
public void getProperties(IProperties
props) {
// добавляем существующие свойства из родительского класса
super.getProperties(props);
props.add(myProp);
// добавляем свое свойство
} }
Подробнее
об использовании класса Propertylnfo см. раздел «Атрибуты нестандартных
свойств» далее в этой главе.
Пример:
свойство Alignment
Здесь приведен
полный пример добавления свойства alignment для класса SuperLabel — текст выравнивается
в метке по правому или левому краю либо по центру. Из этого примера Вы узнаете,
как:
Код примера
см. в разделе «Полный листинг примера» далее в этой главе.
Базовый
код свойства Alignment
Как выглядит
этот код, показано ниже. В методе setAlignment, вызывается функция invalidate,
которая неявно заставляет перерисовать элемент управления с учетом нового значения
свойства.
private int
align = AlignStyle.LEFT;
public int getAlignment()
{
return align;
}
public void setAlignment(int value)
{
align = value;
invalidate();
// перерисовка элемента управления с учетом
// нового значения свойства
}
Создание
перечислимых значений свойства
Поскольку
для данного свойства допустимы лишь три значения, Вы можете использовать механизм
перечисления. Хотя в Java нет встроенных средств поддержки перечислимых типов,
WFC предоставляет для этих целей класс com.ms.wfc.core.Enum. Любой класс, производный
от com.ms.wfc.core.Enum, рассматривается как объект Enum. Такие объекты соответствуют
стандарту, по которому могут содержать только переменные типа public static
final integer, представляющие допустимые значения. У них может быть метод, проверяющий
допустимость значений и возвращающий true, если переданное значение определено
в данном объекте Enum.
Окно Properties
распознает объекты-перечислители. Поэтому, создавая подкласс класса Enum, Вы
автоматически создаете доступный па этапе разработки список возможных значений
свойства в окне Properties.
Следующий
пример демонстрирует класс, определяющий перечислитель AlignStyle:
// AlignStyle.Java
import com.ms.wfc.core.*;
public class
AlignStyle extends Enum {
public static
final int LEFT = 1;
public static
final int RIGHT = 2;
public static
final int CENTER = 3;
// необязательный метод проверки передаваемого значения
public static
boolean valid(int value) {
return (LEFT
<= value && value <= CENTER); } }
Примечание
Сделав метод valid статическим, Вы позволите вызывать его без предварительного
создания экземпляра соответствующего класса.
Проверка
допустимости значения свойства
Чтобы проверять
допустимость значения свойства alignment, добавьте в метод setAlignment вызов
метода valid перечислителя AlignStyle. Тогда, если будет допущена ошибка, Вы
инициируете исключение WFCInvalidEnumException. Модифицируем метод setAlignment,
добавив такую проверку:
public void setAlignment(int value)
{
if (lAlignStyle.valid(value))
throw new WFCInvalidEnumException("value", value, AlignStyle.class);
align = value;
invalidate(); // перерисовка элемента управления с учетом
// нового значения
свойства }
Применение
Об использовании invalidate для перерисовки элемента управления см.
раздел «Обновление экранного представления элемента управления»
далее в этой главе.
Для исключения
WFCInvalidEnumException предусмотрено специальное сообщение. Оно переведено
на несколько языков, и, запуская элемент управления в разных языковых версиях
системы, Вы автоматически выводите понятное пользователю сообщение об ошибке.
Использование
значения свойства при прорисовке
Свойство
alignment влияет на формат текста в элементе управления. Поэтому код, отвечающий
за прорисовку элемента управления, должен получать значение этого свойства и
соответствующим образом его использовать.
Вы управляете прорисовкой, переопределяя метод onPaint. Ниже показано, как считать значение свойства alignment и использовать его в методе drawstring для выравнивания текста. Константы LEFT, RIGHT и др. определены в кла,ссе TextFormat из пакета com.ms.wfc.ui. protected
void onPaint(PaintEvent p)
{
Graphics g = p.graphics; int style = 0; switch (align)
{
case AlignStyle.LEFT:
style = TextFormat.LEFT; break;
case AlignStyle.RIGHT:
style = TextFormat.RIGHT;
break;
case AlignStyle.CENTER:
style = TextFormat.HORIZONTALCENTER;
break;
}
g.drawString(getText(), getClientRect(), style);
}
Обеспечение
доступа к свойству на этапе разработки
Для этого
Вы должны создать подкласс класса Classlnfo, чтобы сначала определить свойство
(созданием экземпляра класса Propertylnfo). Затем Вы предоставляете свойство,
вызвав getProperties:
public static
class Classlnfo extends Control.Classlnfo {
public static
final Propertylnfo alignment = new Propertylnfo( MyControl.class, "alignment",
AlignStyle.class);
public void
getProperties(IProperties props) {
super.getProperties(props);
props.add(alignment);
} }
Примечание
Третий параметр, передаваемый конструктору Propertylnfo (здесь —
AlignStyle.class), обычно указывает тип данных свойства. Но поскольку это свойство
принимает перечислимые значения, Вы передаете в нем свой класс, расширяющий
Enum.
Так как AlignStyle
является производным от Enum, окно Properties использует редактор, связанный
с классом Enum. В итоге Вы получаете список, позволяющий выбрать значение свойства.
Примечание
Чтобы создать свойство, недоступное на этапе разработки (используемое,
например, лишь в период выполнения), установите через объект Propertylnfo атрибут
BrowsableAttribute как NO. Подробнее см. раздел «Атрибуты нестандартных
свойств» далее в этой главе.
Полный
листинг примера
Следующий
листинг демонстрирует код законченного элемента управления «метка»,
свойство alignment которого включает все, о чем недавно говорилось.
// SuperLabel.Java
import com.ms.wfc.ui,*;
import com.ms.wfc.core.*;
public class
SuperLabel extends Control { private int align = AlignStyle.LEFT; public int
getAlignment() {
return align;
}
public void
setAlignment(int value) { if (!AllgnStyle.valid(value))
throw new WFCInvalidEnumException("value",
value, AlignStyle.class); align = value; invalidate(); // перерисовка элемента
управления с учетом
// нового значения
свойства }
protected void
onPaint(PaintEvent p) { Graphics g = p.graphics; int style = 0; switch (align)
{
case AlignStyle.LEFT:
style = TextFormat.LEFT;
break; case AlignStyle.RIGHT;
style = TextFormat.RIGHT;
break;
case AlignStyle.CENTER:
style = TextFormat.HORIZONTALCENTER; break;
}
g.drawString(getText(), getClientRect(), style);
}
public static class Classlnfo extends Control.Classlnfo
{
public static final Propertylnfo alignment = new Propertylnfo(
SuperLabel.class,
"alignment", AlignStyle.class);
public void getProperties(IProperties props)
{
super.getProperties(props);
props, add('alignment);
}
} }
Атрибуты
нестандартных свойств
Создавая
свойство (а потом — объект Propertylnfo, чтобы установить его
атрибуты), Вы можете настроить параметры свойства.
Ниже показан
базовый синтаксис, используемый при создании объекта Propertylnfo:
new PropertyInfo(Class
владелец, String имя, Class тип_данных)
Например,
объект Propertylnfo для свойства myProp элемента управления MyControl выглядит
так:
public static
final Propertylnfo myProp = new PropertyInfo(MyControl.class, "myProp",
int.class);
Вы можете
настроить свойство, предусмотрев любое количество дополнительных атрибутов-членов
объекта Propertylnfo. Атрибут-член определяется как класс, производный от com.ms.wfc.core
.MemberAttribute. В классе Propertylnfo уже имеется какое-то количество атрибутов,
но Вы можете создать и добавить свои, производные от MemberAttribute. Предопределенные
атрибуты в классе Propertylnfo перечислены в следующей таблице.
Атрибут |
Описание |
||
BrowsableAttribute | Задает, появится ли свойство в окне Properties. По умолчанию имеет значение YES. Используя этот атрибут в сочетании с PersistableAttribute (см. ниже), можно определять свойства, значения которых сохраняются в элементе управления, но не модифицируются в окне Properties. | ||
Category Attribute |
Указывает категорию
в окне Properties, в которую следует включить данное свойство, например
Appearance, Behavior и т. д. По умолчанию предлагается Misc. Вы можете
либо поместить свойство в существующую категорию (одним из статических
методовCategoryAttribute), либо создать новую, сконструировав новый
CategoryAttribute и передав ему имя, присваиваемое этой категории. |
||
DataBindableAttribute |
Указывает, можно
ли связывать это свойство с данными. Значение по умолчанию — .NO.
А если значение равно .YES, компонент Data-Binder перечисляет это
свойство в своем списке. |
||
DefaultValueAttribute |
Исходное значение
для простых свойств. Используется при сбросе текущего значения свойства
в окне Properties. Кроме того, Forms Designer сравнивает исходное
значение с текущим. Если они совпадают, Forms Designer не генерирует
код для присвоения значения свойству, что сокращает общий объем кода
для формы. Примечание Вы можете создать исходное значение и по-другому
— создав метод reset<свойство>. Об этом см. следующий
раздел. Наиболее часто используемые значения для различных типов данных
предопределены как статические (например, TRUE и FALSE для данных
типа Boolean). |
||
DescriptionAttribute |
Определяет текст,
отображаемый в нижней части окна Properties, когда пользователь выбирает
данное свойство. Значение по умолчанию — пустая строка. |
||
LocalizableAttribute |
Определяет,
будет ли значение свойства сохраняться в файле ресурсов, если пользователь
захочет локализовать форму. Значение по умолчанию — NO. При значении
YES, пользователь сможет локализовать файл ресурсов, не изменяя код
программы. |
||
ValueEditor Attribute | Задает нестандартный редактор, используемый в окне Properties для этого свойства. Если такой редактор не задан, окно Properties работает с редактором, сопоставленным с типом данных свойства. Подробнее см. раздел «Создание собственного редактора значений свойств» далее в этой главе. , | ||
PersistableAttribute |
Определяет,
будет ли значение свойства сохраняться при сохранении его контейнера
на этапе разработки. Значение по умолчанию — YES. Этот атрибут особенно
полезен, если Вы не хотите сохранять значение свойства из-за того,
что оно, например, зависит от состояния или просто вычисляется. Обычно
свойства, у которых этот атрибут равен NO, не появляются в окне Properties
(для этого атрибут Browsable Attribute должен быть тоже NO). |
||
Чтобы задать
атрибут, Вы создаете его экземпляр или берете один из предопределенных (например,
BrowsableAttribute.NO), а потом добавляете его в определение Propertylnfo по
следующему синтаксису:
new PropertyInfo(Class владелец, String имя,
Class тип_данных,
[MemberAttribute, [MemberAttribute, ...]])
Вот как определить
описание и исходное значение для свойства myProp, используя предопределенные
атрибуты:
public static
final Propertylnfo myProp =
new PropertyInfo(MyControl.class,
"myProp", int.class,
new DescriptionAttribute("Test
property"),
new DefaultValueAttribute(DefaultValueAttribute.ONE);
Вы можете
добавить до шести атрибутов, просто передав их в качестве параметров конструктору
Propertylnfo. Если Вам надо задать больше пяти атрибутов, используйте синтаксис
конструктора Propertylnfo, который позволяет принимать массив атрибутов. Подробнее
см. раздел «Propertylnfo» в Microsoft Visual J+ + 6.0 WFC Library
Reference, Part 1 (пакет com.ms.wfc.core).
Динамически
определяемые исходные значения и управление сохранением значений свойства
Чтобы установить
исходное (по умолчанию) значение для свойства, Вы используете атрибут DefaultValueAttribute
при создании нового объекта Propertylnfo, как уже было описано в предыдущем
разделе. Иногда бывает нужно задавать исходное значение динамически, вычисляя
его в период выполнения. Аналогичным образом может понадобиться сохранение значения
свойства между сеансами работы в зависимости от определенных условий.
Управление
сохранением значений свойства
По умолчанию,
когда исходное значение свойства элемента управления изменяется на этапе разработки
(например, в окне Properties), оно сохраняется при каждом сохранении самого
элемента управления. Так, если экземпляр Вашего элемента управления находится
на форме Visual J++ и Вы модифицировали значение какого-то свойства в окне Properties,
оно будет сохранено в момент сохранения формы. Но текущее значение, совпадающее
с исходным, не сохраняется.
Иногда по
результатам проверки некоего условия в период выполнения нужно решать, сохранять
ли значение свойства. Для этого переопределите метод shouldPersist<свойство>
класса Control. После проверки условия он должен вернуть либо true, либо false,
сообщая тем самым, требуется ли сохранять пользовательское значение свойства.
Чтобы запретить его сохранение, возвращайте false. Метод shouldPersist<свойстве»
можно переопределить и для свойств, наследуемых от надкласса, как в следующем
примере, где значение свойства font сохраняется в зависимости
от того, было ли оно изменено:
public boolean shouldPersistFont()
{
return font != null;
}
Примечание
Свойства, наследующие свои значения от предка (к ним относятся font
и backColor), при показанном выше сравнении возвращают null, уведомляя о том,
что они содержат исходные значения. Однако, явно считывая значения таких свойств
(например, методом getFont), Вы получаете реальные данные (допустим, информацию
о шрифте).
Динамическое
вычисление исходных значений
Чтобы установить
исходное значение для свойства в период выполнения, создайте для него метод
reset < свойство >. Ниже показано, как это делается на примере
свойства lastUpdate (его исходное значение определяется по текущей дате):
private Date
dtLastUpdate;
public void
setLastUpdate(Date d) {
dtLastUpdate
= d;
invalidate();
// перерисовка элемента управления с учетом
// нового значения
свойства }
public void
resetLastUpdate() {
return new Date();
}
Кроме того, можно переопределить методы reset < свойство >, наследуемые от класса Control. Следующий пример демонстрирует переопределение метода resetFont для нестандартного элемента управления:
public void
resetFont() {
Font f = new
Font("Arial", 8.Of, FontSize.POINTS, FontWeight.BOLD, false, false,
false);
setFont(f);
}
Создание
собственного редактора значений свойств
Окно Properties
в Visual J + + 6.0 позволяет модифицировать значения свойств, предоставляемых
Вашим элементом управления. Это окно на самом деле является всего лишь хостом
редакторов (стандартных и нестандартных). Вы можете назначить свой редактор
или воспользоваться стандартными.
Механизм
таков: окно Properties проверяет, назначен ли свойству нестандартный редактор.
Нет — ищет в классе, который определяет тип свойства, внутренний класс с именем
Editor. Если такой класс не найден, просматривает иерархию типов в поисках надкласса,
имеющего редактор. Поскольку существует класс ObjectEditor, сопоставленный с
Java.lang.Object, какой-нибудь редактор будет найден в любом случае. Окно Properties
предоставляет стандартные редакторы для простых типов, например, редактор строк,
редактор целых чисел и т. д. Собственный редактор может быть полезен по многим
причинам. Обычно они выполняют следующие задачи:
Подробнее
на эту тему см. следующий раздел.
Определение
собственного редактора значений
Чтобы создать
такой редактор, реализуйте интерфейс com.ms.wfc.core. IValueEditor. Для этого
лучше расширить класс com.ms.wfc.core.ValueEditor — Вы получите стандартную
реализацию всех методов. Для создания значения, отображаемого (в виде строки)
в окне Properties, переопределите метод getTextFromValue. Чтобы изменение значения
отражалось на элементе управления и показывалось в окне Properties, переопределите
метод getValueFromText.
Следующий
пример демонстрирует простой нестандартный редактор, который отображает в окне
Properties значение в процентах, но перед сохранением значения отсекает знак
процента. Для преобразований используется класс Value из пакета com.ms.wfc.util:
import com.ms.wfc.core.
*; import com.ms.wfc.util. *;
public class
PercentEditor extends ValueEditor {
// создаем строку в виде nn%" для отображения в окне Properties
public String
getTextFromValue(Object value) {
return Value.toString(Value.toInt(value))
+ "X"; }
// Преобразование
строки формата "nn)(" в целое значение.
// Этот метод
обязателен.
public Object getValueFromText(String text)
{
String s = Utils.trimBlanks(text. replace('%' , ' '));
return Value.toObject(Value.to!nt(s));
} }
Методы класса
com.ms.wfc.core.ValueEditor, обеспечирающие обмен информацией с редактором свойств,
перечислены в таблице.
Метод |
Описание |
||
editValue
getConstantName getStyle getTextFromValue getValues
getValueFromText paintValue |
Предоставляет пользовательский интерфейс для модификации значения свойства. Получает полностью квалифицированное имя константы, представляющей указанное значение. Возвращает битовое поле флагов стиля. Преобразует значение в строку. Возвращает массив значений; обычно окно Properties отображает их в списке, вызывая в цикле метод getTextFromValue. Возвращает значение из строки. Этот метод реализуете Вы. Отображает значение
в заданном прямоугольнике. |
||
Два дополнительных
метода— getSubProperties и getValueFromSubPro-pertyValues — позволяют работать
со свойствами объектов, например объекта Font, которые обычно передаются как
часть конструктора. Метод getStyle устанавливает и считывает флаги, используемые
для настройки редактора значений. Допустимы следующие стили.
Следующий
пример демонстрирует, как вывести модальное диалоговое окно, используемое в
качестве нестандартного редактора. Интерфейс IValueAccess предоставляет абстрактный
способ установки и получения значения, модифицируемого в данный момент. Это
удобно, если допускается выбор сразу нескольких элементов. Когда Вы вызываете
IValue-Access-методы установки и получения значений, окно Properties может корректно
установить значения нескольких элементов. Чтобы вывести кнопку с многоточием,
открывающую диалоговое окно, здесь переопределяется метод getStyle — он устанавливает
флаг STYLE_EDITVALUE в битовом поле, возвращаемом методом getStyle надкласса:
import com.ms.wfc.core. *; import com.ms.wfc.util. *;
public class PercentEditor extends ValueEditor {
public void editValue(IValueAccess
valueAccess) {
PercentDialog
dialog = new PercentDialog();
int value =
Value.toInt(valueAccess.getValue());
dialog.setPercentValue(value);
int result =
dialog. showDialog();
if (result ==
DialogResult.OK) {
valueAccess.setValue(dialog.getText());
} }
public int getStyle()
{
// с помощью оператора OR устанавливаем флаг в битовом поле,
// что обеспечивает отобрахение кнопки с многоточием
return
super.getStyle()
| STYLE_EDITVALUE;
} }
Ниже демонстрируется
вывод выпадающего диалогового окна. Элемент управления вызывает lEditorHost.
dropDownDone по завершении модификации свойства. Кроме того, он отвечает за
вызов IValueAccess-метода setValue.
import com.ms.wfc.core.
*; import com.ms.wfc.util. *;
public class PercentEditor extends ValueEditor {
public void editValue(IValueAccess valueAccess) {
PercentControl control = new PercentContro1();
int value = Value.toInt(valueAccess.getValue());
dialog. setPercentValue(value);
lEditorHost host =(lEditorHost)valueAccess.getService(IEditorHost.class);
control.setValueAccess(valueAccess);
host.dropDownControl(control);
}
public int getStyle()
{
return super.getStyle()
I STYLE.EDITVALUE | STYLE_DROPDOWNARROW; } }
Работа
с событиями
элемента управления
Так как Ваш
элемент управления является, подклассом класса Control, он автоматически поддерживает
стандартный набор событий Windows. Вы захватываете и обрабатываете эти события
в своем элементе управления и, если надо, передаете их хост-приложению. Кроме
того, Вы можете определять собственные события, уникальные для Вашего элемента
управления, и при необходимости генерировать их.
Захват
ввода в элементе управления
Ваш элемент
управления может захватывать и обрабатывать стандартные события до того, как
они посылаются хост-приложению. Два самых распространенных вида событий, вызываемых
действиями пользователя, — операции с мышью и клавиатурой.
Захват
событий, связанных с мышью
WFC-класс
Control предусматривает большую часть событий мыши, которые могут Вам понадобиться.
В их число входят события mouse-Move, mouseUp, mouseDown. mouseWheel, mouseEnter
и mouseLeave. Кроме того, Вы можете получать события click и doubleClick. Чтобы
получать в своем элементе управления события мыши, переопределите нужное Вам
событие. Чтобы передать его в хост-приложение, вызовите соответствующий метод
надкласса.
Следующий
пример показывает, как переопределить событие mouse-Move. Обработчик неявно
отображает координаты мыши, которые доступны в свойствах х и у переданного ему
объекта MouseEvent.
protected void onMouseMove(MouseEvent e) {
String sMsg
= "" + e.x + ", " + e.y; 'this.setText(sMsg);
invalidate();
// перерисовка элемента управления
super.onMouseMove(e);
// передача события хосту
}
Захват
событий, связанных с клавиатурой
Стандартные
события клавиатуры — keyUp, keyDown и keyPress. Они генерируются как для обычных,
так и для системных клавиш (например, F1-F12).
Весь ввод
с клавиатуры в WFC осуществляется с использованием набора символов Unicode.
При работе в операционных системах, не поддерживающих Unicode (например, в Windows
95), WFC автоматически выполняет требуемые преобразования.
Ниже показано, как захватывать события клавиатуры в элементе управления. Переопределенный метод onKeyUp — прежде чем передать событие хост-приложению — отфильтровывает символы, относящиеся к цифрам, protected
void
onKeyllp(KeyEvent e) {
if(e.keydata
< Key.DO && e.keyData > Key.D9) { super. onKeyllp(e);
invalidate();
// перерисовка элемента управления
} }
Создание
собственного события
WFC-элементы
поддерживают стандартный набор событий, наследуемых от класса Control, — например,
click, mouseDown, keyPress и т. д. Ваш элемент управления автоматически предоставляет
эти события, и они доступны хост-приложению без всяких усилий с Вашей стороны
— если только Вы не переопределите их для расширения функциональности.
Примечание
О создании обработчиков событий, принимающих события в хост-приложении,
см. раздел «Обработка событий в WFC» главы 12 «Концепции программирования
на базе WFC». Подробнее о захвате стандартного события и его обработке
в элементе управления см. предыдущие разделы.
Однако в
некоторых случаях может понадобиться создание собственных событий, уникальных
для Вашего элемента управления. Например, Вы хотите, чтобы элемент посредством
события уведомлял хост-приложение об изменении своего состояния (допустим, по
завершении инициализации) или сообщал об ошибке.
Примечание
Для уведомления об изменении значения свойства принято создавать
методы on<свойство>Changing и оп<свойство>Changed no методике, описанной
ниже. Подробнее см. раздел «Уведомление об изменении значения свойства»
далее в этой главе.
Нестандартные
события базируются на технологии делегатов Visual J++. Чтобы реализовать собственное
событие, Вы создаете делегат, связывающий обработчик события в приложении с
Вашим событием.
Использование
делегатов для событий
В WFC-модели
событий для связывания событий с обрабатывающими их методами используются делегаты.
Чтобы определить событие в Visual J+ + , Вы создаете подкласс класса Event,
затем — класс-делегат на основе com.ms.lang.Delegate, который связывается, с
событием. Делегат позволяет другим классам регистрироваться на уведомление о
событиях, указывая метод-обработчик. При генерации события делегат вызывает
присоединенный метод.
Делегаты
можно связывать с одним или несколькими методами — это называется многоканальным
вещанием (multicasting). Создавая делегат для события, Вы обычно создаете
многоканальное событие (multicast event). Редким исключением бывает событие,
вызывающее срабатывание специфической процедуры (например, вывод диалогового
окна), которую бессмысленно повторять несколько раз в течение одного события.
Многоканальный
делегат содержит список подлежащих вызову методов (invocation list), к которым
он присоединен. Такой делегат поддерживает метод combine для добавления методов
в этот список и remove для их удаления из списка.
Элемент управления
запускает событие, вызывая делегат для этого события. Делегат в свою очередь
вызывает присоединенный к нему метод. В самом общем случае многоканальный делегат
поочередно вызывает все методы из своего списка, что обеспечивает уведомление
типа «один-ко-многим». При этой стратегии элементу управления не
требуется хранить
список объектов, уведомляемых о событии, — всю работу по регистрации, уведомлению
и снятии с регистрации выполняет делегат. Делегаты позволяют также связывать
несколько событий с одним методом, обеспечивая уведомление типа «многие-к-одному».
Например, события click кнопки и команды меню могут вызывать один и тот же делегат,
который будет обращаться к одному и тому же методу обработки этих двух событий.
Используемый
делегатами механизм присоединения является динамическим: в процессе работы делегат
может быть присоединен к любому методу, сигнатура которого соответствует сигнатуре
обработчика события. Это позволяет назначать или менять присоединенный метод
в зависимости от условий и динамически связывать обработчик события с элементом
управления.
Создание
класса нестандартного события
Если Вы хотите
передавать вместе с событием и его данные (по аналогии со свойствами х и у события
mouseMove класса Control), то должны создать свой класс события и определить
его данные. Для этого создайте подкласс класса Event. Чтобы событие было открытым
классом верхнего уровня, поместите его в отдельный JAVA-файл. А в своем классе
определите члены, которые будут хранить данные, относящиеся к событию.
Следующий
пример демонстрирует, как создать простой класс для события error; в нем два
поля: одно — для номера ошибки, другое — для текста сообщения об ошибке.
// ErrorEvent.Java
import com.tns.wfc.core.
*;
public class
ErrorEvent extends Event {
public final
int errorNumber;
public final
String errorText;
public ErrorEvent(int eNum, String eTxt) {
this.errorNumber
= eNum; this.erforText = eTxt;
} }
Создание
делегата
Чтобы разрешить
связывание со своим событием, создайте класс-обработчик, который объявляет делегат,
определяя сигнатуру для события. А чтобы делегат можно было присоединять к нескольким
методам, объявите его как многоканальный (с помощью ключевых слов multicast
и delegate). Делегат должен быть классом верхнего уровня, и поэтому помещается
в отдельный файл. Пример класса-обработчика для ранее показанного класса ErrorEvent:
// ErrorEventHandler.Java
import com.ms.wfc.core.*;
public final
multicast delegate void ErrorEventHandler(Object sender,
ErrorEvent event);
Многоканальные
делегаты должны возвращать void, так как. нельзя вернуть результаты работы сразу
нескольких методов.
Реализация
нестандартного события
Чтобы, реализовать в своем элементе управления нестандартное событие, Вы должны предоставить хост-приложению способ регистрации на уведомление о данном событии. Сначала создайте закрытый экземпляр своего делегата. Потом — метод addOn<событие>, который позволит хост-приложению регистрироваться на уведомление о событии и связать последнее с определенным методом. Если делегат — многоканальный, вызовите его метод combine из своего метода addOn<событие>, чтобы включить пользовательский метод в список подлежащих вызову методов, поддерживаемый делегатом. Для уже показанного класса ErrorEvent это делается так: // создаем экземпляр делегата
private ErrorEventHandler
err = null;
// вызываем метод делегата combine, чтобы добавить связь в список,
// поддерживаемый делегатом public final
void addOnErrorEvent(ErrorEventHandler
handler) {
err = (ErrorEventHandler)Delegate.combine(err,'
handler); }
Кроме того,
Вы обычно предоставляете и метод rетovеОп<событие>, чтобы хост
мог отменить регистрацию на уведомление о событии:
public final
void removeOnErrorEvent(ErrorEventHandler handler) {
err = (ErrorEventHandler)Delegate.
remove(err, handler"); }
Если Ваш
делегат не является многоканальным, синтаксис метода addOn<свойство>
упрощается:
private ErrorEventHandler err = null;
// создается экземпляр делегата
public final
void addOnErrorEvent(ErrorEventHandler handler) {
err = handler;
}
Примечание
Класс Component, от которого наследует элемент управления, содержит
вспомогательный код для добавления, удаления и генерации событий.
Обеспечение
доступа к событию на этапе разработки
Здесь полная
аналогия с обеспечением доступа к свойству на этапе разработки. Прежде всего
Вы создаете экземпляр класса Eventlnfo, указы-
вая класс,
имя и делегат нового события. Далее Вы переопределяете метод getEvents надкласса.
добавляя сначала существующие события надкласса, а затем — свое.
Примечание
Имя события должно начинаться со строчной буквы (например, errorEvent),
кроме тех случаев, когда его первые две буквы прописные (допустим, MDIChildActivated).
Следующий
пример, демонстрирует класс Classlnfo для события Error-Event:
public static
class Classlnfo extends Control.ClassInfo {
public static
Eventlnfo errEvt = new EventInfo(MyControl.class,
"errorEvent", ErrorEventHandler.class);
public void getEvents(IEvents events) { super.getEvents(events);
events. add
(errEvt); } }
Генерация
события
Чтобы вызвать
событие, Вы создаете экземпляр его класса, передавая параметры, определенные
в классе. Затем вызываете делегат, например:
// делегат err уже должен существовать int errNumber = 1020;
String errText = "Invalid value." ErrorEvent e = new ErrorEvent(errNumber, errText);
err.invoke(this,
e);
У большинства
событий в WFC имеется защищенный метод оп<имя_со-бытия>, который позволяет
подклассам переопределять событие и управлять последовательностью его вызова
обращением к методу super.оп<имя_события>. Пример защищенного метода
для ErrorEvent: protected void onErrorEvent(ErrorEvent event) {
if(err != null)
{
err.invoke(this,
event);
} }
Если Вы включили
защищенный метод, то можете генерировать событие из элемента управления, вызывая
этот метод вместо прямого обращения к invoke:
if(value >
20) {
onErrorEvent(new
ErrorEvent(1020, "Invalid value.")); }
Полный
листинг примера
Ниже приведен
листинг простого элемента управления с событием ErrorEvent, описанным в предыдущих
разделах.
// MyControl.
Java import com.ms.wfc.ui.* ; import com.ms.wfc.core.* ; import com.ms.lang.Delegate;
public class
MyControl extends Control { private int myProp = 1; public int getMyProp() {
return myProp;
} public void setMyProp(int value) {
// генерируем
событие error, если значение свойства превышает 200
if(value >
200) {
onErrorEvent(new
ErrorEvent(1020, "Invalid value."));
}
myProp = value;
}
protected void
onPaint(PaintEvent p) {
super.onPaint(p);
Graphics g =
p.graphics;
g.drawString(getText(),
0, 0); }
// настройка, необходимая для генерации события ErrorEvent
private ErrorEventHandler
errOelegate = null;
public final void addOnErrorEvent(ErrorEventHandler handler)
{ errDelegate
= (ErrorEventHandler)Delegate.combine(errDelegate,
handler); }
public final void removeOnErrorEvent(ErrorEventHandler handler)
{ errDelegate
= (ErrorEventHandler)Delegate.remove(errDelegate,
handler); }
protected void onErrorEvent(ErrorEvent event)
{
if(errDelegate
!= null) {
errDelegate.invoke(this,
event); }
}
public static
class Classlnfo extends Control.Classlnfo {
public static
final Propertylnfo myProp = new
PropertyInfo(MyControl.class,
"myProp", int.class);
public void getProperties(IProperties props)
{
super.getProperties(props);
props.add(myProp);
}
public static
Eventlnfo evt = new EventInfo(MyControl.class, "errorEvent, ErrorEventHandler.class);
public void getEvents(IEvents events)
{
super.getEvents(events);
events.add(evt);
}
}
Уведомление
об изменении значения свойства
При создании
событий, сообщающих, что значение свойства изменяется или только что изменено,
в WFC принята следующая схема именования.
Примечание
При связывании с данными требуется реализация события <имяСвойства>
Changed.
Эти события
предназначены не для проверки допустимости данных, а для управления свойствами
в процессе модификации — например, чтобы запретить элементу управления обновлять
свое значение. Проверка данных выполняется либо самим источником (скажем, набором
записей), либо через нестандартные свойства элемента управления. Вы реализуете
события, связанные с изменением свойств, так же, как и нестандартное событие.
Для каждого события Вы предоставляете методы addOn<свойстве» Changing
и addOn<ceowcmeo>Changed, а также соответствующие removeOn-методы. Кроме
того, Вы обычно создаете защищенные методы on<ИмяСвоиства>Changing
и оп<ИмяСвойства>Changed. Первый из них, как правило, использует
объект Cancel-Event, чтобы была возможность запретить изменение. Подробнее о
создании таких методов
см. раздел «Создание собственного события» ранее в этой главе.
Следующий
пример демонстрирует, как включить поддержку уведомления для свойства alignment:
public void setAlignment(int value)
{
if (alignment != value)
{
CancelEvent e = new CancelEvent();
onAlignmentChanging(e);
if (!e.cancel) { alignment = value; invalidate();
// перерисовка
элемента управления с учетом
// нового значения свойства onAlignmentChanged(Event.EMPTY);
} } } }
private EventHandler
eAlignChanged = null;
public final void addOnAlignmentChanged(EventHandler handler)
{
eAlignChanged = (EventHandler)Delegate.combine(eAlignChanged, handler);
} public final void removeOnAlignmentChanged(EventHandler handler)
{
eAlignChanged = (EventHandler)Delegate.remove(eAlignChanged, handler);
} protected void onAHgnmentChanged( Event event)
{
if(eAlignChanged != null)
{
eAlignChanged.invoke(this,
event);
} }
private CancelEventHandler eAlignChanging = null;
public final void addOnAlignmentChanging(CancelEventHandler handler)
{
eAlignChanging = (CancelEventHandler)
Delegate.combine(eAlignChanging,handler);
} public final void removeOnAlignmentChanging(CancelEveptHandler handler)
{
eAlignChanging
= (CancelEventHandler)Delegate.remove(eAlignChanging,
handler);
}
protected void
onAlignmentChanging(CancelEvent event) {
if(eAlignChanging
!= null) {
eAlignChanging.invoke(this,
event);
} }
public static Eventlnfo eiAlignChanged = new EventInfo(MyControl.class,"onAlignmentChanged", EventHandler.class);
public static Eventlnfo eiAlignChanging = new EventInfo(MyControl.class, onAlignmentChanging", EventHandler.class);
public void
getEvents(IEvents events) {
super.getEvents(events);
events.add(eiAlignChanged);
events.add(eiAlignChanging);
}
Настройка
элементов управления
У элемента
управления можно настроить не только его методы, но и функциональность.
Определение
визуального представления
В своем элементе
управления Вы можете создать код, который определяет и модифицирует его визуальное
представление. В следующих разделах приведены базовые сведения о визуализации
элемента управления и управлении этим процессом.
Визуальные
характеристики элемента управления
Визуальные
характеристики элемента управления определяются координатами его границ (bounds),
клиентской области (client) и области отображения (display). Границы элемента
управления — это его контур. Их координаты всегда выражаются относительно координат
клиентской области предка.
Координаты
клиентской области определяют размеры участка, где элемент управления выполняет
операции вывода. Начало координат в клиентской области всегда находится в ее
верхнем левом углу (0, 0), а ее размер определяется внутренней областью, принадлежащей
клиентской. В клиентскую область не включаются обрамление, (например, WS_BORDER,
WS_EX_CLIENTEDGE) и любые заголовки окон верхнего уровня.
Координаты
области отображения являются виртуальными координатами клиентской области и
определяют участок, где отображаются дочерние элементы управления. Они могут
задавать участок, выходящий за рамки координат клиентской области. Пример —
форма с автопрокруткой. Когда это свойство установлено, размер отображаемой
области формы может быть больше, чем размер клиентской, т. е. виртуальная форма
выходит за рамки физического окна. Перемещая движки на полосах прокрутки формы,
Вы изменяете координаты отображаемой области.
Примечание
Так как границы элементов управления всегда выражаются относительно
координат клиентской области предка, то при прокрутке формы координаты границ
дочернего элемента управления корректируются с учетом истинной позиции относительно
координат клиентской области предка.
В большинстве
элементов управления Вы имеете дело только с координатами клиентской области.
Координаты отображаемой области важны, лишь когда Вы создаете элемент управления
— контейнер других элементов (например, элемент TabControl) — и хотите разрешить
стыковку или другие механизмы разметки (компоновки).
Позиция
и размер
Разметка
элемента управления определяется комбинацией значений нескольких свойств, наследуемых
от класса Control:
Эти свойства
можно устанавливать в период выполнения, что позволяет динамически изменять
позицию и размер элемента управления. Если позиция или размер элемента управления
изменяется в период выполнения, элемент генерирует уведомляющие события. Событие
layout вызывается, когда в элементе управления меняется что-то, что заставляет
модифицировать разметку. Примеры таких ситуаций — добавление дочерних элементов
управления, изменение'границ элементов или выполнение других операций над ними
— скажем, модификация свойств. Событие resize генерируется только при изменении
границ элемента. Стандартная логика разметки в WFC поддерживается методами Control.
onLayout и Form. onLayout. Свойства dock и anchor дочернего элемента управления
используются в событии layout предка. Таким образом, сначала панель размещает
дочерние элементы, а после этого предок панели размещает ее саму.
Обновление
визуального представления элемента управления
Любой видимый
элемент управления должен обеспечивать свою визуализацию на экране. Если Ваш
элемент не создает подкласс другого элемента, Вы должны включить в него свою
логику прорисовки, переопределив метод onPaint. В нем вызовите сначала, метод
onPaint надклас-са, чтобы отобразить элемент управления, а потом добавьте свой
код, модифицирующий визуальное представление элемента. Событие onPaint принимает
объект PaintEvent. через который можно получить экземпляр объекта Graphics.
Затем вызывайте методы объекта Graphics, чтобы обновить изображение элемента
управления. Следующий пример показывает, как вывести текст в элементе управления,
изменяя свойство text:
protected void onPaint(PaintEvent p)
{
super.onPaint(p);
Graphics g =
p.graphics;
g.drawString(getText(), 0, 0);
}
WFC-объект
Graphics обладает богатой функциональностью. С его помощью можно рисовать практически
любые простые элементы, в том числе дуги, эллипсы, прямоугольники, многоугольники,
линии и точки. Часто используемые методы объекта Graphics перечислены.в таблице.
Метод Graphics |
Описание |
||
setPen |
Задает объект
Pen, определяющий способ прорисовки линий и границ объектов. |
||
setBrush |
Задает объект
Brush, заполняющий элемент 'управления (например, в методе clearRect). |
||
setBackColor |
Определяет цвет
фона. |
||
setTextColor |
Определяет цвет
текста. |
||
setFont |
Определяет шрифт,
которым выводится текст. |
||
drawArc |
Рисует эллиптическую
дугу. |
||
drawlmage |
Выводит изображение. |
||
drawString |
Выводит строку;
поддерживает перенос строк, выравнивание, отсечение и т. д. |
||
Механизм
прорисовки в WFC использует стандартную модель Win32, при которой область элемента
управления перерисовывается после объявления ее недействительной, но процедура
прорисовки выполняется не сразу. В цикле, показанном ниже, элементу управления
асинхронно посылается событие paint. Когда событие достигает элемента управления,
тот сначала вызывает eraseBackground, чтобы очистить область, подлежащую прорисовке.
События paint
объединяются, поэтому элемент управления получит лишь одно событие paint для
всех своих недействительных областей. Прорисовка, вызываемая объединенным событием
paint, ограничена регионом, получаемым в результате объединения всех недействительных
областей.
Следующий
пример показывает, как вызвать метод invalidate, чтобы заставить элемент управления
обновить свое визуальное представление. Здесь invalidate вызывается без параметра
типа Rectangle, задающего область, поэтому перерисовывается весь элемент управления:
public void setAlignment(int value)
{
if (!AlignStyle.valid(value))
throw new WFCInvalidEnumException("value", value, AlignStyle.class);
align = value;
invalidate(); // перерисовка элемента управления с учетом
// нового значения
свойства }
Примечание
Если Вы модифицируете несколько свойств (или одно и то же свойство
повторно), элемент управления не будет дважды выполнять операцию полной прорисовки.
Чтобы операция
прорисовки стала синхронной, вызовите метод update элемента управления, который
заставляет его немедленно обработать отложенные события paint.
Устранение
мелькания при прорисовке
Для этого
переопределите событие onEraseBackgreund. Стандартная реализация очищает фон
элемента управления с учетом текущего значения свойства backColor. Однако не
всегда нужно перерисовывать всю область элемента управления — более того, выполняя
эту операцию без особой необходимости, Вы не избежите неприятного мелькания.
Такое явление характерно, главным образом, для элементов управления, занимающих
большие участки экрана или имеющих сложную логику прорисовки.
В примере,
приведенном ниже, для вывода текста в элементе управления используется метод
drawstring. Если фон предварительно не очищен методом eraseBackground, он выглядит
так, будто его никогда не прорисовывали, что создает эффект «прозрачности».
Примечание
Элемент управления, конечно же, не прозрачен. Просто, когда содержимое
области экрана не очищается, оно, естественно, остается на экране.
Чтобы создать
элемент управления, изображение которого не мелькает на экране, избегайте прорисовки
фона тех областей, которые будут вновь обновляться — на этот раз отображаемой
информацией. Простейший способ этого добиться — распространить действие метода
onPaint на всю область clientRect. Тогда Вы сможете пропускать прорисовку фона,
переопределив событие onEraseBackground и сообщив, что событие уже обработано:
protected void
onEraseBackground(EraseBackgroundEvent event) {
event.handled
= true; }
protected void
onPaint(PaintEvent p) {
Graphics g =
p.graphics;
g.clearRect(getClientRect());
g.drawString(getText(),
0, 0); }
Этот пример
очень короткий, а код метода onPaint предельно прост, поэтому Вы, вероятно,
не до конца прочувствовали, какие преимущества
дает переопределение события onEraseBackground. В Вашем элементе управления,
где код прорисовки может быть куда сложнее, данный прием значительно уменьшит
мелькание.
Другой способ
повышения визуальной стабильности элемента управления — применение двойной буферизации
экранного изображения. Для этого Вы должны создать растровое изображение всей
клиентской области, а на его основе — объект Graphics. Обновление графических
данных в буфере выполняется гораздо быстрее, чем по обычному механизму.
Двойную буферизацию
следует использовать, лишь когда Вы уже переопределили событие onEraseBackground
и оптимизировали код прорисовки, но мелькание устранить не удалось. Двойная
буферизация требует немалых ресурсов, так как в этом случае приходится хранить
дополнительную копию изображения элемента. Крупное изображение может потребовать
значительного объема памяти (точный объем зависит от размера изображения и его
цветовой глубины), а поддержка буфера — особенно большого — снизить производительность.
Чтобы задействовать в своем элементе управления двойную буферизацию:
В следующем
примере в клиентской области элемента управления выводится звездообразный узор.
Без двойной буферизации изображение будет сильно мелькать.
// Star.Java
import com.ms.wfc.ui.*;
import com.ms.wfc.core.
*;
public class
Star extends Control {
// создаем буфер
Bitmap buffer
= null;
// переопределяем
onResize для создания нового буфера
protected void onResize(Event e) {
if (buffer !=
null) {
buffer.dispose();
// освобождаем ресурсы buffer = null; }
Point s = getClientSize();
buffer = new Bitmap(s.x, s.y); invalidate();
// заставляем
Windows полностью перерисовать
// элемент управления
super.onResize(e); }
protected void
onEraseBackground(EraseBackgroundEvent event) {
event.handled
= true; }
protected void onPaint(PaintEvent pe) { Graphics g = buffer.getGraphics();
Rectangle client
= getClientRect();
// явно устанавливаем
свойства backColor и pen
// в соответствии
со свойствами буфера
g.setBackColor(getBackColor());
g.setPen(new
Pen(getForeColor()));
g.clearRect(client);
int x = 0;
int у = 0;
int xCenter
= client.width/2;
int yCenter
= client.height/2;
// рисуем звезду
for (; x<client.width;
x+=4) {
g.drawLine(x, y, xCenter, yCenter); }
for (; y<client.height;
y+=4) {
g.drawLine(x, y, xCenter, yCenter); }
for (; x>=0;
x-=4) {
g.drawLine(x, y, xCenter, yCenter); }
for (; y>=0;
y-=4) {
g.drawl_ine(x,
y, xCenter, yCenter); }
g.dispose();
pe.graphics.drawlmage(buffer,
0, 0); }
public static
class Classlnfo extends Control.Classlnfo { } }
Добавление
битовой карты для элемента управления
Частью визуального
представления Вашего элемента управления может быть битовая карта (растр) размером
16x16 пикселов. Она отображается в Toolbox среды Visual J+ + . А также показывается
для элементов управления, не имеющих визуального представления в период выполнения,
— например, для таймеров.
Примечание
Битовая карта выводится, только когда элемент управления используется
как WFC-элемент. Если он применяется как ActiyeX-элемент, в Toolbox отображается
стандартная, предопределённая битовая карта.
По умолчанию
объект Classlnfo загружает любое растровое изображение, имя файла которого совпадает
с именем файла для элемента управления. Если элемент хранится в MyControl.java,
с ним можно сопоставить растровое изображение, добавив файл MyControl.bmp в
ту же папку, где находится файл MyControl.class.
Кроме того,
можно указать конкретный файл растрового изображения, переопределив метод getToolboxBitmap
в своем подклассе Classlnfo. Вот как это делается:
public Bitmap getToolboxBitmap()
{
return = new Bitmap(MyControl.class, "Gears.bmp");
}
Заданное
растровое изображение должно иметь размер не более 16x16 пикселов и использовать
не более 16 цветов. Visual J+-I- способен автоматически изменять размер загружаемого
растрового изображения, но обычно это ухудшает его качество.
Создание
настройщиков элементов управления
Настройщики
(customizers) — тип метаданных периода разработки для конкретного экземпляра.
Класс Classlnfo определяет метаданные (metadata), специфичные для класса, а
настройщик обеспечивает доступ к более глубокой («продвинутой»)
функциональности периода разработки. Создав объект Customizer, Вы можете придать
элементу управления такие возможности, как активизация в период разработки (design-time
activation), определение операций для элемента управления (control verbs) и
создания собственных (нестандартных) страниц свойств (синоним: design pages).
Как и в случае
редакторов значений, здесь тоже предлагается стандартная реализация com.ms.wfc.core.Customizer
в интерфейсе com.ms.wfc.core.ICustomizer. Вы можете в Classlnfo переопределить
метод getCusto-mizer, чтобы он возвращал экземпляр настройщика.
Включение
активизации в период разработки
WFC поддерживает
активизацию элементов управления на этапе разработки хост-приложений (например,
прокрутку или раскрытие списка) на основе простой схемы проверки попаданий (hit
testing). Когда элемент управления выбран, его настройщику передаются запросы
на проверку попаданий. Если проверка дает true, элемент считается активным в
данной позиции. Такая простая архитектура обеспечивает активизацию отдельных
участков элемента управления, скажем, отдельного ярлычка на полоске ярлычков
(tab strip).
Ниже демонстрируется
несложный способ проверки попаданий, позволяющий элементу получать сообщения,
когда мь!шь находится в пределах 50 пикселов верхней части его окна.
Примечание
Элемент управления активизируется (и генерируется событие getHitTest),
только когда он является главным выбранным компонентом.
import com.ms.wfc.ui.*;
import com.ms.wfc.core.*;
public class MyTabControl extends Control
{
public static class Classlnfo extends Control.ClassInfo
{
public ICustomizer getCustomzier(Object comp)
{
return
((MyTabControl)comp).new
Customizer();
} }
public class Customizer extends com.ms.wfc.core.Customizer
{
public boolean getHitTest(Point pt)
{
if (pt.y < 50)
{
return true;
}
return false;
}
} }
Определение
операций для элемента управления
Вы можете
указать операцию (verb), выполняемую над объектом на этапе разработки. Они обычно
доступны в контекстном меню среды программирования как обычные команды.
Чтобы определить
операцию, создайте подкласс класса Customizer, а затем — объект CustomizerVerb,
позволяющий задать текст для названия операции и создать делегат, связывающий
эту операцию с заданным Вами методом.
Следующий
пример иллюстрирует, как создать команду с именем About. Класс About обеспечивает
одну операцию, обозначаемую в контекстном меню как команда «About»;
она выводит простое окно с сообщением. В этот класс включен метод, предназначенный
для отображения результатов, получаемых после команды About.
// About.Java
import com.ms.wfc.ui.*;
import com.ms.wfc.core.
*;
public class About extends Control
{
public static class Classlnfo extends Control.Classlnfo
{
public ICustomizer getCustomizer(Object comp)
{
return ((About)comp).new
Customizer();
} }
public class Customizer extends com.ms.wfc.core.Customizer {
public CustomizerVerb[]
getVerbs() {
Customize.rVerb
v = new CustomizerVerb("About",
new VerbExecuteEventHandler(About.this.showAbout));
return new CustomizerVerb[] {v};
} }
private void showAbout(Object sender, VerbExecuteEvent event) {
MessageBox.show("This
control was written in WFC", "About", MessageBox.OK);
} }
Кроме данных
и делегата. Вы можете определить состояния команды «помечена» (checked)
и «разрешена» (enabled) и сопоставить с ней растровое изображение.
Хотя в настоящее время Visual J++ не показывает растровое изображение, определенное
в настройщике (например, для контекстного меню), некоторые другие хост-приложения
допускают такую возможность.
Определение
страниц свойств
Страницы
свойств (называемые в WFC как design pages) можно редактировать в среде программирования.
Чтобы реализовать такую страницу, Вы создаете класс, расширяющий класс DesignPage
и переопределяющий методы onReadProperty и onWriteProperty.
Примечание
Хотя WFC поддерживает страницы свойств, рекомендуется предоставлять
функциональность элемента управления через нестандартные редакторы, операции
и поддержку активизации
периода разработки. Подробнее см. раздел «Создание собственного редактора
значений свойств» ранее в этой главе.
Ниже показано,
как создать страницу для свойства alignment. Значения этого свойства (left,
center, right) реализуются как группа переключателей. Метод onReadProperty проверяет,
действительно ли ему передано свойство alignment, и возвращает значение свойства,
установленное на странице. Метод onWriteProperty тоже проверяет свойство и отображает
его значение на странице. В обработчике, едином для всех переключателей, вызывается
метод setDirty, который помечает страницу свойств как измененную и делает доступной
кнопку Apply в окне Properties.
// SuperLabelOP.java import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
public class SuperLabelDP extends DesignPage {
public SuperLabelDP()
{
initForm();
}
private void
setAlign(int value) { switch (value) {
case AlignStyle.LEFT:radioButtonl.setChecked(true);
break;
case AlignStyle.CENTER:radioButton2.setChecked(true);
break;
case AlignStyle.RIGHT:
radioButton3.setChecked(true);
break; } }
private int
getAlign() {
int align = AlignStyle.LEFT;
if (radioButton1.getChecked())
{
align = AlignStyle.LEFT; }
else if (radioButton2.getChecked())
{
align = AlignStyle.CENTER; }
else if (radioButton3.getChecked())
{
align = AlignStyle.RIGHT;
}
return align;
}
private void
radioClicked(Object sender, Event e) {
setDirty();
}
protected Object
onRe.adProperty(String name) {
if (name.equals("alignment"))
{ return new Integer(getAlign());
}
return null;
}
protected v6id onWriteProperty(String name, Object value) {
if (name.equalsC'alignment")
&& value instanceof Integer) {
setAlign(((Integer)value).intValue());
}
}
Container components = new Container();
GroupBox groupBoxl = new GroupBox();
RadioButton radioButtonl = new RadioButton();
RadioButton radioButton2 = new RadioButton();
RadioButton radioButtonS = new RadioButton();
private void
initForm() {
this.setText("Alignment");
this.setAutoScaleBaseSize(13);
this.setBorderStyle(FormBorderStyle.NONE);
this.setClientSize(new
Point(307, 131));
this.setControlBox(false);
this.setMaxButton(false);
this.setMinButton(false);
groupBoxl.setLocation(new Point(8, 8));
groupBoxl.setSize(new Point(128, 112));
groupBoxl.setTablndex(0);
groupBoxl.setTabStop(false);
groupBoxl.setText("Alignment ");
radioButtonl.setLocation(new Point(8, 16));
radioButtonl.setSize(new Point(112, 25));
radioButtonl.setTablndex(0);
radioButtonl.setTabStop(true);
radioButtonl.setText("Left");
radioButtonl.setChecked(true);
radioButtonl.addOnClick(new
EventHandler(this.radioClicked));
radioButton2.setLocation(new
Point(8, 48));
radioButton2.setSize(new
Point(112, 25));
radioButton2.setTabIndex(1);
radioButton2.setText("Center");
radioButton2.addOnClick(new
EventHandler(this.radioClicked));
radioButton3.setLocation(new
Point(8, 80));
radioButton3.setSize(new
Point(112, 25));
radioButtonS.setTabIndex(2);
radioButton3.setText("Right");
radioButton3.addOnClick(new
EventHandler(this.radioClicked));
this.setNewControls(new
Control[] {
groupBoxl});
groupBox1.setNewControls(new
Control[] { radioButton3, radioButton2, radioButtonl}); } }
Использование
элементов управления
Создав WFC-элемент,
Вы можете задействовать ею в хост-приложениях точно так же, как и любой другой
элемент управления.
Регистрация
элемента управления
Элементы
управления регистрируются на компьютере, на котором они будут работать. Каждый
элемент должен содержать идентификацион-ную информацию, считываемую в процессе
регистрации и помещаемую в реестр Windows. Считывая регистрационную информацию,
приложения смогут находить и загружать элемент управления.
Примечание
Если Вы используете элемент управления на том же компьютере, где создавался
проект этого элемента, регистрировать его не требуется — при сборке он регистрируется
автоматически. И, кроме того, если Вы применяете элемент управления в среде
Visual J++, идентификационную информацию можно не указывать.
Хост-приложение
для работы с Вашим элементом управления требует также библиотеку типов (TLB-файл).
Оно считывает информацию, содержащуюся в библиотеке типов, чтобы узнать, какие
свойства, события и методы поддерживает Ваш элемент управления. Visual J++ способен
автоматически создавать библиотеку типов при сборке элемента управления.
Определение
информации для регистрации и библиотеки типов
Для регистрации
нужна следующая информация:
Если Вы создаете
элемент управления с помощью WFC Component Builder, идентификаторы класса генерируются
автоматически. Кроме того, Visual J + + автоматически создает такие идентификаторы
при сборке элемента управления, если Вы сообщаете, что данный элемент является
COM DLL. Вы выбираете этот вариант на вкладке COM Classes диалогового окна свойств
проекта. Подробнее о сборке DLL см. главу 17 «Сборка и импорт СОМ-объектов».
Наконец,
можно самостоятельно создать идентификаторы класса для своего элемента управления.
Так поступают, когда хотят гарантировать неизменность идентификатора для элемента
управления или просто знать его по какой-то другой причине, не полагаясь на
Visual J+ + . Вы можете сгенерировать GUID программно, используя, например,
Uuid-gen.exe — свободно распространяемую утилиту (ее можно найти на Web-узле
Microsoft). Всего Вы должны предоставить два идентификатора класса: один — для
элемента управления, другой — для его библиотеки типов.
Чтобы введенная
вручную регистрационная информация стала доступной программе регистрации, включите
ее в JAVA-файл элемента управления как Javadoc-комментарий:
/*
* @com.register
(clsid=guid, typelib=guid)
*/
Пример блока
регистрации показан ниже. Последний идентификатор здесь перенесен на другую
строку, но в Вашем файле такого переноса не должно быть.
/*
* @com.register
(Clsid=d0702fa0-fb3b-11d1-8f88-00aa00600a54,
typelib=d3108a20-fb3b-11d1-8f88-00aa00600a54)
*/
При сборке
проекта Вашего элемента управления Visual J+ + ищет этот блок. Если он найден,
создается библиотека типов. Затем элемент управления и библиотека типов регистрируется
на Вашем компьютере.
Создание
ProgID
При регистрации
идентификатор progID для Вашего элемента управ- . ления создается автоматически.
Его формат таков:
ИмяПроекта.ИмяЭлементаУправления
Например,
если проект называется MyProject, a JAVA-файл элемента управления — MyControl.java,
то после регистрации элемента, его progID будет иметь вид MyProject.MyControl.
Запуск
процесса регистрации
Если Вы собираетесь
использовать элемент управления на том же компьютере, где и создавали его, регистрировать
элемент самостоятельно не требуется — при сборке он будет зарегистрирован автоматически.
А если Вы распространяете элемент управления, то должны зарегистрировать его
на каждом компьютере, на котором он будет работать. Создав элемент управления
в виде COM DLL, можно зарегистрировать его обычным способом — с помощью Windows-программы
Regsvr32.exe:
Regsvr32.ехе
путь/имя_элемента_управления
Если элемент
управления представляет собой просто CLASS-файл, используйте утилиту командной
строки Vjreg.exe, поставляемую с Visual J++. (Regsvr32.exe в этом случае не
годится, так как не предназначена для регистрации CLASS-файлов.)
Vj reg путь/имя_элемента_управления
Работа
с элементом управления в хост-приложении
Вы можете
работать со своим элементом управления в хост-приложениях типа Visual J+ + ,
Visual Basic или Internet Explorer так же, как и с любыми другими элементами
управления. Чтобы^запустить элемент управления, созданный в Visual J++, на хост-компьютере
должна быть:
Кроме того,
для каждого элемента управления нужно предоставить:
Примечание
Если Вы запускаете элемент управления на компьютере, на котором был
собран его проект, все эти файлы уже имеются.
Установив
файлы на хост-компьютер, зарегистрируйте элемент управления (см. раздел «Регистрация
элемента управления» ранее в этой главе). Тогда Вы сможете создавать экземпляры
элемента управления. Многие хост-приложения позволяют включать элемент управления
в панель Toolbox.
Например, в Visual Basic можно щелкнуть Toolbox правой кнопкой мыши, выбрать
из контекстного меню команду Components, а затем — свой элемент управления.
Использование
элемента управления в Internet Explorer
Чтобы задействовать
элемент управления в Internet Explorer, создайте тэг <OBJECT>. В атрибуте
CLASSID идентифицируйте свой элемент управления одним из следующих способов.
<OBJECT CLASSID="progid:MyProject.MyControl">
</OBJECT>
<OBJECT CLASSID="clsid:d0702fa0-fb3b-11d1-8f88-00aa00600a54">
</OBJECT>
<OBJECT CLASSID="JAVA:MyControl">
</OBJECT>
Создание
составных WFC-элементов
WFC-модель компонентов позволяет создавать элементы управления двух типов: нестандартные (custom) и составные (composite). Нестандартные элементы управления наследуют от класса com.ms.-wfc.ui.Control. Вы можете разработать собственный элемент либо с самого нуля, либо создав подкласс существующего WFC-элемента. Подробнее см. раздел «Создание WFC-элементов» ранее в этой главе. Составные элементы управления включают в себя другие элементы управления. Они наследуют от класса com.ms.wfc.ui.UserControl. Так как UserControl — подкласс класса com.ms.wfc.ui.Form, Вы можете скомпоновать элементы, образующие Ваш составной элемент, в Forms Designer.
Из этого раздела
Вы узнаете, как:
Создание
проекта элемента управления
Шаблон Control
в Visual J++ служит отправной точкой в создании WFC-элемента. Он предоставляет
класс, производный от com.ms.wfc.ui.UserControl и содержащий класс Classlnfo,
производный от User-Control. Classlnfo.
Примечание
Прежде чем создавать элемент управления по следующей процедуре, закройте
любые открытые в данный момент проекты. (Для этого выберите из меню File
команду Close All.)
Чтобы
создать проект элемента управления по шаблону Control:
Примечание
Переименование этого файла не приводит к переименованию соответствующего
класса в исходном коде, и наоборот. Вам придется вручную модифицировать все
экземпляры старого имени. (Обратите внимание, что Вы можете создать пустой проект
и только после этого добавить класс элемента управления. Тогда Вам удастся присвоить
имя элементу управления еще до его создания.)
Следующий
шаг — компоновка элемента управления.
Проектирование
элемента управления
Для визуальной
разработки и редактирования составного элемента управления используйте Forms
Designer. Этот процесс идентичен разработке формы. Вы можете добавлять элементы
управления из Toolbox, перемещать и масштабировать их на поверхности UserControl,
определять свойства и создавать обработчики событий.
Следующий
пример демонстрирует создание составного элемента управления из GroupBox и CheckBox,
помещаемого в левый верхний угол GroupBox. После размещения этого составного
элемента на форме Вы сможете добавлять в его область GroupBox другие элементы
управления. Сброс его флажка CheckBox отключает элементы управления, добавленные
на составной элемент, а установка — вновь активизирует.
Чтобы
добавить элементы управления в UserControl:
Если Toolbox на экране
нет. выберите из меню View команду Toolbox.
Добавляется элемент
управления CheckBox с именем по умолчанию — checkBoxl. Убедитесь,
что CheckBox помещен внутрь GroupBox. Для экономии места на экране рекомендуется
изменить размер User-Control. А в связки с этим важно, чтобы при масштабировании
UserControl изменялись и размеры включенных в него элементов управления.
Чтобы
установить размер элементов управления:
Размер поверхности
UserControl на этапе разработки соответствует изначальному размеру готового
элемента управления, добавленному на форму.
Чтобы
установить свойства элементов управления:
Следующий шаг — добавление
в элемент управления нового свойства.
Добавление
нестандартного свойства с помощью WFC Component Builder
WFC Component
Builder позволяет добавлять и удалять нестандартные свойства. Он выполняет за
Вас всю подготовительную работу, необходимую для определения новых свойств элемента
управления.
Чтобы
добавить свойство с помощью WFC Component Builder:
Откроется окно редактора
Text с исходным кодом Вашего элемента управления.
Поле, список
или флажок |
Значение |
||
Name Data Type Category Description Read-only Property Declare Member
Variable |
checked Boolean Behavior Определяет состояние флажка Сброшен Сброшен |
||
WFC Component
Builder создает методы getChecked и setChecked и добавляет определение свойства
в класс Classlnfo элемента управления.
Следующий
шаг — добавление кода в методы getChecked и setChecked.
Добавление
кода в методы свойств
WFC Component
Builder создает методы и поля, необходимые для определения и реализации нестандартных
свойств. Обычно Вы изменяете этот код, реализуя требуемую функциональность.
Чтобы
добавить код в собственные методы свойств:
return checkBox1.getChecked();
checkBoxl.setChecked(value);
onCheckedChanged(Event.EMPTY);
enableControls(this, value);
Этот код устанавливает
состояние CheckBox по значению, переданному в метод свойства checked. Кроме
того, вызываются методы onCheckedChanged и enableControls. Вызов onCheckedChanged
генерирует нестандартное событие checkedChanged, которое будет добавлено
чуть позже.
Значение Event.EMPTY,
передаваемое методу onCheckedChanged, определяет пустой объект Event, который
присваивается событию checkedChanged. Вызов метода enableControls включает
или отключает элементы, входящие в GroupCheck. Этот метод тоже будет добавлен
чуть позже. Следующий шаг — добавление события к элементу управления.
Добавление
событий с помощью WFC Component Builder
WFC Component
Builder позволяет добавлять и удалять нестандартные события в Ваши элементы
управления. Это освобождает Вас от необходимости вручную определять события
в классе Classlnfo элемента управления.
Чтобы
добавить событие с помощью WFC Component Builder:
Поле, список
или флажок |
Значение |
||
Name Туре Category Description |
checkedChanged Event Action Возникает при
изменении состояния флажка элемента управления |
||
WFC Component
Builder создает методы addOnCheckedChanged, removeOnCheckedChanged и onCheckedChanged
и добавляет определение события в класс Classlnfo элемента управления. WFC Component
Builder добавляет также экземпляр делегата EventHandler, используемого событием.
Следующий
этап — переопределение унаследованных методов.
Переопределение
методов UserControl
Class Outline
упрощает переопределение методов классов, наследуемых Вашим элементом управления.
Код переопределенного метода, создаваемый Class Outline, послужит отправной
точкой в реализации метода класса Вашего элемента управления. Комментарии, добавляемые
Class Outline в код переопределенного метода, помогут быстро определить место,
в которое следует вносить собственный код.
Чтобы
переопределить метод с помощью Class Outline:
Class Outline добавит
определение метода в Ваш код.
Следующий шаг — добавление
кода в переопределенные методы.
Добавление
кода в переопределенные методы
Создав переопределенные
методы с помощью Class Outline, Вы добавляете в них код своей реализации. В
зависимости от того, как Вы реализуете переопределенный метод, можете либо оставить,
либо удалить вызов версии этого метода, реализованной в надклассе.
Добавление
кода в метод add
Чтобы в группу
GroupBox элемента GroupCheck можно было добавлять элементы управления, Вы вставляете
в метод add элемента GroupCheck код, вызывающий метод add элемента GroupBox.
В итоге предком добавляемого элемента управления будет GroupBox, а не GroupCheck.
Чтобы
добавить код в метод add:
private boolean m_bReady
= false;
if(m_bReady) {
control.setEnabled(checkBox1.getChecked());
groupBoxl.add(control);
}
else
super, add (control);
Этот код проверяет,
присвоено ли переменной-члену m_bReady значение true, чтобы не допустить
добавление элемента управления GroupBox к самому себе. Если m_bReady равна
true, код вызывает метод setEnabled элемента управления, переданного методу
в качестве параметра. Методу setEnabled передается состояние флажка CheckBox
элемента GroupCheck. Так как элементы управления могут добавляться в GroupCheck
и тогда, когда флажок сброшен, важно, чтобы их состояние (включен/отключен)
было установлено корректно.
Далее вызывается метод
add элемента GroupBox, которому передается добавляемый элемент; тем самым
элемент вставляется именно в GroupBox, а не UserControl. Если переменная-член
m_bReady равна false, вызывается метод add надкласса и в качестве параметра
вновь передается добавляемый элемент управления.
Добавление
кода в методы, связанные с элементами управления
Пользователь
сможет получить доступ к элементам управления в Group-Box после того., как Вы
добавите в методы getControl, getControlCount и getControls код, вызывающий
реализацию этих методов в GroupBox.
Чтобы
добавить код в методы, связанные с элементами управления:
return groupBox1 .getControl(index);
return groupBox1 .getControlCount();
return groupBox1 .getControls();
Добавление
кода в метод remove
Пользователь
сможет удалять элементы управления из GroupCheck после того, как Вы добавите
в метод remove элемента GroupCheck код, вызывающий метод remove элемента GroupBox.
Чтобы
добавить код в метод remove:
if(m_bReady) {
groupBoxl.remove(c);
}
else { ,
super, remove(c); }
Этот код
проверяет, присвоено ли переменной m_bReady значение true. Если да, вызывается
метод remove элемента GroupBox с удаляемым элементом управления в качестве параметра.
Проверка m_bReady выполняется, чтобы предотвратить удаление CheckBox и GroupBox.
Если m_bReady равна false, вызывается метод remove надкласса, что гарантирует
корректную обработку удаляемого элемента управления.
Добавление
кода в метод setText
Элемент управления
GroupBox не предусматривает автоподстройки размера текстовой части элемента
под размер выводимого текста. Для корректного отображения элементом управления
GroupCheck содержимого свойства text, переопределите метод setText так, чтобы
он вычислял ширину элемента по размеру выводимого текста.
Чтобы
добавить код в метод setText:
Graphics g = checkBox1.createGraphics();
checkBox1.setWidth(g.getTextSize(value).x
+ 20);
g.dispose();
checkBoxl.setText(value);
super.setText(value);
Этот код
определяет размер текста методами класса Graphics. Вычисленная длина увеличивается
на 20, чтобы учесть размер флажка и расстояние от него до текстовой части элемента
CheckBox. Затем вызывается метод dispose класса Graphics, чтобы освободить ранее
выделенные ресурсы, устанавливается свойство text элемента Check-Box и вызывается
метод setText надкласса. Следующий шаг — добавление в элемент управления нового
метода.
Добавление
методов в элемент управления
Разрабатывая
элемент управления, Вы обычно предоставляете пользователям методы для операций
с этим элементом. В случае с GroupCheck надо добавить метод, который включает
или отключает находящиеся в GroupBox элементы управления, исходя из состояния
флажка CheckBox. Этот метод вызывается из setChecked.
Чтобы
добавить метод в элемент управления:
public void enableControls(Control start, boolean enable) {
for(int i =
0; i < start.getControlCount(); i++) {
Control с =
start.getControl(i);
if(c == groupBoxt
|| с == checkBoxl) { continue;
}
c.setEnabled(enable);
enableControls
(c, enable); } }
Метод enableControls
принимает в качестве параметров элемент управления и значение типа boolean,
указывающее, в какое состояние надо установить элементы, содержащиеся в GroupBox,
— активное или отключенное. Когда он вызывается из setChecked, ему передается
текущий экземпляр GroupCheck.
Метод enableControls
начинает с перебора всех элементов управления, содержащихся в параметре start.
В цикле for код получает элемент управления методом getControl, указывая текущее
значение счетчика цикла. Если элемент не является GroupBox или CheckBox элемента
GroupCheck, он включается или отключается на основе значения параметра enable.
После этого любые элементы управления, содержащиеся в данном элементе, тоже
включаются или отключаются посредством рекурсивного вызова метода enableControls.
Следующий шаг — добавление кода в конструктор.
Для присвоения
начальных значений членам своего элемента управления Вы должны добавить код
в конструктор. В данном случае — код, инициализирующий GroupCheck при вставке
на форму.
Чтобы
добавить код в конструктор:
initForm();
setStyle(this.STYLE_ACCEPTSCHILDREN, true);
m_bReady = true;
Этот код
устанавливает такой стиль элемента GroupCheck, который позволяет принимать дочерние
элементы управления. Он также присваивает закрытой переменной-члену m_bReady
значение true, чтобы метод add элемента GroupCheck узнавал о завершении элементом
своей инициализации и его готовности к приему элементов управления, добавляемых
в GroupBox. Следующий шаг — сборка элемента управления.
Чтобы использовать
свой элемент управления, надо сначала его собрать. После этого его можно добавить
на панель Toolbox.
Чтобы
собрать элемент управления:
Любые сообщения об
ошибках при компиляции или предупреждения появляются в окне Task List. (Двойной
щелчок сообщения об ошибке в этом окне переводит курсор ввода в тот участок
исходного кода, который вызвал данную ошибку.)
Собрав элемент
управления, Вы тестируете и отлаживаете его, чтобы он работал именно так, как
Вы задумали. Для этого добавьте свой элемент в Toolbox, включите в проект форму
и поместите на нее этот элемент. В данном случае надо добавить в GroupCheck
и другие элементы управления, создать обработчик нестандартного события, собрать
проект и запустить его.
Добавление
элемента управления в Toolbox
Вы добавляете
элемент в Toolbox, чтобы сделать его доступным для использования.
Чтобы
добавить элемент управления в Toolbox:
Добавление
формы в проект
Для тестирования
и отладки элемента управления включите в проект форму.
Чтобы
добавить в проект форму:
Имя формы не должно
совпадать с именем элемента управления (здесь — GroupCheck), иначе
возникнет конфликт имен.
Форма будет
добавлена в проект под указанным Вами именем и открыта в Forms Designer.
Добавление
элемента управления на форму
Тестирование
элемента управления требует его вставки на форму. Чтобы добавить элемент управления
на форму:
Добавление
элементов управления в GroupCheck
Теперь убедитесь,
что GroupCheck корректно «усыновляет» добавляемые в него элементы
управления. Для этого вставьте в него другие WFC-элементы.
Чтобы
добавить в GroupCheck другие элементы управления:
Создание
обработчиков событий
GroupCheck
имеет нестандартное событие checkedChanged. Оно возникает, когда устанавливается
или сбрасывается флажок CheckBox, содержащийся в GroupCheck. Проверьте, правильно
ли вызывается это событие, и для этого добавьте к форме обработчик события checkedChanged.
Чтобы
добавить обработчик события checkedChanged:
В окне редактора
Text появится пустой обработчик ошибок. Для проверки, вызывается ли событие
checkedChanged, введите в обработчик код, открывающий окно с сообщением при
возникновении этого события.
Чтобы
добавить код в обработчик события:
MessageBox.show("The
checkedChanged event was triggered.");
Сборка
проекта и тестирование элемента управления
Поместив
элемент управления на форму, добавив в него другие элементы управления и обработчик
события checkedChanged, Вы собираете и запускаете проект.
Чтобы
собрать и запустить форму:
Так как Вы запускаете
проект в первый раз и он содержит два JAVA-файла, на экран выводится диалоговое
окно Project Properties.
Подробнее
о свойствах проекта см. раздел «Настройка параметров проекта»
главы 1 «Создание проектов».
Пока выполняется
проект, Вы можете манипулировать элементом управления и тем самым проверить,
нормально ли он работает.
Чтобы проверить элемент управления в период выполнения:
На экране появится
окно с сообщением о том, что возникло событие checkedChanged. Это событие
возникает всякий раз, когда меняется состояние флажка. Элементы управления,
содержащиеся в GroupCheck, либо включаются, либо отключаются в зависимости
от состояния флажка.
Об экспорте WFC-элемента как ActiveX-элемента см. раздел «Создание элементов управления ActiveX» главы 16 «Сборка и импорт ActiveX-элементов».