// Вспомогательный перечень для изображений.
public enum AnimFrames {
Lemon1, Lemon2, Lemon3,
AbоutТоBlow, EngineBlown
}
Тип CarControl поддерживает достаточно большой набор приватных данных, необходимых для представления программной логики анимации. Вот краткое описание каждого из членов.
public partial class CarControl: UserControl {
// Данные состояния.
private AnimFrames currFrame = AnimFrames.Lemon1;
private AnimFrames currMaxFrame = AnimFrames.Lemon3;
private bool IsAnim;
private int currSp = 50;
private int maxSp = 100;
private string carPetName= "Lemon";
private Rectangle bottomRect = new Rectangle();
public CarControl() {
InitializeComponent();
}
}
Как видите, здесь есть данные, представляющие текущую и максимальную скорости, название автомобиля, а также два члена типа AnimFrames. Переменная currFrame используется для указания того, какой из членов ImageList следует отобразить. Переменная currMaxFrame используется для обозначения текущего верхнего предела в ImageList (напомним, что в цикле анимации CarControl используются от трех до пяти изображений, в зависимости от скорости автомобиля). Элемент данных IsAnim используется для определения того, что автомобиль в настоящий момент находится в режиме использования анимации. Наконец, член Rectangle(bottomRect) используется для представления нижней части области CarControl. Позже в этой части элемента управления будет отображаться название автомобиля.
Чтобы разделить 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 delegatevoid CarEventHandler(string msg);
public eventCarEventHandler AboutToBlow;
public eventCarEventHandler BlewUp;
Замечание. Напомним, что "настоящий и полноценный" делегат (см. главу 8) должен указать два аргумента, первым из которых должен быть System.Object (представляющий отправителя), а вторым – тип, производный от System.EventArgs. Однако для нашего примера вполне подойдет и предложенный выше делегат.
Определение пользовательских свойств
Как и любой другой тип класса, элемент управления может определять набор свойств, с помощью которых внешние объекты смогут выяснить (или изменить) состояние этого элемента. Нам понадобится определить только три свойства. Сначала рассмотрим свойство Animate. Это свойство включает или отключает тип Timer.
// Используется для конфигурации внутреннего типа Timer.
public bool Animate {
get { return IsAnim; }
set {
IsAnim = value;
imageTimer.Enabled = IsAnim;
}
}
Свойство PetName выполняет то, что и следует ожидать, исходя из его имени, и не требует подробных комментариев. Однако заметьте, что при установке пользователем соответствующего имени выполняется вызов Invalidate(), чтобы это имя CarControl отобразилось в нижней прямоугольной области элемента управления (сам этот шаг будет сделан чуть позже).
Читать дальше