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

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

Аннотация: В данной лабораторной работе приведены упражнения по созданию собственных пользовательских комопонентов. Добавление объектов всплывающей подсказки. Таблица цветов структуры Color. Разработка кнопки ClickmaticButton. Наполнение класса ClickmaticButton функциональностью генерации щелчков мыши. Тестирование генерирующей кнопки ClickmaticButton. Разработка кнопки в стиле полос прокрутки. Тестирование скроллирующей кнопки ArrowButton. Комбинирование элементов в единый компонент NumericScan. Использование компонента NumericScan для управления текстом. Динамическое создание панели управления текстом. Динамическое создание панели представления текста.

Файлы к лабораторной работе Вы можете скачать здесь.

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

В данной работе мы продолжим выполнять упражнения по созданию собственных пользовательских компонентов, обладающих необходимыми нам свойствами. Мы создадим решение ( MySolution ), которое будет содержать проект DLL -библиотеки создаваемых компонентов ( UserControls ) и проект необходимых тестов ( Test ). Для удобства презентации выполненной работы создадим стартовую форму ( Start ) с кнопками-ссылками ( LinkLabel ), за каждой из которых закрепим вызов теста соответствующего упражнения.

В упражнениях мы последовательно разработаем свой (пользовательский) элемент управления на основе нескольких библиотечных элементов путем их комбинирования. Применим прием многоэтапного последовательного расширения классов на основе наследования. Вначале создадим из библиотечного класса Button класс расширения кнопки ClickmaticButton такой, чтобы в нажатом состоянии кнопка непрерывно генерировала событие Click с заданным интервалом. Затем вновь последовательно расширим класс ClickmaticButton классом ArrowButton, в котором на кнопке нарисуем стрелку. И наконец, скомбинируем два экземпляра созданной кнопки-стрелки и экземпляр библиотечного элемента TextBox в единый пользовательский компонент NumericScan по аналогии с библиотечным компонентом NumericUpDown - поле со счетчиком.

Разработка стартовой формы

Это небольшое приложение, которое обеспечит удобный способ демонстрации результатов всех других упражнений работы. Попутно решим проблему, которая состоит в предотвращении повторного запуска уже запущенной формы. Мы эту проблему уже решали в лабораторной работе №25 с использование коллекции ArrayList, которую сами создавали и поддерживали. Здесь мы применим уже готовую коллекцию запущенных форм приложения System.Windows.Forms.FormCollection.

  • Выбрав удобное место размещения, создайте новое решение командой File/New/Project и заполните окно мастера в соответствии с рисунком

Мастер оболочки создаст каталог MySolution для размещения многих проектов и добавит в него подкаталог Test с заготовкой проекта исполнимой сборки тестирования наших будущих компонентов.

  • В панели Solution Explorer вызовите контекстное меню для узла Test проекта, выполните команду Properties и настройте на вкладке Application текстовые поля Assembly name и Default namespace в соответствии с рисунком, где на на место второй составляющей имени MyCompany. StudName подставьте свою фамилию (можно кириллицей)

Тем самым мы указали оболочке имя исполнимой сборки и пространство имен, которое будет автоматически генерироваться при добавлении к проекту каждого нового элементы. Теперь, чтобы задействовать новые настройки оболочки, мы удалим старый файл Form1.cs и создадим его заново.

  • Откройте на редактирование файл Program.cs и замените в нем пространство имен Test на MyCompany. StudName
  • В панели Solution Explorer удалите файл Form1.cs, в котором используется прежнее пространство имен Test, и на его место командой Project/Add Windows Form вновь добавьте файл с прежним именем Form1.cs

Последнее действие мы выполнили для того, чтобы вручную не исправлять пространство имен в двух местах: Form1.cs и Form1.Designer.cs. Мы еще не написали ни строчки кода в этих файлах, поэтому нам не жалко их быстро перегенерировать, но уже с другим пространством имен.

  • В панели Solution Explorer переименуйте файл Form1.cs в Start.cs и откройте его в режиме View Designer

Сейчас мы займемся формированием стартовой формы для запуска всех будущих упражнений этой работы с использованием коллекции System.Windows.Forms.FormCollection. Вначале мы просто добавим к проекту 3 формы, чтобы испытать работоспособность подхода, а впоследствии мы их скорректируем в соответствии с выполненными упражнениями.

  • Добавьте к проекту 3 формы командой Project/Add Windows Form с именами Form1, Form2 и Form3
  • В панели Solution Explorer вызовите для корневого узла проекта Test контекстное меню и командой Add/New Folder добавьте новую папку с именем ChildrenForms
  • Удерживая клавишу Ctrl, выделите файлы Form1.cs, Form2.cs, Form3.cs и переместите их мышью в папку ChildrenForms

