Опубликован: 25.03.2010 | Доступ: свободный | Студентов: 1447 / 158 | Оценка: 4.31 / 4.00 | Длительность: 25:42:00
Лекция 17:

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

Комбинирование готовых элементов управления

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

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

using System;
using System.Drawing;
using System.Windows.Forms;
    
namespace Test
{
    //******************************************************
    
    // Кнопка, генерирующая множество событий Click
    // При нажатии и удержании на ней курсора мыши
    class ClickmaticButton : Button
    {
    // Определяем поля класса
    // Создаем таймер
    Timer timer = new Timer();
    // Задержка срабатывания кнопки Button после нажатия мыши
    int iDelay = 250 * (1 + SystemInformation.KeyboardDelay);
    // Скорость для срабатывания таймера, генерирующего события повторных
    // нажатий кнопки компонента при удержании нажатой левой кнопки мыши
    int iSpeed = 405 - 12 * SystemInformation.KeyboardSpeed;
    
    // Конструктор
    public ClickmaticButton()
    {
    timer.Tick += TimerOnTick;// Подписались на тики
    }
    
    // Перехватываем нажатие левой кнопки мыши и запускаем таймер
    // для генерации событий Click кнопки Button
    protected override void OnMouseDown(MouseEventArgs args)
    {
    base.OnMouseDown(args);
    
    // Если нажата левая кнопка мыши
    if ((args.Button & MouseButtons.Left) != 0)
    {
    timer.Interval = iDelay;// Задержка до первого тика
    timer.Start();// Включили таймер
    }
    }
    
    // Отработка тиков
    void TimerOnTick(object sender, EventArgs e)
    {
    // Программно генерируем повторное 
    // нажатие Button при каждом тике
    this.OnClick(EventArgs.Empty);
    // Меняем скорость таймера
    timer.Interval = iSpeed;
    }
    
    // Контролируем, находится ли нажатый курсор мыши
    // над кнопкой и управляем приостановкой таймера
    protected override void OnMouseMove(MouseEventArgs args)
    {
    base.OnMouseMove(args);
            
    // Приостанавливаем таймер
    timer.Enabled = this.Capture &&
      this.ClientRectangle.Contains(args.Location);
    }
    
    // При отпускании кнопки таймер останавливаем
    protected override void OnMouseUp(MouseEventArgs args)
    {
    base.OnMouseUp(args);
    
    timer.Stop();
    }
    }
    
    //******************************************************
    
    // Рисование на генерирующей событие Click кнопке 
    // изображения стрелки
    class ArrowButton : ClickmaticButton
    {
        // Поле для хранения ориентации кнопки
        // По умолчанию острый угол треугольника направлен вправо
        ScrollButton scrollButton = ScrollButton.Right;
    
        // Конструктор
        public ArrowButton()
        {
            // Задание стиля кнопки как не получающей фокус ввода
            this.SetStyle(ControlStyles.Selectable, false);
        }
    
        // Свойство для поддержки переменной перечисления
        public ScrollButton ScrollButton
        {
            get { return scrollButton; }
            set
            {
                scrollButton = value;
                this.Invalidate();// Перерисовать
            }
        }
    
        // Переопределяем метод отрисовки кнопки
        protected override void OnPaint(PaintEventArgs args)
        {
            Point point = PointToClient(Control.MousePosition);
            bool mouseInButton = this.Capture &&
                this.ClientRectangle.Contains(point);
            ButtonState state = mouseInButton ?
                ButtonState.Pushed : ButtonState.Normal;
    
            state = !this.Enabled ?
                ButtonState.Inactive : state;
    
            Graphics gr = args.Graphics;
            // Рисуем кнопку с треугольником внутри
            ControlPaint.DrawScrollButton(
                gr,
                this.ClientRectangle,
                scrollButton,
                state);
        }
    
        protected override void OnMouseCaptureChanged(EventArgs args)
        {
            base.OnMouseCaptureChanged(args);
    
            this.Invalidate();// Перерисовать
        }
    }
    
    //******************************************************
    
    // Самодельный элемент управления - поле с кнопками
    public class NumericScan : UserControl
    {
        // Объявили событие
        public event EventHandler ValueChanged;
    
        // Объявили ссылки-поля для видимости объектов
        TextBox txtBox;
        ArrowButton btn1, btn2;
    
        // Объявили базовые закрытые поля для открытых свойств
        int iDecimalPlaces = 0;
        decimal mValue = 0;
        decimal mIncrement = 1;
        decimal mMinimum = 0;
        decimal mMaximum = 100;
    
