Нестандартные формы и стандартные диалоги
Упражнение 1. Создание непрямоугольной формы
Файлы к лабораторной работе Вы можете скачать здесь.
Изначально все формы отображаются прямоугольными. И все к этому привыкли. Нельзя пытаться удивлять пользователя экзотическими элементами программы. Ему некогда тратить время на привыкание, программа должна согласовываться с его предыдущим опытом и должна быть интуитивно понятной.
Но мы, как проектировщики, должны знать о существовании возможности создания окон или элементов управления и в нетрадиционном стиле. При создании непрямоугольных форм и элементов управления используется свойство Region базового класса System.Windows.Forms.Control всех визуальных компонентов, которое ожидает ссылки на объекты-экземпляры класса System.Drawing.Region.
- Командой File/New/Project создайте новый проект FormsApp
- Через панель Solution Explorer откройте файл Program.cs и скопируйте из него в класс Form1 точку входа для индивидуального запуска новой формы
Теперь класс Form1 файла Form1.cs должен стать таким
public partial class Form1 : Form { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } public Form1() { InitializeComponent(); } }Листинг 18.1. Точка входа в классе Form1 файла Form1.cs
- Выполните команду Project/FormsApp Properties и установите в параметре Startup object вкладки Application форму Form1 в качестве стартовой
- Через панель Solution Explorer удалите файл Program.cs
- Откройте файл Form1.cs в режиме View Designer, выделите форму щелчком по ее заголовку и через панель Properties установите размеры формы в свойстве
Size=450;200
- Запустите приложение и убедитесь, что новая форма появляется сразу
На этом подготовительная часть к выполнению упражнения завершена.
В библиотеке .NET Framework (FCL - Framework Class Library) существует класс System.Drawing.Drawing2D.GraphicsPath, который можно использовать как коллекцию для заблаговременного сохранения различных графических примитивов, которые затем можно нарисовать все "одним махом", если передать эту коллекцию в функцию
System.Drawing.Graphics.DrawPath(System.Drawing.Pen pen, System.Drawing.Drawing2D.GraphicsPath path).
Особенность такого способа рисования состоит в том, что незамкнутые контуры всех входящих в коллекцию фигур автоматически замыкаются функцией рисования. И это позволяет легко заливать сложные замкнутые фигуры функцией
System.Drawing.Graphics.FillPath(System.Drawing.Brush brush, System.Drawing.Drawing2D.GraphicsPath path)
Давайте продемонстрируем упомянутые возможности, нарисовав на новой форме головку Буратино. Код рисования поместим в отдельную функцию, а вызов этой функции разместим в переопределении метода OnPaint() формы, чтобы при каждом разрушении окна формы (перемещение, изменение размеров) прорисовка изображения повторялась.
- Подключите в начале файла Form1.cs пространство имен System.Drawing.Drawing2D для сокращенной идентификации класса GraphicsPath
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Drawing.Drawing2D;Листинг 18.2. Добавление к файлу Form1.cs пространства имен
- Добавьте к классу Form1 новый метод с именем DrawBuratino(), который заполните так
// Рисование головки Буратино private void DrawBuratino() { GraphicsPath path = new GraphicsPath(); path.AddArc(20, 20, 120, 120, 0, 330); // Голова без носа path.AddArc(220, 20, 10, 10, 270, 180); // Удаленный кончик носа path.CloseFigure(); // Замкнули незаконченные дуги path.AddEllipse(90, 40, 20, 20); // Добавили глаз Graphics gr = this.CreateGraphics(); // Создаем холст gr.DrawPath(Pens.Red, path); // Рисуем контур Буратино gr.TranslateTransform(200, 0); // Смещаемся вправо gr.FillPath(Brushes.Blue, path); // Рисуем заливку Буратино gr.DrawPath(Pens.Red, path); // Рисуем контур для заливки }Листинг 18.3. Метод DrawBuratino() в классе Form1
- Переопределите метод OnPaint() формы, который наследуется ею от класса Control
Код начинайте вводить вручную с ключевого слова override. Когда вы его введете и нажмете клавишу пробела, подсказчик кода ( суфлер ) выдаст список переопределяемых методов формы, в котором можно быстро позиционироваться, продолжая набирать имя переопределяемого метода в любом регистре. Нажатие клавиши ввода завершит создание правильной заготовки переопределяемого метода.
// Переопределяем метод перерисовки формы protected override void OnPaint(PaintEventArgs e) { DrawBuratino(); base.OnPaint(e); }Листинг 18.4. Переопределеный метод OnPaint() перерисовки формы
- Запустите приложение и должен получиться такой результат
Теперь сделаем форму непрямоугольной. Для этого необходимо воспользоваться свойством Region формы, определяющим ее силуэт.
- Добавьте к классу Form1 новую функцию с именем DrawForm()
// Рисование непрямоугольной формы private void DrawForm() { GraphicsPath path = new GraphicsPath(); path.AddEllipse(0, -300, this.Width, 600); this.Region = new Region(path); }Листинг 18.5. Функция задания контура формы через свойство Region
В функции
public void AddEllipse (int x, int y, int width, int height)
первые два параметра определяют координаты левого верхнего угла прямоугольника, описывающего эллипс. При этом началом координат считается левый верхний угол клиентской области формы. Последние два параметра определяют размеры описывающего прямоугольника.
- Добавьте в функции OnPaint() вызов этой новой функции DrawForm()
// Переопределяем метод перерисовки формы protected override void OnPaint(PaintEventArgs e) { DrawBuratino(); DrawForm(); base.OnPaint(e); }Листинг 18.6. Вызов функции рисования непрямоугольной формы
- Перейдите в режим View Designer формы, выделите форму щелчком по ее заголовку и через панель Properties в режиме Events создайте обработчик для события Resize, в который поместите требование перерисовки формы при изменении размеров
private void Form1_Resize(object sender, EventArgs e) { this.Invalidate(); }Листинг 18.7. Обработчик для события Resize формы Form1
- Запустите форму на выполнение - должен получиться такой результат
Событие Resize мы использовали для прорисовки формы на случай изменения ее размеров в сторону возрастания.
В заключение выполнения этого упражнения введем возможность перетаскивания формы не только за ее заголовок, но и за клиентскую область.
- Добавьте к классу Form1 объявления двух локальных полей целого типа, которые будут хранить координаты курсора в момент нажатия на левую кнопку мыши над формой
int x, y;Листинг 18.8. Объявления полей класса Form1
- Создайте для формы через панель Properties в режиме Events обработчики для событий MouseDown и MouseMove, которые заполните так
private void Form1_MouseDown(object sender, MouseEventArgs e) { // Сохраняем координаты мыши на момент щелчка относительно формы if (e.Button == MouseButtons.Left) { x = e.X; y = e.Y; } } private void Form1_MouseMove(object sender, MouseEventArgs e) { // Измеряем смещение относительно щелчка // и подвигаем форму на это смещение if (e.Button == MouseButtons.Left) { this.Left += e.X - x; this.Top += e.Y - y; } }Листинг 18.9. Обработчики событий MouseDown и MouseMove формы
Этот код обеспечивает перемещение формы относительно начального положения, которое она имела до нажатия левой кнопки мыши, ровно настолько, на сколько переместился курсор мыши после нажатия кнопки. Поэтому нажатый курсор и форма перемещаются синхронно.
Остался последний штрих для этого упражнения - изменять форму курсора при захвате формы.
- Добавьте в код обработчика нажатия кнопки мыши строку изменения формы курсора
private void Form1_MouseDown(object sender, MouseEventArgs e) { // Сохраняем координаты мыши на момент щелчка относительно формы if (e.Button == MouseButtons.Left) { x = e.X; y = e.Y; // Форма курсора Cursor.Current = Cursors.SizeAll; } }Листинг 18.10. Изменение формы курсора при нажатии кнопки мыши
- Измените заголовок формы, задав ее свойству Text значение " Упражнение 1. Непрямоугольная форма "
- Запустите приложение и убедитесь, что при нажатии левой кнопки мыши на клиентской области форма курсора меняется и окно послушно следует за ним