Чтобы разделить CarControl на две прямоугольных области, создайте приватную вспомогательную функцию с именем StretchBox. Задачей этого члена будет вычисление правильных размеров члена bottomRect и гарантия того, что элемент PictureBox будет растянут на верхние примерно две трети поверхности типа CarControl.
private void StretchBox {
// Конфигурация окна изображения.
currentImage.Top = 0;
currentImage.Left = 0;
currentImage.Height = this.Height – 50;
currentImage.Width = this.Width;
currentImage.Image = carImages.Images[(int)AnimFrames.Lemon1];
// Выяснение размеров нижнего прямоугольника.
rect.bottomRect.X = 0;
bottomRect.Y = this.Height – 50;
bottomRect.Height = this.Height – currentImage.Height;
bottomRect.Width = this.Width;
}
После установки размеров каждого прямоугольника в рамках конструктора, заданного по умолчанию, вызывается StretchBox.
public CarControl {
InitializeComponent;
StretchBox;
}
Тип CarControl обеспечивает поддержку двух событий, отправляемых содержащей тип форме в зависимости от текущей скорости автомобиля. Первое событие, AboutToBlow, генерируется тогда, когда скорость CarControl приближается к верхнему пределу. Событие BlewUp отправляется контейнеру тогда, когда текущая скорость становится больше позволенного максимума. Каждое из этих событий использует пользовательский делегат (CarEventHandler), который может содержать адрес любого метода, возвращающего void и получающего System.String в качестве параметра. Мы обработаем эти события чуть позже, a пока что добавьте к группе открытых элементов CarControl следующие члены.
// События и пользовательский делегат Car.
public delegate void CarEventHandler(string msg);
public event CarEventHandler AboutToBlow;
public event CarEventHandler BlewUp;
Замечание. Напомним, что "настоящий и полноценный" делегат (см. главу 8) должен указать два аргумента, первым из которых должен быть System.Object (представляющий отправителя), а вторым – тип, производный от System.EventArgs. Однако для нашего примера вполне подойдет и предложенный выше делегат.
Как и любой другой тип класса, элемент управления может определять набор свойств, с помощью которых внешние объекты смогут выяснить (или изменить) состояние этого элемента. Нам понадобится определить только три свойства. Сначала рассмотрим свойство Animate. Это свойство включает или отключает тип Timer.
// Используется для конфигурации внутреннего типа Timer.
public bool Animate {
get { return IsAnim; }
set {
IsAnim = value;
imageTimer.Enabled
}
}
Свойство PetName выполняет то, что и следует ожидать, исходя из его имени, и не требует подробных комментариев. Однако заметьте, что при установке пользователем соответствующего имени выполняется вызов Invalidate, чтобы это имя CarControl отобразилось в нижней прямоугольной области элемента управления (сам этот шаг будет сделан чуть позже).
// Выбор имени машины.
public string PetName {
get { return carPetName; }
set {
CarPetName = value;
Invalidate;
}
}
Далее, у нас есть свойство Speed. Вдобавок к простому изменению члена currSp, свойство Speed – это элемент, "стимулирующий" генерирование событий AboutToBlow и BlewUp, в зависимости от текущей скорости CarControl. Вот как выглядит соответствующая программная логика.
// Проверка currSp и currMaxFrame и генерирование событий.
public int Speed {
get { return currSp; }
set {
// В пределах безопасной скорости?
if (currSp ‹= maxSp) {
currSp = value;
currMaxFrame = AnimFrames.Lemon3;
}
// Вблизи взрывоопасной ситуации?
if ((maxSp – currSp) ‹= 10) {
if (AboutToBlow != null) {
AboutToBlow("Чуть помедленнее, парень!");
currMaxFrame = AnimFrames.AboutToBlow;
}
}
// Превышаем?