        // Доступные свойства-обертки полей
        public int DecimalPlaces
        {
            get { return iDecimalPlaces; }
            set { iDecimalPlaces = value; }
        }
        public decimal Value
        {
            get { return mValue; }
            set { txtBox.Text = ValueToText(mValue = value); }
        }
        public decimal Increment
        {
            get { return mIncrement; }
            set { mIncrement = value; }
        }
        public decimal Minimum
        {
            get { return mMinimum; }
            set
            {
                // Заодно отслеживаем диапазон Value
                if (Value < (mMinimum = value))
                    Value = mMinimum;
            }
        }
        public decimal Maximum
        {
            get { return mMaximum; }
            set
            {
                // Заодно отслеживаем диапазон Value
                if (Value > (mMaximum = value))
                    Value = mMaximum;
            }
        }
    
        // Конструктор
        public NumericScan()
        {
        txtBox = new TextBox();
        txtBox.Parent = this;
        // Текст у правой границы поля
        txtBox.TextAlign = HorizontalAlignment.Right;
        txtBox.Text = ValueToText(mValue);// Инициализируем
        txtBox.TextChanged += TextBoxOnTextChanged;
        // При вводе Enter обновить Value элемента
        txtBox.KeyDown += TextBoxOnKeyDown;
        // Не принимать лишние символы
        txtBox.KeyPress += new KeyPressEventHandler(txtBox_KeyPress);
    
        btn1 = new ArrowButton();
        btn1.Parent = this;
        // Ориентация треугольничка влево
        btn1.ScrollButton = ScrollButton.Left;
        btn1.Click += ButtonOnClick;
    
        btn2 = new ArrowButton();
        btn2.Parent = this;
        // Ориентация треугольничка вправо
        btn2.ScrollButton = ScrollButton.Right;// По умолчанию
        btn2.Click += ButtonOnClick;
        }
    
        string ValueToText(decimal mValue)
        {
        // Возвращаем с форматированием
        return mValue.ToString("F" + iDecimalPlaces);
        }
    
        // Обработчик события изменения содержимого текстового поля
        void TextBoxOnTextChanged(object sender, EventArgs args)
        {
        try
        {
        // Преобразуем текст в число
        mValue = Decimal.Parse(txtBox.Text);
        }
        catch { }// На исключения не реагируем
        }
    
        // Обрабатывает каждую клавишу, но ждет клавишу Enter
    
       void TextBoxOnKeyDown(object sender, KeyEventArgs args)
        {
        switch (args.KeyCode)
        {
        // При нажатии Enter генерируем наше событие ValueChanged 
        case Keys.Enter:
          OnValueChanged(EventArgs.Empty);
        break;
        }
    
      // Обнаруживаем цифру
      isNumeric =
        // Основная клавиатура
        args.KeyCode >= Keys.D0 && args.KeyCode <= Keys.D9
        // Дополнительная клавиатура
        || args.KeyCode >= Keys.NumPad0 && 
  args.KeyCode <= Keys.NumPad9
        // Клавиша BackSpace
        || args.KeyCode == Keys.Back;
      }
    
      // Отфильтровываем лишние символы из поля ввода
      bool isNumeric;// Видимое поле
      void txtBox_KeyPress(object sender, KeyPressEventArgs args)
      {
      if (isNumeric != true)// Пропускаем только цифры
        args.Handled = true;// Не принимаем в TextBox
      }
    
      // Срабатывает автоматически и устанавливает
      // размеры компонента this.Width, this.Height
      public override Size GetPreferredSize(Size proposedSize)
      {
      const float weight = 1.2f;// Вес ширины
      // Высота компонента
      int height = txtBox.PreferredHeight +
        SystemInformation.HorizontalScrollBarHeight;
      // Устанавливаем размеры компонента
      return new Size((int)(weight * height), height);
      }
    
      // Срабатывает автоматически, позиционирует
      // кнопки и устанавливает их размеры
      protected override void OnResize(EventArgs args)
      {
      base.OnResize(args);
    
      txtBox.Height = txtBox.PreferredHeight;
      txtBox.Width = this.Width;// Текстовое поле по ширине компонента
      btn1.Location = new Point(0, txtBox.Height);// Слева снизу
      btn2.Location = new Point
    (this.Width / 2, txtBox.Height);// Середина низ
      // Приравниваем значения полей структур
      btn1.Size = btn2.Size = new Size(txtBox.Width / 2, 
      this.Height - txtBox.Height);
      }
        
