Продолжение темы о создании собственного элемента управления с нуля.
В прошлый раз мы остановились на создании класса, отвечающего за набор записей в нашем элементе управления. Прокомментируем некоторые куски кода.
private struct ItemInCollection — Является внутренней структурой, которая доступна только в рамках класса списка элементов. Играет роль упрощения хранения данных и не более.
private Object _SyncRoot = null;
private bool _IsSynchronized = true;
public Object SyncRoot {get{return this._SyncRoot;}}
public bool IsSynchronized {get{return this._IsSynchronized;}}
public void CopyTo(Array A, int Idx);
public bool MoveNext();
public void Reset();
object IEnumerator.Current;
public object Current;
IEnumerator IEnumerable.GetEnumerator();
— Является обязательными. Так как наш класс является наследником интерфесов ICollection и IEnumerator.
Таким образом, по сути, мы реализовали только пару методов:
//Возвращаем элемент Idx
public object this[int Idx];
//Возвращаем изображение для элемента Idx
public Image Img(int Idx);
//Список элементов
private List _Items;
//Добавление элемента
public void Add(object Obj);
public void Add(object Obj, Image Img);
//Удаление всех элементов
public void Clear();
Теперь вернемся к нашему основному классу контролла. Начальный макет класса я уже описывал в предыдущем посте. Далее будем только дополнять его.
Как уже было отмечено ранее, сам контролл рисуется с помощью класса Graphics. В случае создания GUI лучше не жидиться на функции, и описывать прорисовку нашего элемента, а точнее его составляющих в отдельных функциях.
За прорисовку элемента отвечает функция:
private void MyListBox_Paint(object sender, PaintEventArgs e);
Опишем следующим образом:
private void MyListBox_Paint(object sender, PaintEventArgs e) { this.PainItems(e.Graphics); } private void PainItems(Graphics g) { for (int i = 0; i < this.Items.Count; ++i) { int line = i * 20; //отступ от начала элемента сверху Rectangle Rest = new Rectangle(0, line, this.Width, 20); Pen P = new Pen(this.ItemBorderColor, this.ItemBorderWeight); bool Selected = i == this.SelectedIndex; Image I = this.Items.Img(i); if (!Selected) { g.FillRectangle(new SolidBrush(this.ItemBackgroundColor), Rest); g.DrawString(this.Items[i].ToString(), this.Font, new SolidBrush(this.ForeColor), new PointF(this.Padding.Left + (I == null ? 0 : this.Padding.Left + I.Width), line + this.Padding.Top - (this.Font.Size / 4))); } else { g.FillRectangle(new SolidBrush(this.SelectedItemBackgroundColor), Rest); g.DrawString(this.Items[i].ToString(), this.Font, new SolidBrush(this.SelectedItemColor), new PointF(this.Padding.Left + (I == null ? 0 : this.Padding.Left + I.Width), line + this.Padding.Top - (this.Font.Size / 4))); } //Изображение if (I != null) { g.DrawImage(I, new Point(this.Padding.Left, line + this.Padding.Top)); } //Рамка g.DrawRectangle(P, Rest); } }
В принципе и все. База есть. Если добавить контролл на форму и добавить несколько элементов, то мы уже увидим как наше творение выглядит наглядно. Однако функционала пока наш элемент не имеет, нужно это срочно исправить.
Добавляем следующий код в наш класс:
public delegate void _SelectedIndexChanged(); public event _SelectedIndexChanged SelectedIndexChanged;
Таким образом мы объявляем событие для контролла с именем SelectedIndexChanged. Назвать новое событие мы можем как угодно, однако будем придерживаться традиций.
Теперь вернемся назад и поговорим о контсрукции:
public int SelectedIndex { get; set; }
Так как эта переменная позволяет менять текущий выбранный элемент, то такого объявления будет не достаточно, так как необходимо перерисовывать наш контролл. Добавляем следующее:
public int SelectedIndex { get { return this._SelectedIndex; } set { this._SelectedIndex = value; if (-1 != value && this.SelectedIndexChanged != null) this.SelectedIndexChanged(); this.Refresh(); } }
Так же для удобства зададим стандартное событие (то что будет создаваться при двойном щелчке на контролл на форме проекта). Добавим перед объявлением нашего класса строку:
[DefaultEvent("SelectedIndexChanged")] public class MyListBox: UserControl { ... }
Ну вот и все. Теперь наш класс готов к труду и обороне. Не исключено что в данном примере есть ошибки или опечатки, так как в данной статье лишь выдержка из более сложного класса, реализующего элемент ListBox. Однако как и все что находится на данном ресурсе, это лишь пример и наводка для тех, кто хочет понять принципы создания собственных интерфейсов.