Опубликован: 13.07.2010 | Доступ: свободный | Студентов: 890 / 20 | Оценка: 4.40 / 4.20 | Длительность: 77:34:00
Самостоятельная работа 27:

Разработка комбинированного компонента

Тестирование компонента NumericScan

  • Поместите из панели Toolbox на форму Form3 два экземпляра компонента NumericScan и одну текстовую метку Label
  • Выделите одновременно оба объекта NumericScan и в панели Properties для их события ValueChanged, введенное нами при разработке компонента, зарегистрируйте общий обработчик с именем NumericScanOnValueChanged, который заполните так
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
    
namespace MyCompany.StudName
{
    public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
        }
    
        private void NumericScanOnValueChanged(object sender, EventArgs e)
        {
            label1.Text = "Первый: " + numericScan1.Value +
                "; Второй: " + numericScan2.Value;
        }
    }
}
Листинг 19.27. Общий обработчик события ValueChanged в клиенте Form3 компонента
  • Настройте в конструкторе класса Form3 экземпляры компонентов NumericScan
public Form3()
        {
            InitializeComponent();
    
            numericScan1.DecimalPlaces = 
                numericScan2.DecimalPlaces = 2;
            numericScan1.Increment = 
                numericScan2.Increment = 0.01M;
        }
Листинг 19.28. Настройки объектов NumericScan в конструкторе класса Form3
  • Запустите " Упражнение 3 " и испытайте работу кода. Поизменяйте значения текстового поля клавиатурой и мышью, а также попользуйтесь клавишей Tab для смены фокуса ввода, испытав работу перегруженного метода OnLeave().

Замечание. Текстовое поле, ожидающее клавиатурный ввод числа типа Decimal, символом разделителя дробной части считает запятую.

Внешний результат будет таким


Упражнение 4. Использование компонента NumericScan для управления текстом

В этом упражнении мы проиллюстрируем использование компонентов как классов с динамическим созданием их экземпляров прямо в коде, без использования графического редактора оболочки. Для автоматического размещения компонентов расширим библиотечный класс System.Windows.Forms.TableLayoutPanel, который представляет аналог невидимой таблицы для локализации элементов пользовательского интерфейса. Расширение опишет таблицу 6x2 (шесть строк - два столбца), левый столбец которой будет содержать текстовые метки, а правый - экземпляры компонентов NumericScan.

Экземпляр этого расширения будет представлять панель управления для искажения текста, который будет помещен в панели отображения. Искажение текста определяется свойством Transform контекста устройства, представленного объектом Graphics. Это свойство имеет тип Matrix из пространства имен System.Drawing.Drawing2D. Экземпляр класса Matrix перед рисованием текста мы будем создавать с параметрами, которые пользователь будет задавать в объектах NumericScan панели управления. Для обмена данными между элементами управления и матрицей преобразования предусмотрим в классе-расширении свойство Matrix.

Динамическое создание панели управления текстом

  • В панели Solution Explorer вызовите контекстное меню для узла Test и добавьте командой Add/Class новый файл с именем AuxForm4.cs
  • В панели Solution Explorer вызовите контекстное меню для папки ChildrenForms и командой Add/New Folder добавьте подпапку с именем AuxForms
  • В панели Solution Explorer переместите мышью файл AuxForm4.cs в подпапку AuxForms, чтобы "не мешалась под ногами"
  • Откройте на редактирование файл AuxForm4.cs и преобразуйте его так
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
    
namespace MyCompany.StudName
{
    // Панель размещения элементов управления текстом
    class MatrixPanel : TableLayoutPanel
    {
    }
}
    
namespace MyCompany.StudName
{
    // Панель отображения текста
    class DisplayPanel : Panel
    {
    }
}
Листинг 19.29. Заготовка файла для размещения вспомогательных классов
  • Добавьте в класс MatrixPanel конструктор класса, в котором заполняются ячейки унаследованной таблицы динамически созданными объектами, чтобы класс стал таким
// Панель размещения элементов управления текстом
    class MatrixPanel : TableLayoutPanel
    {
        // Заготовили массив ссылок для будущей адресации
        NumericScan[] numscan = new NumericScan[6];
    
        // Конструктор для наполнения таблицы размещения
        public MatrixPanel()
        {
            // Параметры таблицы
            this.AutoSize = true;   // Подстраиваться под содержимое
            this.Padding = new Padding(this.Font.Height);   // Внутренний отступ
            // дочерних элементов от ячеек таблицы
            this.ColumnCount = 2;   // Количество столбцов таблицы размещения
    
            this.SuspendLayout();   // Приостановить механизм размещения элементов таблицы
            // до завершения создания содержимого ячеек 
    
            // Заполнить ячейки элементами интерфейса слева-направо сверху вниз
            for (int i = 0; i < 6; i++)
            {
                // Создаем и настраиваем текстовые метки в левом столбце таблицы
                Label lbl = new Label();    // Создаем неадресуемый экземпляр
                lbl.Parent = this;      // Отдали таблице
                lbl.AutoSize = true;    // Подстраиваться под содержимое
                lbl.Anchor = AnchorStyles.Left; // Привязываем к ячейке слева
                // Нерациональный способ многократно создавать 
                // массив, а использовать только один элемент 
                lbl.Text = new string[]{
                    "X Scale:", "Y Shear:", "X Shear:",
                    "Y Scale:", "X Translate:", "Y Translate:"}[i];
    
                // Создаем и настраиваем экземпляры компонента NumericScan
                numscan[i] = new NumericScan(); // Создаем адресуемый экземпляр
                numscan[i].Parent = this;       // Отдали таблице
                numscan[i].AutoSize = true;     // Подстраиваться под содержимое
                numscan[i].Anchor = AnchorStyles.Right;// Привязываем к ячейке справа
                numscan[i].Minimum = -1000; // Нижняя граница диапазона
                numscan[i].Maximum = 1000;  // Верхняя граница диапазона
                numscan[i].DecimalPlaces = 2;   // Число значащих цифр после запятой
            }
    
            this.ResumeLayout();   // Включить механизм размещения элементов таблицы
        }
    }