      // Когда элемент управления уходит из фокуса ввода, надо
      // проверить введенное и сгенерировать событие ValueChanged
      protected override void OnLeave(EventArgs args)
      {
      base.OnLeave(args);
      this.OnValueChanged(EventArgs.Empty);// Аргумент не используется
      }
    
      // Изменения величины пользователем по щелчкам мыши
      void ButtonOnClick(object sender, EventArgs e)
      {
      // Если не было, то дать фокус тексовому полю
      if (!txtBox.Focused)
        txtBox.Focus();
     
      // Повышаем полномочия ссылки
      ArrowButton btn = sender as ArrowButton;
      // Пробная величина для изменения значения на шаге
      decimal mNewValue = Value;
    
      // Выявляем кнопку и проверяем выход за границу
      if (btn == btn1)
        if ((mNewValue -= Increment) < Minimum)
          return;
    
      if (btn == btn2)
        if ((mNewValue += Increment) > Maximum)
          return;
    
      // Принимаем изменение и вызываем обработчик,
      // который сгенерирует событие ValueChanged
      Value = mNewValue;
      OnValueChanged(EventArgs.Empty);
      }
    
      // Ввели свою дополнительную функцию
      // Изменение величины прямым вводом в текстовое поле
      protected virtual void OnValueChanged(EventArgs args)
      {
      // Последовательно проверяем выход величины за левую и правую
      // границы, и если выходит за границу, то обрезаем по границе
      Value = Math.Min(Maximum, Value);
      Value = Math.Max(Minimum, Value);
      //Value = Decimal.Round(Value, DecimalPlaces);
    
      // Генерируем событие
      if (ValueChanged != null)
        ValueChanged(this, args);
      }
    }
    
    //******************************************************
    
    // Тест для проверки нашего элемента управления
    class MyClass : Form
    {
    // Внутренние поля для видимости в методах
    Label lbl1, lbl2;// Заполняются обработчиком события ValueChanged
    NumericScan numscan1, numscan2;// Ссылки на компоненты
    
    public MyClass()
    {
    this.Text = "Тест пользовательского компонента";
    // Размеры формы
    this.Width = (int)(1.3f * this.Width);
    this.Height = (int)(.8f * this.Height);
    
    FlowLayoutPanel flow = new FlowLayoutPanel();
    flow.Parent = this;// Привязали к форме
    // Размещение по столбцам
    flow.FlowDirection = FlowDirection.TopDown;
    // Внутреннее обрамление в пикселах
    flow.Padding = new Padding(20);
    // Растянули по форме
    flow.Dock = DockStyle.Fill;
    
    numscan1 = new NumericScan();
    numscan1.Parent = flow;
    numscan1.AutoSize = true;
    // Элементы между собой выравнивать по центру
    numscan1.Anchor = AnchorStyles.None;
    numscan1.ValueChanged += NumericScanOnValueChanged;
    
    lbl1 = new Label();
    lbl1.Parent = flow;
    lbl1.AutoSize = true;
    // Элементы между собой выравнивать по центру
    lbl1.Anchor = AnchorStyles.None;
    
    // Пустая текстовая метка для отступа элементов
    (new Label()).Parent = flow;
    
    numscan2 = new NumericScan();
    numscan2.Parent = flow;
    numscan2.AutoSize = true;
    // Элементы между собой выравнивать по центру
    numscan2.Anchor = AnchorStyles.None;
    numscan2.ValueChanged += NumericScanOnValueChanged;
    
    lbl2 = new Label();
    lbl2.Parent = flow;
    lbl2.AutoSize = true;
    // Элементы между собой выравнивать по центру
    lbl2.Anchor = AnchorStyles.None;
    
    // Сами принудительно выполняем обработчик
    // для начальной инициализации текстовых меток
    NumericScanOnValueChanged(numscan1, EventArgs.Empty);
    NumericScanOnValueChanged(numscan2, EventArgs.Empty);
    }
    
    void NumericScanOnValueChanged(object sender, EventArgs args)
        {
            NumericScan ob = sender as NumericScan;// Приводим ссылку
            if (ob == numscan1)
                lbl1.Text = "Первый: " + numscan1.Value;
            else if (ob == numscan2)
                lbl2.Text = "Второй: " + numscan2.Value;
        }
    }
    
    //******************************************************
    
    // Запуск
    class Program
    {
        static void Main()
        {
            Application.EnableVisualStyles();
            // Создали форму и запустили цикл сообщений Windows
            Application.Run(new MyClass());
        }
    }
}
Листинг 17.13 . Пример разработки пользовательского элемента управления
Максим Филатов
Максим Филатов

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

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

 

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