Мы пытаемся группировать файлы проекта по категориям решаемых задач. Но если бы мы вначале создали папку, а потом стали генерировать в нее новые формы, то оболочка упаковала бы их в пространство имен MyCompany.StudName.ChildrenForms. Это привело бы к тому, что классы дочерних форм оказались бы в другом пространстве имен и мы вынуждены были бы явно подключить его в файле Start.cs инструкцией using для видимости компилятору.

  • Откройте файл Start.cs в режиме View Designer и поместите на форму 3 компонента LinkLabel из панели Toolbox
  • Расположите компоненты LinkLabel на форме и выполните настройки в соответствии с таблицей свойств
Таблица свойств формы Start
Элемент Свойство Значение
Form Name По умолчанию
  Text Lab47. Студент Name
  StartPosition CenterScreen
  FormBorderStyle FixedDialog
LinkLabel Name Form1
  Tag Form1
  Text Упражнение 1. ClickmaticButton
LinkLabel Name Form2
  Tag Form2
  Text Упражнение 2. ArrowButton
LinkLabel Name Form3
  Tag Form3
  Text Упражнение 3. NumericScan

Интерфейс главной формы должен стать примерно таким


Свойство Tag любых компонентов, в том числе LinkLabel, служит для простого сохранения объектов произвольного типа, поскольку имеет тип Object. Мы разместили в нем метки имени формы для распознавания в общем обработчике щелчка на объекте LinkLabel.

  • Откройте файл Start.cs в режиме View Code и добавьте в код класса поле объявления ссылки типа Form
namespace MyCompany.StudName
{
    public partial class Start : Form
    {
        public Start()
        {
            InitializeComponent();
        }
    
        Form frm;
    }
}
Листинг 19.1. Поле объявления ссылки типа Form в классе Start
  • Откройте файл Start.cs в режиме View Designer, выделите на форме одновременно все экземпляры LinkLabel и создайте через панель Properties для их события LinkClicked общий обработчик с именем OnLinkClicked
  • Заполните обработчик OnLinkClicked следующим кодом
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 Start : Form
    {
        public Start()
        {
            InitializeComponent();
        }
    
        Form frm;
    
        private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            // Повышаем полномочия ссылки и извлекаем закрепленное имя формы
            string formName = (string)((LinkLabel)sender).Tag;
    
            // Читаем коллекцию запущенных форм
            FormCollection fc = Application.OpenForms;
            bool IsRun = false;
            // Перебираем коллекцию
            foreach (Form form in fc)
            {
                // Имя очередной запущенной формы сравниваем с закрепленным за кнопкой
                if (form.Name == formName)
                {
                    IsRun = true;   // Нашли, что форма запущена, поднимаем флаг
                    frm = form;     // Сохраняем ссылку на запущенную форму для фокуса
                    break;          // Выходим из цикла
                }
            }
    
            // Если форма не запущена - запускаем
            if (!IsRun)
            {
                switch (formName)
                {
                    case "Form1":
                        frm = new Form1();
                        break;
                    case "Form2":
                        frm = new Form2();
                        break;
                    case "Form3":
                        frm = new Form3();
                        break;
                }
    
                this.AddOwnedForm(frm);     // Сделать новую frm подчиненной Start
                // frm.Owner = this;        // Алтернативный способ назначить владельца
                frm.ShowInTaskbar = false;  // Не отображать метку окна в панели задач
                frm.Show();                 // Показать новую форму
            }
            else
                frm.Focus();                // Передать фокус запущенной форме
        }
    }
}
Листинг 19.2. Обработчик OnLinkClicked в файле Start.cs

Инструкция добавления подчиненной формы

this.AddOwnedForm(frm);

назначает для созданного экземпляра frm в качестве владельца текущую форму (в нашем случае - стартовую). Это означает, что подчиненное окно будет всегда размещаться поверх владельца, принудительно закрываться и разворачиваться тогда, когда это делает владелец. Таким поведением обладают, например, окна поиска и замены, или дочерние окна в многодокументных интерфейсах ( MDI ).

Если известна ссылка frm на подчиненную форму, то освободить ее позволяет метод владельца RemoveOwnedForm(frm). Альтернативным способом назначить или сменить владельца у подчиненной формы можно через ее свойство Owner, а для освобождения от владельца нужно этому свойству присвоить нулевую ссылку Owner=null.

  • Запустите приложение и убедитесь, что на данном этапе наш механизм предотвращения повторного запуска форм не работает!!!

Дело в том, что имена форм по умолчанию в коллекции считаются пустыми до тех пор, пока явно не прописаны в теле класса формы. Оболочка сделает это явно в файле *.Designer.cs только после первого помещения на форму какого-либо компонента. Только тогда она посчитает, что может возникнуть необходимость их различать, и явно присвоит имя самой форме. Заставим оболочку явно прописать имена наших форм.

  • Поместите на каждую форму в каталоге ChildrenForms любой компонент пользовательского интерфейса (например, Button ) и сразу же его удалите
  • Опять запустите приложение и убедитесь, что все заработало правильно
Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000