Опубликован: 25.03.2010 | Уровень: для всех | Доступ: платный
Лекция 17:

Пользовательские элементы управления

Задание круглой формы для кнопки
using System;
using System.Drawing;
using System.Windows.Forms;
    
using System.Drawing.Drawing2D;
    
namespace Test
{
    // Класс круглой кнопки
    class CircleButton : Button
    {
    public CircleButton()
    {
    // Сами отрисуем вместо операционной системы
    this.SetStyle(ControlStyles.UserPaint, true);
    // Отключить от системы автоматическую отрисовку
    this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    // Наружный отступ по контуру кнопки
    this.Margin = new Padding(this.Font.Height);
    }
    
    // Возвращается предпочтимый размер кнопки в зависимости от текста
    public override Size GetPreferredSize(Size proposedSize)
    {
    // Создаем временный контекст устройства
    Graphics gr = CreateGraphics();
    // Вычисляем размер текстового блока надписи
    SizeF sizeF = gr.MeasureString(this.Text, this.Font);
    // Освобождаем контекст
    gr.Dispose();
    // Теорема Пифагора
    int radius = (int)Math.Sqrt(
      Math.Pow(sizeF.Width / 2, 2)
      + Math.Pow(sizeF.Height / 2, 2));
    
    // Возвращаем диаметр
    return new Size(2 * radius, 2 * radius);
    }
    
    // Вычисление контура кнопки при изменении ее размеров
    protected override void OnResize(EventArgs args)
    {
    // Рисуем кнопку круглой, используя класс Region
    GraphicsPath path = new GraphicsPath();// Объект для региона
    path.AddEllipse(this.ClientRectangle);// Преобразовать в элипс
    this.Region = new Region(path);
    
    // Вызываем предка для инициации OnPaint()
    base.OnResize(args);
    }
    
    // Отрисовка круглой кнопки
    protected override void OnPaint(PaintEventArgs args)
    {
    Graphics gr = args.Graphics;// Адресовались к контексту
    // Качество рисования - сглаживать контуры
    gr.SmoothingMode = SmoothingMode.AntiAlias;
    Rectangle rect = this.ClientRectangle;
    
    // Флаг попадания курсора внутрь кнопки при нажатии
    bool bPressed = this.Capture &
      ((Control.MouseButtons & MouseButtons.Left) != 0) &
      this.ClientRectangle.
      Contains(this.PointToClient(Control.MousePosition));
    
    // Отрисовка внутренней части кнопки
    // При нажатии она становится темнее
    GraphicsPath path = new GraphicsPath();
    path.AddEllipse(rect);
    PathGradientBrush gradient = new PathGradientBrush(path);
    int k = bPressed ? 2 : 1;// Для нажатой увеличим вес градиента
    gradient.CenterPoint = new PointF(
      k * (rect.Left + rect.Right) / 3,
      k * (rect.Top + rect.Bottom) / 3);
    gradient.CenterColor = bPressed ? Color.Blue : Color.White;
    gradient.SurroundColors = new Color[] { Color.SkyBlue };
    gr.FillRectangle(gradient, rect);
    
    // Отрисовка контура кнопки, утолщенного для активных кнопок
    Brush brush = new LinearGradientBrush(
      rect,
      Color.FromArgb(0, 0, 255),
      Color.FromArgb(0, 0, 128),
      LinearGradientMode.ForwardDiagonal);
    Pen pen = new Pen(
      brush,
      (this.IsDefault ? 6 : 2) * gr.DpiX / 72);
      gr.DrawEllipse(pen, rect);
    
    // Отображение надписи в центре прямоугольника кнопки
    // Для неактивной кнопки цвет надписи должен быть бледно-серым
    StringFormat strFormat = new StringFormat();
    // Определение точки привязки в центре текстового блока
    strFormat.Alignment = 
      strFormat.LineAlignment = 
      StringAlignment.Center;
    brush = this.Enabled ? SystemBrushes.WindowText // Для активной
      : SystemBrushes.GrayText;// Для неактивной
    gr.DrawString(this.Text, this.Font, brush, rect, strFormat);
    
    // Отображение пунктира вокруг текста, если кнопка имеет фокус
    if (this.Focused)
      {
      SizeF sizeF = gr.MeasureString(
        this.Text, 
        this.Font,
        PointF.Empty, 
        StringFormat.GenericTypographic);
      pen = new Pen(this.ForeColor);// Текущий цвет
      pen.DashStyle = DashStyle.Dash;// Пунктир
      gr.DrawRectangle(
        pen,
        rect.Left + rect.Width / 2 - sizeF.Width / 2,
        rect.Top + rect.Height / 2 - sizeF.Height / 2,
        sizeF.Width,
        sizeF.Height);
        }
    
      if (this.Paint != null)// Если есть хоть одна подписка
        this.Paint(this, args);// Инициируем событие
      }
    
