Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 36:

Интерфейс времени проектирования для компонента

Вывод дополнительной надписи на компоненте

Иногда может возникнуть необходимость пометить компонент в режиме проектирования надписью, которая в режиме выполнения не будет видна. Здесь нужно перегрузить метод OnPaintAdornments() класса дизайнера, присоединенного к компоненту, который работает только в режиме проектирования.

  • Добавьте в конец файла GradientLabelDesigner.cs блок кода №9 дизайнера, переопределяющий метод вывода дополнительной надписи на компоненте на этапе проектирования
//*///////////////////////////////////////////////////////////////
// Блок кода №9. Отрисовка компонента в режиме проектирования 
//////////////////////////////////////////////////////////////////
namespace MyControl
{
    partial class GradientLabelDesigner
    {
        protected override void OnPaintAdornments(PaintEventArgs pe)
        {
            base.OnPaintAdornments(pe);
    
            pe.Graphics.Clear(Color.White);// Стираем старое
    
            // Рисуем дополнительную информацию в поле компонента
            String text = "Мой компонент";
            pe.Graphics.DrawString(
                text,
                this.Control.Font,
                new SolidBrush(Color.Black),
                0.0F,
                0.0F
            );
        }
    }
}
//***************************************************************/
Листинг 36.19. Метод дизайнера для вывода дополнительной надписи
  • Откомпилируйте решение и убедитесь, что дополнительная надпись на компоненте работает только на этапе проектирования


Режим времени проектирования Режим времени выполнения

Здесь есть одно неудобство, которое нарушает преимущества визуального программирования. Пользователь нашего компонента не сможет представить, как будет выглядеть компонент во время выполнения, пока не запустит форму, где он используется. А в режиме проектирования он будет видеть только дополнительную надпись (или наложение надписей, если на выполнить очистку поля компонента). Поэтому желательно сделать так, чтобы дополнительная надпись разработчика компонента появлялась бы только при определенных условиях, например, при наведении на компонент курсора мыши.

Класс System.Windows.Forms.Design.ControlDesigner содержит ряд виртуальных методов, которые автоматически вызываются оболочкой при попадании мыши на компонент в режиме проектирования. В данном случае нам понадобятся только два метода, которые мы можем переопределить в классе-наследнике GradientLabelDesigner, а именно

  1. protected virtual void OnMouseEnter() - вызывается автоматически при попадании курсора мыши на компонент
  2. protected virtual void OnMouseLeave() - вызывается автоматически при уходе курсора мыши с компонента

Нам удобно ввести булев флаг mouseOver, который будет подниматься в методе OnMouseEnter() и опускаться в методе OnMouseLeave(). В этих же методах будет вызываться метод разрушения окна компонента, что будет инициировать выполнение метода перерисовки компонента OnPaintAdornments(). А в самом методе перерисовки код очистки поля компонента и вывода дополнительной надписи мы поставим на условие в зависимости от состояния флага mouseOver.

  • С учетом сказанного модифицируйте блок №9 файла GradientLabelDesigner.cs следующим образом
//*///////////////////////////////////////////////////////////////
// Блок кода №9. Отрисовка компонента в режиме проектирования 
//////////////////////////////////////////////////////////////////
namespace MyControl
{
    partial class GradientLabelDesigner
    {
        bool mouseOver = false;
    
        protected override void OnPaintAdornments(PaintEventArgs pe)
        {
            base.OnPaintAdornments(pe);
    
            if (mouseOver)
            {
                pe.Graphics.Clear(Color.White);// Стираем старое
    
                // Рисуем дополнительную информацию в поле компонента
                String text = "Мой компонент";
                pe.Graphics.DrawString(
                    text,
                    this.Control.Font,
                    new SolidBrush(Color.Black),
                    0.0F,
                    0.0F
                );
            }
        }
    
        // Мышь попала на компонент
        protected override void OnMouseEnter()
        {
            base.OnMouseEnter();
    
            mouseOver = true;
            this.Control.Invalidate();
        }
    
        // Мышь сошла с компонента
        protected override void OnMouseLeave()
        {
            base.OnMouseLeave();
    
            mouseOver = false;
            this.Control.Invalidate();
        }
    }
}
//***************************************************************/
Листинг 36.20. Код вывода дополнительной надписи при попадании на компонент курсора мыши

Управление действием по умолчанию графического редактора

Действие по умолчанию - это действие графического редактора (дизайнера) оболочки, выполняемое по двойному щелчку мыши на компоненте. Стандартным реагированием является создание обработчика события по умолчанию. Чтобы компоненту назначить событие по умолчанию, нужно прикрепить к классу этого компонента атрибут DefaultEvent с именем события в качестве аргумента.

  • Откройте файл Form1.cs в режиме View Designer и выполните двойной щелчок на помещенном на форму экземпляре компонента gradientLabel1. Удостоверьтесь, что графический редактор автоматически создал обработчик события Click объекта компонента gradientLabel1 с заготовкой