Листинг 19.30. Конструктор класса панели управления MatrixPanel

Для искажения текста в панели представления DisplayPanel мы воспользуемся матрицей преобразования, представленной классом System.Drawing.Drawing2D.Matrix, которая имеет следующий вид


При создании экземпляра класса Matrix с помощью конструктора без параметров создается единичная матрица. Меняя состояние объектов NumericScan мы изменяем значения элементов этой матрицы и тем самым намерены влиять на параметры рисования текста.

  • Добавьте к классу MatrixPanel новое свойство с именем Matrix, которое будет представлено следующим кодом
// Свойство для взаимодействия экземпляров NumericScan и объекта Matrix 
        public System.Drawing.Drawing2D.Matrix Matrix
        {
            set
            {
                // Извлекаем элементы матрицы преобразования в интерфейсные элементы
                for (int i = 0; i < 6; i++)
                    numscan[i].Value = (decimal)value.Elements[i];
            }
            get
            {
                // Создаем матрицу преобразования по значениям интерфейсных элементов
                return new System.Drawing.Drawing2D.Matrix(
                    (float)numscan[0].Value,    // X Scale:
                    (float)numscan[1].Value,    // Y Shear:
                    (float)numscan[2].Value,    // X Shear:
                    (float)numscan[3].Value,    // Y Scale:
                    (float)numscan[4].Value,    // X Translate:
                    (float)numscan[5].Value);   // Y Translate:
            }
        }
Листинг 19.31. Добавление свойства Matrix к классу для связи интерфейса и матрицы преобразования
  • Добавьте в конец конструктора класса код начального заполнения интерфейсных элементов значениями единичной матрицы преобразования через свойство класса
// Конструктор для наполнения таблицы размещения
        public MatrixPanel()
        {
            .......................................................
    
            this.ResumeLayout();   // Включить механизм размещения элементов таблицы
    
            // Заполнение интерфейсных элементов единичной 
            // матрицей преобразования через свойство класса
            this.Matrix = new System.Drawing.Drawing2D.Matrix();
        }
Листинг 19.32. Начальное заполнение интерфейсных элементов в конструкторе класса

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

  • Добавьте в класс MatrixPanel объявление нового события с именем Changed и создайте метод его диспетчеризации OnChanged() - защищенный и виртуальный для переопределения в возможных потомках, если наследование продолжится
// Панель размещения элементов управления текстом
    class MatrixPanel : TableLayoutPanel
    {
        ......................................................
    
        // Объявляем событие изменения состояния интерфейсных элементов
        // по типовому библиотечному делегату EventHandler
        public event EventHandler Changed;
    
        // Метод диспетчеризации события Changed 
        protected virtual void OnChanged(EventArgs args)
        {
            // Проверяем наличие подписанных обработчиков и генерируем событие
            if (Changed != null)
                Changed(this, EventArgs.Empty);
        }
    }
Листинг 19.33. Добавление в класс MatrixPanel события и метода его диспетчеризации
  • В конструкторе класса MatrixPanel в код создания и настройки интерфейсных элементов NumericScan добавьте код регистрации общего обработчика. Код вводите вручную, чтобы оболочка создала обработчик с правильной сигнатурой, соответствующей делегату события
// Конструктор для наполнения таблицы размещения
        public MatrixPanel()
        {
            .......................................
    
            // Заполнить ячейки элементами интерфейса слева-направо сверху вниз
            for (int i = 0; i < 6; i++)
            {
                // Создаем и настраиваем текстовые метки в левом столбце таблицы
                ...............................................
    
                // Создаем и настраиваем экземпляры компонента NumericScan
                numscan[i] = new NumericScan(); // Создаем адресуемый экземпляр
                numscan[i].Parent = this;       // Отдали таблице
                numscan[i].AutoSize = true;     // Подстраиваться под содержимое
                numscan[i].Anchor = AnchorStyles.Right;// Привязываем к ячейке справа
                numscan[i].Minimum = -1000; // Нижняя граница диапазона
                numscan[i].Maximum = 1000;  // Верхняя граница диапазона
                numscan[i].DecimalPlaces = 2;   // Число значащих цифр после запятой
                // Регистрируем общий обработчик на изменения состояния объекта
                // НАБИРАТЬ ВРУЧНУЮ !!!!!!!
                numscan[i].ValueChanged += new EventHandler(MatrixPanel_ValueChanged);
            }
    
            this.ResumeLayout();   // Включить механизм размещения элементов таблицы
    
            // Заполнение интерфейсных элементов единичной 
            // матрицей преобразования через свойство класса
            this.Matrix = new System.Drawing.Drawing2D.Matrix();
        }
    
        void MatrixPanel_ValueChanged(object sender, EventArgs e)
        {
            throw new Exception("The method or operation is not implemented.");
        }
Листинг 19.34. Подписка общего обработчика на событие ValueChanged интерфейсных элементов
  • Замените код генерации исключения в теле заготовки обработчика на код вызова метода диспетчеризации события
void MatrixPanel_ValueChanged(object sender, EventArgs e)
        {
            OnChanged(EventArgs.Empty);
        }
Листинг 19.35. Код обработчика изменения состояния интерфейсных элементов NumericScan

На этом создание панели с интерфейсными элементами управления текстом завершено.