    new public event PaintEventHandler Paint;// Объявляем новое событие
    }
       
    // Форма приложения
    class MyClass : Form
    {
    public MyClass()
      {
      this.Text = "Круглые кнопки";
      this.Font = new Font("Times New Roman", 18);
      this.MaximizeBox = false;
      // Подстраиваться под кнопки
      this.AutoSize = true;
      this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
    
      // Панель для размещения элементов интерфейса в столбец
      FlowLayoutPanel flow = new FlowLayoutPanel();
      flow.Parent = this;
      flow.AutoSize = true;
      flow.FlowDirection = FlowDirection.TopDown;// В столбец
    
      // Встроенная компоновочная панель для верха
      FlowLayoutPanel flowTop = new FlowLayoutPanel();
      flowTop.Parent = flow;
      flowTop.FlowDirection = FlowDirection.LeftToRight;// По умолчанию
      flowTop.AutoSize = true; 
      // Выравнивание в столбец по центру родительской панели
      flowTop.Anchor = AnchorStyles.None; 
    
      // Помещаем в верхнюю компоновочную панель метку
      Label lbl = new Label();
      lbl.Parent = flowTop;
      lbl.AutoSize = true;
      lbl.Text = "Введите текст: ";
      lbl.Anchor = AnchorStyles.None;// Выравнивание по центру строки
    
      // Помещаем в верхнюю компоновочную панель поле ввода
      TextBox txtBox = new TextBox();
      txtBox.Parent = flowTop;
      txtBox.Width = 8 * this.Font.Height;// Длина поля ввода
      txtBox.TabStop = false;// Отключить клавишу Tab
    
      // Встроенная компоновочная панель для низа
      FlowLayoutPanel flowBottom = new FlowLayoutPanel();
      flowBottom.Parent = flow;
      flowBottom.FlowDirection = 
    FlowDirection.LeftToRight;// По умолчанию
      flowBottom.AutoSize = true;
      // Выравнивание в столбец по центру родительской панели
      flowBottom.Anchor = AnchorStyles.None;
    
      // Помещаем в нижнюю компоновочную панель кнопки
      CircleButton btn = new CircleButton();
      btn.Parent = flowBottom;
      btn.AutoSize = true;// Подстраиваемая
      btn.Text = "Кнопка 1";
      btn.Anchor = AnchorStyles.None;// Выравнивать по горизонтали
      btn.Click += btn_Click;
    
      btn = new CircleButton();
      btn.Parent = flowBottom;
      btn.AutoSize = true;// Подстраиваемая
      btn.Text = "Кнопка 2";
      btn.Anchor = AnchorStyles.None;// Выравнивать по горизонтали
      btn.Click += btn_Click;
    
      btn = new CircleButton();
      btn.Parent = flowBottom;
      btn.AutoSize = true;// Подстраиваемая
      btn.Text = "Кнопка 3";
      btn.Anchor = AnchorStyles.None;// Выравнивать по горизонтали
      btn.Click += btn_Click;
      }
    
        void btn_Click(object sender, EventArgs e)
        {
            //this.Close();// Завершить приложение
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            Application.EnableVisualStyles();
            // Создали форму и запустили цикл сообщений Windows
            Application.Run(new MyClass());
        }
    }
}
Листинг 17.12 . Класс самодельных круглых кнопок

Результат выполнения будет таким


Поскольку в классе расширения кнопки мы переопределили виртуальный метод OnPaint() и не предусмотрели его вызов для базового класса, то событие Paint никогда инициировано не будет. Потому что оно вызывается в методе OnPaint() именно базового класса, который мы перехватили. Чтобы не потерять событие Paint, мы его заново объявили в классе расширения, а в переопределенный метод OnPaint() вставили инициализацию этого события с предварительной проверкой, подписано ли оно в вызывающем коде.

Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

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

 

Как активировать код?

Денис Пашков
Денис Пашков
Россия
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974