C#: Собственный контрол. Часть 2 из 2.

Продолжение темы о создании собственного элемента управления с нуля.

В прошлый раз мы остановились на создании класса, отвечающего за набор записей в нашем элементе управления. Прокомментируем некоторые куски кода.

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. Однако как и все что находится на данном ресурсе, это лишь пример и наводка для тех, кто хочет понять принципы создания собственных интерфейсов.

Запись опубликована в рубрике C# с метками , , , , , . Добавьте в закладки постоянную ссылку.