private void gradientLabel1_Click(object sender, EventArgs e)
        {
    
        }
  • Откатите действие графического редактора, выполнив команду Undo (комбинация клавиш Ctrl+Z )
  • В файле GradientLabel.cs добавьте в блок кода №2 перед объявлением класса GradientLabel атрибут, назначающий компоненту событие по умолчанию
/////////////////////////////////////////////////////////////////
// Блок кода №2 продолжения класса GradientLabel 
/////////////////////////////////////////////////////////////////
namespace MyControl
{
    // Всплывающая подсказка компонента в панели Toolbox
    [Description("Текстовая метка с градиентной заливкой фона")]
    // Подключение пиктограммы компонента для панели Toolbox 
    [ToolboxBitmap(typeof(GradientLabel))]
    // Подключение класса дизайнера к классу компонента       
    [Designer(typeof(GradientLabelDesigner))]
    // Событие по умолчанию
    [DefaultEvent("GradientChange")]
    
    partial class GradientLabel
    {
        // Закрытые поля
        private Color startColor = Color.Yellow;
        private Color endColor = Color.Red;
    
        // Категория свойства в панели Properties
        [Category("Gradient")]
        ...............................................................
    }
}
Листинг 36.22. Назначение события по умолчанию для компонента GradientLabel
  • Откомпилируйте решение с введенными изменениями в коде компонента
  • Вновь откройте файл Form1.cs в режиме View Designer и выполните двойной щелчок на помещенном на форму экземпляре компонента gradientLabel1. Удостоверьтесь, что графический редактор автоматически создал теперь уже обработчик события GradientChange объекта компонента gradientLabel1 с заготовкой
private void gradientLabel1_GradientChange(object sender, EventArgs e)
        {
    
        }

Событие по умолчанию можно отменить или как-то дополнить, если переопределить виртуальный метод класса дизайнера

public virtual void DoDefaultAction()

в котором не вызывать базовый метод base.DoDefaultAction() цепочки наследования.

  • Добавьте в конец файла GradientLabelDesigner.cs блок кода №10, перекрывающий виртуальный метод DoDefaultAction()
//*///////////////////////////////////////////////////////////////
// Блок кода №10. Перекрытие события по умолчанию
//////////////////////////////////////////////////////////////////
namespace MyControl
{
    partial class GradientLabelDesigner
    {
        public override void DoDefaultAction()
        {
            //base.DoDefaultAction();
            MessageBox.Show("Событие по умолчанию компонента перекрыто");
        }
    }
}
//***************************************************************/
Листинг 36.24. Перекрытие виртуального метода DoDefaultAction()
  • Откомпилируйте компонент и выполните двойной щелчок мыши на экземпляре компонента, помещенного на форму Form1

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


Прямая обработка очереди сообщений Windows

У базового класса System.Windows.Forms.Design.ControlDesigner имеется виртуальный метод

protected virtual void WndProc(ref System.Windows.Forms.Message m)

который в качестве параметра принимает ссылку на структуру System.Windows.Forms.Message, связанную с очередью сообщений Windows.

Метод WndProc() вызывается автоматически всякий раз, когда в цикл сообщений операционной системы попадает новое сообщение от внутреннего или внешнего источника. Испытаем работу этой техники на примере обработки левого щелчка мыши на компоненте. Будем отлавливать сам щелчок левой кнопки мыши, определять координаты курсора относительно формы, приводить эти координаты относительно компонента и проверять, выходят ли они за пределы компонента.

  • Добавьте в конец файла GradientLabelDesigner.cs блок кода №11, перекрывающий виртуальный метод WndProc() для прямой обработки сообщений Windows
//*///////////////////////////////////////////////////////////////
// Блок кода №11. Перекрытие метода WndProc()
// для прямой обработки сообщений Windows 
//////////////////////////////////////////////////////////////////
namespace MyControl
{
    partial class GradientLabelDesigner
    {
        const int WM_LBUTTONCLICK = 0x201;
    
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_LBUTTONCLICK)
            {
                // Нажата левая кнопка мыши, приводим координаты курсора к компоненту
                Point point = this.Control.PointToClient(Cursor.Position);
                if (point.X > 0 && point.X < this.Control.Width
                    && point.Y > 0 && point.Y < this.Control.Height)
                {
                    MessageBox.Show(String.Format("Попали в компонент\n"
                    + "с координатами {0}x{1}", point.X, point.Y));
                }
            }
    
            base.WndProc(ref m);
        }
    }
}
//***************************************************************/
Листинг 36.25. Перекрытие виртуального метода WndProc()
  • Откомпилируйте компонент GradientLabel и выполните щелчок левой кнопки мыши на его экземпляре, помещенном на форму Form1

Диалоговое окно сообщений свидетельствует о реакции дизайнера на щелчки мыши способом прямой обработки событий Windows.


Управление слоями и маркерами компонентов

 

Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