Практически все проекты, построенные в наших лекциях, были Reflection, работа с файлами и базами данных; но все это предмет будущего курса. Тем не менее, нельзя окончить этот курс, не посвятив две последние лекции
Первое знакомство с Windows-проектами состоялось в лекции 2, я настоятельно рекомендую перечитать ее, прежде чем продолжить чтение данной лекции. Вкратце напомню, как создается и выполняется Windows-проект. По умолчанию он содержит Form1 - наследника Form. Этот Main, вызывающую статический метод Run Application, который создает объект Form1 и открывает
Как населить Form, а Control. Так что
Каждый вид
В каких отношениях находятся Form, Control,
(рис 24.1) Иерархия классов элементов управленияЕстественно, все эти Object. Заметьте, Control в иерархии Component, определяющий возможность MarshalByRefObject, задающий возможность передачи Control задает важные свойства, методы и события, наследуемые всеми его потомками. Все Control. Чаще всего, это прямые наследники, но иногда они имеют и непосредственного родителя, которым может быть абстрактный Form является одним из потомков Control, так что ScrollableControl и ContainerControl,
Обычное
Следует четко различать процесс создания Form или наследнику этого Show этого Hide. Реально методы Show и Hide изменяют свойство Visible объекта, так что вместо вызова этих методов можно менять значение этого свойства, устанавливая его либо в true, либо в false.
Заметьте разницу между Hide и Close. Первый из них делает Close отбирает у Show после вызова метода Close невозможно, если только не создать объект заново. Открытие и Show. У Close, но нет метода Open. new.
Main при вызове метода Run, называется Exit Application. Закрытие других
Можно создавать Form. Однако такие объекты довольно редки. Чаще всего создается специальный FormX - наследник Form. Так, в частности, происходит в Form1 - наследник Form. Так происходит в режиме проектирования, когда в проект добавляется новая Add Windows Form. Как правило, каждая
Первичным является понятие модального и немодального окна. Окно называется модальным, если нельзя закончить работу в открытом окне до тех пор, пока оно не будет закрыто. Модальное окно не позволяет, если оно открыто, временно переключиться на работу с другим окном. Выйти из модального окна можно, только закрыв его. Немодальные окна допускают параллельную работу в окнах. Show открывает ShowDialog - как модальную. Название метода отражает основное назначение модальных
Часто многие
Если такой глобальный объект создается в
Представим себе, что несколько Books. Пусть в
public Books myBooks;
В
myBooks = new Books(max_books);
где max_books - заданная константа. Пусть еще в NewBook:
public NewBook form2;
При создании объекта form2 его
form2 = new NewBook(this);
NewBook содержит
private Form1 mainform; private Books books;
а его
mainform = form; books = mainform.myBooks;
Теперь объекту form2 доступны ранее созданные объекты, задающие книги и Closed, возникающего при закрытии
private void NewBook_Closed(object sender, System.EventArgs e)
{
mainform.Show();
}
открывающий
Создание элегантного, интуитивно ясного интерфейса пользователя - это своего рода искусство, требующее определенного художественного вкуса. Здесь все играет важную роль: размеры и расположение
Одним из образцов, применимых к
(рис 24.2) Главная кнопочная формаClick для каждой командной
private void button4_Click(object sender, System.EventArgs e)
{
//Переход к показу формы для работы со списком
FormList fl= new FormList();
fl.Show();
}
Как видите, открывается новая
Можно предложить следующий образец
На рис. 24.3 показана
(рис 24.3) Форма для списка с курсором, построенная по образцуСписок с курсором имеет группу
Для организации интерфейса разработано большое число ListView, TreeView, DataGrid, несомненно, заслуживают отдельного рассмотрения, но не здесь и не сейчас. Я ограничусь более подробным разбором лишь одного
Во многих задачах пользователю предлагается некоторый список товаров, гостиниц, услуг и прочих прелестей, и он должен выбрать некоторое подмножество элементов из этого списка.
В списке могут храниться строки, тогда объект совпадает с его отображением. Если же хранятся объекты, то в ToString, возвращаемый результат которого и будет строкой, отображаемой в списке.
Давайте рассмотрим главный вопрос: как список заполняется элементами? Есть несколько разных способов. Новой и интересной технологией, применимой к самым разным DataBinding и другие. Эта технология заслуживает отдельного рассмотрения, я о ней только упоминаю, но рассматривать ее не буду. Рассмотрим три других способа.
Заполнить список элементами можно еще на этапе проектирования. Для этого достаточно выбрать свойство Items: появится специальное окно для заполнения списка строками - элементами списка. Добавлять объекты других
Но это можно делать при программной работе со свойством Items, возвращающим специальную коллекцию объектов, которая задана ObjectCollection. Эта коллекция представляет объекты, хранимые в списке, и является основой для работы со списком. ObjectCollection предоставляет стандартный набор методов для работы с коллекцией - вставки, удаления и поиска элементов. Метод Add позволяет добавить новый объект в конец коллекции, метод Insert позволяет добавить элемент в заданную позицию, указанную индексом. Метод AddRange позволяет добавить сразу множество элементов, заданное обычным массивом, массивом ListArray или коллекцией, возвращаемой свойством Items другого списка. Для удаления элементов используются методы Remove, RemoveAt, Clear. Метод Contains позволяет определить, содержится ли заданный объект в коллекции, а метод IndexOf позволяет определить индекс такого элемента. Коллекция может автоматически сортироваться, для этого достаточно задать значение true свойства Sorted, которым обладает список ListBox.
Еще один способ задания элементов списка поддерживается свойством DataSource, значение которого позволяет указать источник данных, ассоциируемый со списком. Понятно, что этот способ является альтернативой коллекции, задаваемой свойством Items. Так что, если источник данных определен свойством DataSource, то нельзя использовать методы ObjectCollection - Add и другие для добавления или удаления элементов списка, - необходимо изменять сам источник данных.
Главное назначение SelectionMode позволяет указать, сколько элементов разрешается выбирать пользователю - один или несколько. Для работы с отобранными элементами имеется ряд свойств. SelectedItem и SelectedIndex возвращают первый отобранный элемент и его индекс. Свойства SelectedItems и SelectedIndices возвращают коллекции, заданные SelectedObjectCollection и SelectedIndexCollection, которые дают возможность анализировать все отобранные пользователем объекты. Методы Contains и IndexOf позволяют определить, выбрал ли пользователь некоторый элемент. Добавлять или удалять элементы из этих коллекций нельзя.
Среди других методов и свойств ListBox - упомяну свойство MultiColumn, с помощью которого можно организовать показ элементов списка в нескольких столбцах; свойство HorizontalScrollBar, задающее горизонтальный скроллинг; методы BeginUpdate и EndUpdate, позволяющие повысить эффективность работы со списком. Все методы по добавлению и удалению элементов, стоящие после BeginUpdate, не будут приводить к перерисовке списка, пока не встретится метод EndUpdate.
У
(рис 24.4) Шаблон формы для обмена данными двух списковНа listBox1 и listBox2, между которыми расположены две командные Click первой Sorted, то автоматически поддерживается сортировка списка). Переносимые элементы удаляются из первого списка. Вторая > ", " >> ") или (" < ", " << "), изображенными на Enter, возникающих при входе в левый или правый списки - listBox1 или listBox2. Еще две командные Enter наших списков:
private void listBox1_Enter(object sender, System.EventArgs e)
{
/*** Событие Enter у списка возникает при входе в список ***/
button1.Text = ">"; button2.Text = ">>";
}
private void listBox2_Enter(object sender,
System.EventArgs e)
{
/*** Событие Enter у списка возникает при входе в список ***/
button1.Text = "<"; button2.Text = "<<";
}
Посмотрим, как устроены Click для командных
private void button1_Click(object sender, System.EventArgs e)
{
/* Обработчик события Click кнопки "> <"
* Выборочный обмен данными между списками
* ListBox1 <-> ListBox2******************/
if(button1.Text == ">")
MoveSelectedItems(listBox1, listBox2);
else
MoveSelectedItems(listBox2, listBox1);
}
private void button2_Click(object sender, System.EventArgs e)
{
/* Обработчик события Click кнопки ">> <<"
* Перенос всех данных одного списка в конец другого списка
* ListBox1 <-> ListBox2******************/
if(button2.Text == ">>")
MoveAllItems(listBox1, listBox2);
else
MoveAllItems(listBox2, listBox1);
}
private void MoveSelectedItems(ListBox list1, ListBox list2)
{
/*** Выделенные элементы списка list1 ****
*** помещаются в конец списка List2 *****
*** и удаляются из списка list1 ********/
list2.BeginUpdate();
foreach (object item in list1.SelectedItems)
{
list2.Items.Add(item);
}
list2.EndUpdate();
ListBox.SelectedIndexCollection indeces = list1.SelectedIndices;
list1.BeginUpdate();
for (int i = indeces.Count -1; i>=0 ; i--)
{
list1.Items.RemoveAt(indeces[i]);
}
list1.EndUpdate();
}
Некоторые комментарии к этому тексту. Заметьте, для добавления выделенных пользователем элементов к другому списку используется коллекция SelectedItems и метод Add, поочередно добавляющий элементы коллекции. Метод AddRange для добавления всей коллекции здесь не проходит:
list2.Items.AddRange(list1.SelectedItems);
поскольку нет автоматического преобразования между коллекциями ObjectCollection и SelectedObjectCollection.
Для удаления выделенных элементов из списка list1 используется коллекция индексов. Обратите внимание, при удалении элемента с заданным индексом из любой коллекции индексы оставшихся элементов автоматически пересчитываются. Поэтому удаление элементов происходит в обратном порядке, начиная с последнего, что гарантирует корректность оставшихся индексов.
Намного проще устроен метод, переносящий все элементы списка:
private void MoveAllItems(ListBox list1, ListBox list2)
{
/*** Все элементы списка list1 ****
**** переносятся в конец списка list2 ****
**** список list1 очищается *************/
list2.Items.AddRange(list1.Items);
list1.Items.Clear();
}
Добавим еще одну функциональную возможность - разрешим переносить элементы из одного списка в другой двойным щелчком DoubleClick наших списков:
private void listBox1_DoubleClick(object sender,
System.EventArgs e)
{
/* Обработчик события DoubleClick левого списка
* Выбранный элемент переносится в правый список
* ListBox1 <-> ListBox2******************/
MoveSelectedItems(listBox1, listBox2);
}
private void listBox2_DoubleClick(object sender,
System.EventArgs e)
{
/* Обработчик события DoubleClick правого списка
* Выбранный элемент переносится в левый список
* ListBox1 <-> ListBox2******************/
MoveSelectedItems(listBox2, listBox1);
}
Обработчики вызывают уже рассмотренные нами методы.
На этом закончим рассмотрение функциональности проектируемого образца Click для двух оставшихся
Для объектного программиста
public class NewForm : InterfacesAndDrawing.TwoLists
Нужно учитывать, что имени класса родителя должно предшествовать имя пространства имен.
Чаще всего, наследуемые
В результате открывается окно Inheritance Picker, в котором можно выбрать родительскую
(рис 24.5) Окно Inheritance Picker наследования формПри наследовании private, означающий запрет на изменение свойств и protected. У всех элементов родительской modifiers, в котором можно указать статус TwoLists.
Наследованную
Построим по указанной технологии двух наследников TwoLists. Дадим им имена: TwoLists_Strings и TwoLists_Books. Они будут отличаться тем, что первый из них будет заполнять левый список строками, а второй - "настоящими объектами" Click для командных
(рис 24.6) Наследуемая форма, открытая в дизайнереОбратите внимание на значки, сопровождающие все наследуемые TwoLists_Strings добавлены
string[] source_items; string[] selected_items; const int max_items = 20;
В конструктор
source_items = new string[max_items]; selected_items = new string[max_items]; InitList1();
Вызываемый в InitList заполняет массив source_items - источник данных - строками, а затем передает эти данные в левый список
void InitList1()
{
//задание элементов источника и инициализация списка формы
source_items[0] ="Бертран Мейер: Методы программирования";
//аналогично заполняются другие элементы массива
//перенос массива в список ListBox1
int i = 0;
while (source_items[i] != null)
{
this.listBox1.Items.Add(source_items[i]); i++;
}
//this.listBox1.DataSource = source_items;
}
Закомментирована альтернативная возможность заполнения списка DataSource. Когда Click для командных
private void button3_Click(object sender, System.EventArgs e)
{
int i =0;
foreach(string item in listBox2.Items)
{
selected_items[i] = item;
Debug.WriteLine(selected_items[i]);
i++;
}
this.Hide();
}
private void button4_Click(object sender, System.EventArgs e)
{
foreach(string item in listBox2.Items)
{
Debug.WriteLine(item);
}
this.Hide();
}
Оба они в Debug-версии проекта выводят данные о книгах, выбранных пользователем, и скрывают затем selected_items.
Второй наследник TwoLists_Books устроен аналогично, но хранит в списке не строки, а объекты Book. Приведу уже без комментариев соответствующие фрагменты кода:
Book[] source_items; Book[] selected_items; const int max_items = 20;
Код, добавляемый в
source_items = new Book[max_items]; selected_items = new Book[max_items]; InitList1();
Метод InitList1 скорректирован для работы с книгами:
void InitList1()
{
//задание элементов источника и инициализация списка формы
Book newbook;
newbook = new Book("Бертран Мейер",
"Методы программирования",3,1980);
source_items[0] =newbook;
//остальные элементы массива заполняются аналогичным
//образом
//перенос массива в список ListBox1
int i = 0;
while (source_items[i] != null)
{
this.listBox1.Items.Add(source_items[i]);
i++;
}
}
Click командных
private void button3_Click(object sender, System.EventArgs e)
{
int i =0;
foreach(object item in listBox2.Items)
{
selected_items[i] = (Book)item;
selected_items[i].PrintBook();
i++;
}
this.Hide();
}
private void button4_Click(object sender, System.EventArgs e)
{
Book book;
foreach(object item in listBox2.Items)
{
book = (Book)item;
book.PrintBook();
}
this.Hide();
}
Book определен следующим образом:
public class Book
{
//поля
string author, title;
int price, year;
public Book(string a, string t, int p, int y)
{
author = a; title = t; price = p; year = y;
}
public override string ToString()
{
return( title + " : " + author);
}
public void PrintBook()
{
Debug.WriteLine("автор:" + author + " название: " +
title + " цена: " + price.ToString() +"
год издания: " + year.ToString());
}
}
Обратите внимание, что в ToString, который задает строку, отображаемую в списке.
В завершение проекта нам осталось спроектировать
(рис 24.7) Главная кнопочная форма проектаClick вызывают соответствующую
(рис 24.8) Форма TwoLists_Strings в процессе работы
Важными атрибутами интерфейса являются
Несколько слов о терминологии. Когда мы говорим о
Кроме структуры, заданной
Для построения в режиме проектирования MainMenu. (В Visual Studio 2005 MenuStrip, а для создания
После перетаскивания метка с изображением этого
(рис 24.9) Создание меню в режиме проектированияРассмотрим пример, в котором File, Figure, Color. File содержит две Open и Save. Figure состоит из двух пунктов - Closed и Unclosed, первый из которых содержит две Circle и Rectangle, второй содержит одну Line. Пункт Color
Посадим на Click. Для Open, Save и Color, имеющих общепринятый смысл, обработчики будут открывать соответствующие этим
Связывание Properties щелкается значок молнии и из списка событий выбирается событие Click, после чего открывается заготовка
Вот как выглядят Click Open, Save и Color:
private void menuItem4_Click(object sender, System.EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.ShowDialog();
//код, показывающий, что делать с открытым файлом
textBox1.Text = "Открытие Файла!";
}
private void menuItem10_Click(object sender, System.EventArgs e)
{
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.ShowDialog();
//код, анализирующий результат операции сохранения файла
textBox1.Text = "Сохранение Файла!";
}
private void menuItem3_Click(object sender, System.EventArgs e)
{
ColorDialog colorDialog1 = new ColorDialog();
if (colorDialog1.ShowDialog()== DialogResult.OK)
this.textBox1.BackColor =colorDialog1.Color;
}
На рис. 24.10 показано диалоговое окно для выбора цвета, открытое при выборе Color.
(рис 24.10) Диалоговое окно ColorDialog, позволяющее выбрать цветДля полноты картины зададим Circle, Rectangle, Line, не выполняющие пока содержательной работы, а лишь информирующие о намерениях:
private void menuItem7_Click(object sender, System.EventArgs e)
{
textBox1.Text = "Рисование круга!";
}
private void menuItem8_Click(object sender, System.EventArgs e)
{
textBox1.Text = "Рисование прямоугольника!";
}
private void menuItem9_Click(object sender, System.EventArgs e)
{
textBox1.Text = "Рисование прямой!";
}
Закончу на этом рассмотрение процесса создания
Все, что можно делать руками, можно делать программно. Рассмотрим MainMenu, ContextMenu и MenuItem. MenuItem задает MenuItems, которым обладают все MenuItems из MenuItem. С коллекцией можно работать обычным образом. Создание MainMenu и ContextMenu и множества объектов MenuItem. Последние добавляются в коллекцию либо контейнерных MenuItem. Созданные объекты MainMenu и ContextMenu связываются со свойствами Menu и ConextMenu. Проанализируем код, созданный в процессе проектирования Дизайнером Меню и Дизайнером Формы для нашего примера.
Вот какие поля
private System.Windows.Forms.MainMenu mainMenu1; private System.Windows.Forms.MenuItem menuItem1; //другие элементы меню private System.Windows.Forms.MenuItem menuItem10;
Основной код, создаваемый дизайнерами, помещается в метод InitializeComponent. Приведу лишь фрагменты этого кода:
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.menuItem1 = new System.Windows.Forms.MenuItem();
...
// mainMenu1
this.mainMenu1.MenuItems.AddRange(new
System.Windows.Forms.MenuItem[]
{this.menuItem1,this.menuItem2,this.menuItem3});
// menuItem1
this.menuItem1.Index = 0;
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[]
{this.menuItem4,this.menuItem10});
this.menuItem1.Text = "File";
...
// menuItem4
this.menuItem4.Index = 0;
this.menuItem4.Text = "Open";
this.menuItem4.Click += new System.EventHandler(this.menuItem4_Click);
...
// Form1
...
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.textBox1});
this.Menu = this.mainMenu1;
this.Name = "Form1"; this.Text = "Form1";
Надеюсь, что данный программный код прозрачен и не требует дополнительных комментариев.
Панель с командными Click. Как правило,
Роль контейнерного ToolBarButton.
Давайте спроектируем панель с тремя Open, Save и Color, повторяющие ImageList, хранящий рисунки, связываемые с ToolBar - панель, на которой будут располагаться imageList1 добавим три подходящие картинки, свяжем этот объект со свойством ImageList объекта toolBar1. Затем добавим три toolBar1 и зададим для них нужные свойства - текст, появляющийся на ImageList. На рис. 24.11 показан процесс задания
(рис 24.11) Проектирование панели с командными кнопкамиПроанализируем теперь созданный дизайнером программный код. Как всегда начнем с
private System.Windows.Forms.ToolBar toolBar1; private System.Windows.Forms.ImageList imageList1; private System.Windows.Forms.ToolBarButton toolBarButton1; private System.Windows.Forms.ToolBarButton toolBarButton2; private System.Windows.Forms.ToolBarButton toolBarButton3;
В методе InitializeComponent эти объекты создаются и инициализируются:
this.toolBar1 = new System.Windows.Forms.ToolBar();
this.imageList1 = new System.Windows.Forms.ImageList(this.components);
this.toolBarButton1 = new System.Windows.Forms.ToolBarButton();
this.toolBarButton2 = new System.Windows.Forms.ToolBarButton();
this.toolBarButton3 = new System.Windows.Forms.ToolBarButton();
...
// toolBar1
this.toolBar1.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[]
{this.toolBarButton1, this.toolBarButton2,this.toolBarButton3});
this.toolBar1.DropDownArrows = true;
this.toolBar1.ImageList = this.imageList1;
this.toolBar1.Name = "toolBar1";
this.toolBar1.ShowToolTips = true;
this.toolBar1.Size = new System.Drawing.Size(432, 42);
this.toolBar1.TabIndex = 1;
this.toolBar1.ButtonClick +=
new System.Windows.Forms.ToolBarButtonClickEventHandler(
this.toolBar1_ButtonClick);
// toolBarButton1
this.toolBarButton1.ImageIndex = 0;
this.toolBarButton1.Text = "OpenFile";
this.toolBarButton1.ToolTipText =
"Диалоговое окно открытия файла";
...
Этот текст должен быть понятен без комментариев, а вот об Click стоит сказать несколько слов. Во-первых, событие Click не связывается с каждой командной
private void toolBar1_ButtonClick(object sender,
System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
int buttonNumber = toolBar1.Buttons.IndexOf(e.Button);
switch (buttonNumber)
{
case 0:
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.ShowDialog();
//код, показывающий, что делать с открытым файлом
textBox1.Text = "Открытие Файла!";
break;
case 1:
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.ShowDialog();
//код, анализирующий результат операции сохранения файла
textBox1.Text = "Сохранение Файла!";
break;
default:
ColorDialog colorDialog1 = new ColorDialog();
if (colorDialog1.ShowDialog()== DialogResult.OK)
this.textBox1.BackColor =colorDialog1.Color;
break;
}
}
В заключение взгляните на спроектированную
(рис 24.12) Форма с меню и инструментальной панелью
Drawing, хотя Drawing2D.
Объекты этого CreateGraphics Control - наследника Form - возвращает объект, ассоциированный с выводом
При
Graphics graph;
а в
graph = CreateGraphics();
Затем всюду в программе, где нужно работать с graph и его методы. Есть другой способ получения этого объекта - обработчики некоторых событий получают объект
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics gr = e.Graphics;
//перерисовка, использующая методы объекта gr
}
Для получения этого объекта можно использовать и статические методы самого
У
Для DrawString, позволяющий выводить Draw - DrawEllipse, DrawLine, DrawArc и другие, позволяющие цветным пером (объектом Fill - FillEllipse, FillPie, FillRectangle и другие, позволяющие
Методам группы Draw Color. Для выбора подходящего цвета можно использовать упоминавшееся выше диалоговое окно Color либо одно из многочисленных статических свойств Color, возвращающее требуемый цвет. Возможно и непосредственное задание элементов структуры в виде комбинации RGB - трех цветов - красного, зеленого и голубого. Вместо создания нового пера с помощью
SolidBrush - для сплошной закраски области заданным цветом;TextureBrush - для закраски области заданной картинкой (image);HatchBrush - для закраски области предопределенным узором;LinearGradientBrush - для сплошной закраски с переходом от одного цвета к другому, где изменение оттенков задается линейным градиентом;PathGradientBrush - для сплошной закраски с переходом от одного цвета к другому, где изменение оттенков задается более сложным путем.Первые два System.Drawing, остальные - в System.Drawing.Drawing2D.
У каждого из этих
Построим проект для
(рис 24.13) Кнопочная форма "кисть или перо"Выбор соответствующей командной
В
Прежде чем рассмотреть программный код, давайте посмотрим, как выглядят нарисованные программой
(рис 24.14) Паутина БезьеПерейдем к рассмотрению кода. Первым делом добавим в поля
//fields Point center; Point[] points = new Point[10]; Pen pen; Graphics graph; int count;
Точка center будет задавать общую начальную точку для всех рисуемых points будет задавать остальные точки, используемые при построении pen и graph, необходимых при count играет техническую роль, о которой скажу чуть позже, прямого отношения к
В MyInit, инициализирующий введенные объекты:
void MyInit()
{
int cx = ClientSize.Width;
int cy = ClientSize.Height;
points[0] = new Point(0,0);
points[1] = new Point(cx/2,0);
points[2] = new Point(cx,0);
points[3] = new Point(0,cy/2);
points[4] = new Point(cx,cy/2);
points[5] = new Point(0,cy);
points[6] = new Point(cx/2,cy);
points[7] = new Point(cx,cy);
points[8] = new Point(0,0);
points[9] = new Point(cx/2,0);
graph = this.CreateGraphics();
center = new Point(cx/2,cy/2);
count =1;
}
DrawWeb, устроенном очень просто. В цикле рисуется 8 кривых, используя точку center и массив points:
void DrawWeb()
{
for (int i = 0; i<8; i++)
graph.DrawBezier(pen,center,points[i],points[i+2],
points[i+1]);
}
Метод DrawBezier, вызываемый объектом graph Draw. Первым аргументом у всех этих методов является объект
Главный вопрос, требующий решения: где же вызывать сам метод DrawWeb, где инициализировать center будет следовать за курсором мыши, то естественно, чтобы MouseMove нашей BezierWeb. (Напомню, для подключения события
Вот текст обработчика этого события:
private void BezierWeb_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
pen = SystemPens.Control;
DrawWeb();
center.X = e.X; center.Y = e.Y;
//pen = new Pen(Color.Aquamarine);
pen = SystemPens.ControlText;
DrawWeb();
}
Метод DrawWeb вызывается дважды - первый раз с пером цвета фона, другой - с цветом, принятым системой для отображения текста. Обратите внимание, для создания нужного пера в данном случае не вызывается pen с помощью
Перед center получает координаты курсора мыши, передаваемые аргументом
Вызов метода DrawWeb добавлен еще и в
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
pen = SystemPens.ControlText;
DrawWeb();
Debug.WriteLine(count++);
}
Говоря о Invalidate - во всех этих случаях требуется перерисовать область. Тогда-то и возникает count, введенная нами, позволяет в режиме отладки подсчитывать число вызовов
MouseMove. Вместо этого переопределяют родительский метод OnPaint. (Как переопределяются родительские методы группы On, занимающиеся обработкой событий, другие методы OnPaint ). В результате появится заготовка для переопределения метода.)
В данном контексте перерисовка сводится, как это обычно делается, к вызову метода, выполняющего
Закончим на этом с
Создадим в нашем проекте новую RandomShapes, в которой будем рисовать и закрашивать Click. При каждом щелчке
На рис. 24.15 показана
А теперь приведем программный код, реализующий
//fields int cx,cy; Graphics graph; Brush brush; Color color; Random rnd;
Инициализация полей производится в методе MyInit, вызываемом
(рис 24.15) Рисование кистями разного типаvoid MyInit()
{
cx = ClientSize.Width;
cy = ClientSize.Height;
graph = CreateGraphics();
rnd = new Random();
}
Рассмотрим теперь основной метод, реализующий
void DrawShapes()
{
for(int i=0; i<3; i++)
{
//выбирается цвет - красный, желтый, голубой
int numcolor = rnd.Next(3);
switch (numcolor)
{
case 0:
color = Color.Blue; break;
case 1:
color = Color.Yellow; break;
case 2:
color = Color.Red; break;
}
//градиентной кистью рисуется эллипс,
//местоположение случайно
Point top = new Point(rnd.Next(cx), rnd.Next(cy));
Size sz = new Size(rnd.Next(cx-top.X), rnd.Next(cy-top.Y));
Rectangle rct = new Rectangle(top, sz);
Point bottom = top + sz;
brush = new LinearGradientBrush(top, bottom,
Color.White,color);
graph.FillEllipse(brush,rct);
//сплошной кистью рисуется сектор,
//местоположение случайно
top = new Point(rnd.Next(cx), rnd.Next(cy));
sz = new Size(rnd.Next(cx-top.X), rnd.Next(cy-top.Y));
rct = new Rectangle(top, sz);
brush = new SolidBrush(color);
graph.FillPie(brush,rct,30f,60f);
//узорной кистью рисуется прямоугольник,
//местоположение случайно
top = new Point(rnd.Next(cx), rnd.Next(cy));
sz = new Size(rnd.Next(cx-top.X), rnd.Next(cy-top.Y));
rct = new Rectangle(top, sz);
HatchStyle hs = (HatchStyle)rnd.Next(52);
brush = new HatchBrush(hs,Color.White, Color.Black);
graph.FillRectangle(brush,rct);
}
}
Приведу некоторые комментарии в дополнение к тем, что встроены в текст метода. Здесь многое построено на работе со случайными числами. Случайным образом выбирается один из возможных цветов для
brush = new LinearGradientBrush(top, bottom, Color.White,color);
то нужно в
Наиболее просто задается сплошная кисть:
brush = new SolidBrush(color);
Для нее достаточно указать только цвет. Для узорной кисти нужно задать предопределенный тип узора, всего их возможно 52. В нашем примере тип узора выбирается случайным образом:
HatchStyle hs = (HatchStyle)rnd.Next(52); brush = new HatchBrush(hs,Color.White, Color.Black);
Помимо первого аргумента, задающего тип узора, указываются еще два цвета - первый определяет цвет повторяющегося элемента, второй - цвет границы между элементами узора.
Непосредственное Fill:
graph.FillEllipse(brush,rct); graph.FillPie(brush,rct,30f,60f); graph.FillRectangle(brush,rct);
Первый аргумент всегда задает кисть, а остальные зависят от типа рисуемой фигуры. Как правило, всегда задается прямоугольник, ограничивающий данную фигуру.
Вызов метода DrawShapes, как уже говорилось, встроен в Click RandomShapes:
private void RandomShapes_Click(object sender, System.EventArgs e)
{
DrawShapes();
}
На этом поставим точку в рассмотрении данной темы. По сути, этим завершается и наш учебный курс. В последней лекции будет рассмотрен некоторый заключительный проект.
Практически все проекты, построенные в наших лекциях, были Reflection, работа с файлами и базами данных; но все это предмет будущего курса. Тем не менее, нельзя окончить этот курс, не посвятив две последние лекции
Первое знакомство с Windows-проектами состоялось в лекции 2, я настоятельно рекомендую перечитать ее, прежде чем продолжить чтение данной лекции. Вкратце напомню, как создается и выполняется Windows-проект. По умолчанию он содержит Form1 - наследника Form. Этот Main, вызывающую статический метод Run Application, который создает объект Form1 и открывает
Как населить Form, а Control. Так что
Каждый вид
В каких отношениях находятся Form, Control,
(рис 24.1) Иерархия классов элементов управленияЕстественно, все эти Object. Заметьте, Control в иерархии Component, определяющий возможность MarshalByRefObject, задающий возможность передачи Control задает важные свойства, методы и события, наследуемые всеми его потомками. Все Control. Чаще всего, это прямые наследники, но иногда они имеют и непосредственного родителя, которым может быть абстрактный Form является одним из потомков Control, так что ScrollableControl и ContainerControl,
Обычное
Следует четко различать процесс создания Form или наследнику этого Show этого Hide. Реально методы Show и Hide изменяют свойство Visible объекта, так что вместо вызова этих методов можно менять значение этого свойства, устанавливая его либо в true, либо в false.
Заметьте разницу между Hide и Close. Первый из них делает Close отбирает у Show после вызова метода Close невозможно, если только не создать объект заново. Открытие и Show. У Close, но нет метода Open. new.
Main при вызове метода Run, называется Exit Application. Закрытие других
Можно создавать Form. Однако такие объекты довольно редки. Чаще всего создается специальный FormX - наследник Form. Так, в частности, происходит в Form1 - наследник Form. Так происходит в режиме проектирования, когда в проект добавляется новая Add Windows Form. Как правило, каждая
Первичным является понятие модального и немодального окна. Окно называется модальным, если нельзя закончить работу в открытом окне до тех пор, пока оно не будет закрыто. Модальное окно не позволяет, если оно открыто, временно переключиться на работу с другим окном. Выйти из модального окна можно, только закрыв его. Немодальные окна допускают параллельную работу в окнах. Show открывает ShowDialog - как модальную. Название метода отражает основное назначение модальных
Часто многие
Если такой глобальный объект создается в
Представим себе, что несколько Books. Пусть в
public Books myBooks;
В
myBooks = new Books(max_books);
где max_books - заданная константа. Пусть еще в NewBook:
public NewBook form2;
При создании объекта form2 его
form2 = new NewBook(this);
NewBook содержит
private Form1 mainform; private Books books;
а его
mainform = form; books = mainform.myBooks;
Теперь объекту form2 доступны ранее созданные объекты, задающие книги и Closed, возникающего при закрытии
private void NewBook_Closed(object sender, System.EventArgs e)
{
mainform.Show();
}
открывающий
Создание элегантного, интуитивно ясного интерфейса пользователя - это своего рода искусство, требующее определенного художественного вкуса. Здесь все играет важную роль: размеры и расположение
Одним из образцов, применимых к
(рис 24.2) Главная кнопочная формаClick для каждой командной
private void button4_Click(object sender, System.EventArgs e)
{
//Переход к показу формы для работы со списком
FormList fl= new FormList();
fl.Show();
}
Как видите, открывается новая
Можно предложить следующий образец
На рис. 24.3 показана
(рис 24.3) Форма для списка с курсором, построенная по образцуСписок с курсором имеет группу
Для организации интерфейса разработано большое число ListView, TreeView, DataGrid, несомненно, заслуживают отдельного рассмотрения, но не здесь и не сейчас. Я ограничусь более подробным разбором лишь одного
Во многих задачах пользователю предлагается некоторый список товаров, гостиниц, услуг и прочих прелестей, и он должен выбрать некоторое подмножество элементов из этого списка.
В списке могут храниться строки, тогда объект совпадает с его отображением. Если же хранятся объекты, то в ToString, возвращаемый результат которого и будет строкой, отображаемой в списке.
Давайте рассмотрим главный вопрос: как список заполняется элементами? Есть несколько разных способов. Новой и интересной технологией, применимой к самым разным DataBinding и другие. Эта технология заслуживает отдельного рассмотрения, я о ней только упоминаю, но рассматривать ее не буду. Рассмотрим три других способа.
Заполнить список элементами можно еще на этапе проектирования. Для этого достаточно выбрать свойство Items: появится специальное окно для заполнения списка строками - элементами списка. Добавлять объекты других
Но это можно делать при программной работе со свойством Items, возвращающим специальную коллекцию объектов, которая задана ObjectCollection. Эта коллекция представляет объекты, хранимые в списке, и является основой для работы со списком. ObjectCollection предоставляет стандартный набор методов для работы с коллекцией - вставки, удаления и поиска элементов. Метод Add позволяет добавить новый объект в конец коллекции, метод Insert позволяет добавить элемент в заданную позицию, указанную индексом. Метод AddRange позволяет добавить сразу множество элементов, заданное обычным массивом, массивом ListArray или коллекцией, возвращаемой свойством Items другого списка. Для удаления элементов используются методы Remove, RemoveAt, Clear. Метод Contains позволяет определить, содержится ли заданный объект в коллекции, а метод IndexOf позволяет определить индекс такого элемента. Коллекция может автоматически сортироваться, для этого достаточно задать значение true свойства Sorted, которым обладает список ListBox.
Еще один способ задания элементов списка поддерживается свойством DataSource, значение которого позволяет указать источник данных, ассоциируемый со списком. Понятно, что этот способ является альтернативой коллекции, задаваемой свойством Items. Так что, если источник данных определен свойством DataSource, то нельзя использовать методы ObjectCollection - Add и другие для добавления или удаления элементов списка, - необходимо изменять сам источник данных.
Главное назначение SelectionMode позволяет указать, сколько элементов разрешается выбирать пользователю - один или несколько. Для работы с отобранными элементами имеется ряд свойств. SelectedItem и SelectedIndex возвращают первый отобранный элемент и его индекс. Свойства SelectedItems и SelectedIndices возвращают коллекции, заданные SelectedObjectCollection и SelectedIndexCollection, которые дают возможность анализировать все отобранные пользователем объекты. Методы Contains и IndexOf позволяют определить, выбрал ли пользователь некоторый элемент. Добавлять или удалять элементы из этих коллекций нельзя.
Среди других методов и свойств ListBox - упомяну свойство MultiColumn, с помощью которого можно организовать показ элементов списка в нескольких столбцах; свойство HorizontalScrollBar, задающее горизонтальный скроллинг; методы BeginUpdate и EndUpdate, позволяющие повысить эффективность работы со списком. Все методы по добавлению и удалению элементов, стоящие после BeginUpdate, не будут приводить к перерисовке списка, пока не встретится метод EndUpdate.
У
(рис 24.4) Шаблон формы для обмена данными двух списковНа listBox1 и listBox2, между которыми расположены две командные Click первой Sorted, то автоматически поддерживается сортировка списка). Переносимые элементы удаляются из первого списка. Вторая > ", " >> ") или (" < ", " << "), изображенными на Enter, возникающих при входе в левый или правый списки - listBox1 или listBox2. Еще две командные Enter наших списков:
private void listBox1_Enter(object sender, System.EventArgs e)
{
/*** Событие Enter у списка возникает при входе в список ***/
button1.Text = ">"; button2.Text = ">>";
}
private void listBox2_Enter(object sender,
System.EventArgs e)
{
/*** Событие Enter у списка возникает при входе в список ***/
button1.Text = "<"; button2.Text = "<<";
}
Посмотрим, как устроены Click для командных
private void button1_Click(object sender, System.EventArgs e)
{
/* Обработчик события Click кнопки "> <"
* Выборочный обмен данными между списками
* ListBox1 <-> ListBox2******************/
if(button1.Text == ">")
MoveSelectedItems(listBox1, listBox2);
else
MoveSelectedItems(listBox2, listBox1);
}
private void button2_Click(object sender, System.EventArgs e)
{
/* Обработчик события Click кнопки ">> <<"
* Перенос всех данных одного списка в конец другого списка
* ListBox1 <-> ListBox2******************/
if(button2.Text == ">>")
MoveAllItems(listBox1, listBox2);
else
MoveAllItems(listBox2, listBox1);
}
private void MoveSelectedItems(ListBox list1, ListBox list2)
{
/*** Выделенные элементы списка list1 ****
*** помещаются в конец списка List2 *****
*** и удаляются из списка list1 ********/
list2.BeginUpdate();
foreach (object item in list1.SelectedItems)
{
list2.Items.Add(item);
}
list2.EndUpdate();
ListBox.SelectedIndexCollection indeces = list1.SelectedIndices;
list1.BeginUpdate();
for (int i = indeces.Count -1; i>=0 ; i--)
{
list1.Items.RemoveAt(indeces[i]);
}
list1.EndUpdate();
}
Некоторые комментарии к этому тексту. Заметьте, для добавления выделенных пользователем элементов к другому списку используется коллекция SelectedItems и метод Add, поочередно добавляющий элементы коллекции. Метод AddRange для добавления всей коллекции здесь не проходит:
list2.Items.AddRange(list1.SelectedItems);
поскольку нет автоматического преобразования между коллекциями ObjectCollection и SelectedObjectCollection.
Для удаления выделенных элементов из списка list1 используется коллекция индексов. Обратите внимание, при удалении элемента с заданным индексом из любой коллекции индексы оставшихся элементов автоматически пересчитываются. Поэтому удаление элементов происходит в обратном порядке, начиная с последнего, что гарантирует корректность оставшихся индексов.
Намного проще устроен метод, переносящий все элементы списка:
private void MoveAllItems(ListBox list1, ListBox list2)
{
/*** Все элементы списка list1 ****
**** переносятся в конец списка list2 ****
**** список list1 очищается *************/
list2.Items.AddRange(list1.Items);
list1.Items.Clear();
}
Добавим еще одну функциональную возможность - разрешим переносить элементы из одного списка в другой двойным щелчком DoubleClick наших списков:
private void listBox1_DoubleClick(object sender,
System.EventArgs e)
{
/* Обработчик события DoubleClick левого списка
* Выбранный элемент переносится в правый список
* ListBox1 <-> ListBox2******************/
MoveSelectedItems(listBox1, listBox2);
}
private void listBox2_DoubleClick(object sender,
System.EventArgs e)
{
/* Обработчик события DoubleClick правого списка
* Выбранный элемент переносится в левый список
* ListBox1 <-> ListBox2******************/
MoveSelectedItems(listBox2, listBox1);
}
Обработчики вызывают уже рассмотренные нами методы.
На этом закончим рассмотрение функциональности проектируемого образца Click для двух оставшихся
Для объектного программиста
public class NewForm : InterfacesAndDrawing.TwoLists
Нужно учитывать, что имени класса родителя должно предшествовать имя пространства имен.
Чаще всего, наследуемые
В результате открывается окно Inheritance Picker, в котором можно выбрать родительскую
(рис 24.5) Окно Inheritance Picker наследования формПри наследовании private, означающий запрет на изменение свойств и protected. У всех элементов родительской modifiers, в котором можно указать статус TwoLists.
Наследованную
Построим по указанной технологии двух наследников TwoLists. Дадим им имена: TwoLists_Strings и TwoLists_Books. Они будут отличаться тем, что первый из них будет заполнять левый список строками, а второй - "настоящими объектами" Click для командных
(рис 24.6) Наследуемая форма, открытая в дизайнереОбратите внимание на значки, сопровождающие все наследуемые TwoLists_Strings добавлены
string[] source_items; string[] selected_items; const int max_items = 20;
В конструктор
source_items = new string[max_items]; selected_items = new string[max_items]; InitList1();
Вызываемый в InitList заполняет массив source_items - источник данных - строками, а затем передает эти данные в левый список
void InitList1()
{
//задание элементов источника и инициализация списка формы
source_items[0] ="Бертран Мейер: Методы программирования";
//аналогично заполняются другие элементы массива
//перенос массива в список ListBox1
int i = 0;
while (source_items[i] != null)
{
this.listBox1.Items.Add(source_items[i]); i++;
}
//this.listBox1.DataSource = source_items;
}
Закомментирована альтернативная возможность заполнения списка DataSource. Когда Click для командных
private void button3_Click(object sender, System.EventArgs e)
{
int i =0;
foreach(string item in listBox2.Items)
{
selected_items[i] = item;
Debug.WriteLine(selected_items[i]);
i++;
}
this.Hide();
}
private void button4_Click(object sender, System.EventArgs e)
{
foreach(string item in listBox2.Items)
{
Debug.WriteLine(item);
}
this.Hide();
}
Оба они в Debug-версии проекта выводят данные о книгах, выбранных пользователем, и скрывают затем selected_items.
Второй наследник TwoLists_Books устроен аналогично, но хранит в списке не строки, а объекты Book. Приведу уже без комментариев соответствующие фрагменты кода:
Book[] source_items; Book[] selected_items; const int max_items = 20;
Код, добавляемый в
source_items = new Book[max_items]; selected_items = new Book[max_items]; InitList1();
Метод InitList1 скорректирован для работы с книгами:
void InitList1()
{
//задание элементов источника и инициализация списка формы
Book newbook;
newbook = new Book("Бертран Мейер",
"Методы программирования",3,1980);
source_items[0] =newbook;
//остальные элементы массива заполняются аналогичным
//образом
//перенос массива в список ListBox1
int i = 0;
while (source_items[i] != null)
{
this.listBox1.Items.Add(source_items[i]);
i++;
}
}
Click командных
private void button3_Click(object sender, System.EventArgs e)
{
int i =0;
foreach(object item in listBox2.Items)
{
selected_items[i] = (Book)item;
selected_items[i].PrintBook();
i++;
}
this.Hide();
}
private void button4_Click(object sender, System.EventArgs e)
{
Book book;
foreach(object item in listBox2.Items)
{
book = (Book)item;
book.PrintBook();
}
this.Hide();
}
Book определен следующим образом:
public class Book
{
//поля
string author, title;
int price, year;
public Book(string a, string t, int p, int y)
{
author = a; title = t; price = p; year = y;
}
public override string ToString()
{
return( title + " : " + author);
}
public void PrintBook()
{
Debug.WriteLine("автор:" + author + " название: " +
title + " цена: " + price.ToString() +"
год издания: " + year.ToString());
}
}
Обратите внимание, что в ToString, который задает строку, отображаемую в списке.
В завершение проекта нам осталось спроектировать
(рис 24.7) Главная кнопочная форма проектаClick вызывают соответствующую
(рис 24.8) Форма TwoLists_Strings в процессе работы
Важными атрибутами интерфейса являются
Несколько слов о терминологии. Когда мы говорим о
Кроме структуры, заданной
Для построения в режиме проектирования MainMenu. (В Visual Studio 2005 MenuStrip, а для создания
После перетаскивания метка с изображением этого
(рис 24.9) Создание меню в режиме проектированияРассмотрим пример, в котором File, Figure, Color. File содержит две Open и Save. Figure состоит из двух пунктов - Closed и Unclosed, первый из которых содержит две Circle и Rectangle, второй содержит одну Line. Пункт Color
Посадим на Click. Для Open, Save и Color, имеющих общепринятый смысл, обработчики будут открывать соответствующие этим
Связывание Properties щелкается значок молнии и из списка событий выбирается событие Click, после чего открывается заготовка
Вот как выглядят Click Open, Save и Color:
private void menuItem4_Click(object sender, System.EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.ShowDialog();
//код, показывающий, что делать с открытым файлом
textBox1.Text = "Открытие Файла!";
}
private void menuItem10_Click(object sender, System.EventArgs e)
{
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.ShowDialog();
//код, анализирующий результат операции сохранения файла
textBox1.Text = "Сохранение Файла!";
}
private void menuItem3_Click(object sender, System.EventArgs e)
{
ColorDialog colorDialog1 = new ColorDialog();
if (colorDialog1.ShowDialog()== DialogResult.OK)
this.textBox1.BackColor =colorDialog1.Color;
}
На рис. 24.10 показано диалоговое окно для выбора цвета, открытое при выборе Color.
(рис 24.10) Диалоговое окно ColorDialog, позволяющее выбрать цветДля полноты картины зададим Circle, Rectangle, Line, не выполняющие пока содержательной работы, а лишь информирующие о намерениях:
private void menuItem7_Click(object sender, System.EventArgs e)
{
textBox1.Text = "Рисование круга!";
}
private void menuItem8_Click(object sender, System.EventArgs e)
{
textBox1.Text = "Рисование прямоугольника!";
}
private void menuItem9_Click(object sender, System.EventArgs e)
{
textBox1.Text = "Рисование прямой!";
}
Закончу на этом рассмотрение процесса создания
Все, что можно делать руками, можно делать программно. Рассмотрим MainMenu, ContextMenu и MenuItem. MenuItem задает MenuItems, которым обладают все MenuItems из MenuItem. С коллекцией можно работать обычным образом. Создание MainMenu и ContextMenu и множества объектов MenuItem. Последние добавляются в коллекцию либо контейнерных MenuItem. Созданные объекты MainMenu и ContextMenu связываются со свойствами Menu и ConextMenu. Проанализируем код, созданный в процессе проектирования Дизайнером Меню и Дизайнером Формы для нашего примера.
Вот какие поля
private System.Windows.Forms.MainMenu mainMenu1; private System.Windows.Forms.MenuItem menuItem1; //другие элементы меню private System.Windows.Forms.MenuItem menuItem10;
Основной код, создаваемый дизайнерами, помещается в метод InitializeComponent. Приведу лишь фрагменты этого кода:
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.menuItem1 = new System.Windows.Forms.MenuItem();
...
// mainMenu1
this.mainMenu1.MenuItems.AddRange(new
System.Windows.Forms.MenuItem[]
{this.menuItem1,this.menuItem2,this.menuItem3});
// menuItem1
this.menuItem1.Index = 0;
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[]
{this.menuItem4,this.menuItem10});
this.menuItem1.Text = "File";
...
// menuItem4
this.menuItem4.Index = 0;
this.menuItem4.Text = "Open";
this.menuItem4.Click += new System.EventHandler(this.menuItem4_Click);
...
// Form1
...
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.textBox1});
this.Menu = this.mainMenu1;
this.Name = "Form1"; this.Text = "Form1";
Надеюсь, что данный программный код прозрачен и не требует дополнительных комментариев.
Панель с командными Click. Как правило,
Роль контейнерного ToolBarButton.
Давайте спроектируем панель с тремя Open, Save и Color, повторяющие ImageList, хранящий рисунки, связываемые с ToolBar - панель, на которой будут располагаться imageList1 добавим три подходящие картинки, свяжем этот объект со свойством ImageList объекта toolBar1. Затем добавим три toolBar1 и зададим для них нужные свойства - текст, появляющийся на ImageList. На рис. 24.11 показан процесс задания
(рис 24.11) Проектирование панели с командными кнопкамиПроанализируем теперь созданный дизайнером программный код. Как всегда начнем с
private System.Windows.Forms.ToolBar toolBar1; private System.Windows.Forms.ImageList imageList1; private System.Windows.Forms.ToolBarButton toolBarButton1; private System.Windows.Forms.ToolBarButton toolBarButton2; private System.Windows.Forms.ToolBarButton toolBarButton3;
В методе InitializeComponent эти объекты создаются и инициализируются:
this.toolBar1 = new System.Windows.Forms.ToolBar();
this.imageList1 = new System.Windows.Forms.ImageList(this.components);
this.toolBarButton1 = new System.Windows.Forms.ToolBarButton();
this.toolBarButton2 = new System.Windows.Forms.ToolBarButton();
this.toolBarButton3 = new System.Windows.Forms.ToolBarButton();
...
// toolBar1
this.toolBar1.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[]
{this.toolBarButton1, this.toolBarButton2,this.toolBarButton3});
this.toolBar1.DropDownArrows = true;
this.toolBar1.ImageList = this.imageList1;
this.toolBar1.Name = "toolBar1";
this.toolBar1.ShowToolTips = true;
this.toolBar1.Size = new System.Drawing.Size(432, 42);
this.toolBar1.TabIndex = 1;
this.toolBar1.ButtonClick +=
new System.Windows.Forms.ToolBarButtonClickEventHandler(
this.toolBar1_ButtonClick);
// toolBarButton1
this.toolBarButton1.ImageIndex = 0;
this.toolBarButton1.Text = "OpenFile";
this.toolBarButton1.ToolTipText =
"Диалоговое окно открытия файла";
...
Этот текст должен быть понятен без комментариев, а вот об Click стоит сказать несколько слов. Во-первых, событие Click не связывается с каждой командной
private void toolBar1_ButtonClick(object sender,
System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
int buttonNumber = toolBar1.Buttons.IndexOf(e.Button);
switch (buttonNumber)
{
case 0:
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.ShowDialog();
//код, показывающий, что делать с открытым файлом
textBox1.Text = "Открытие Файла!";
break;
case 1:
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.ShowDialog();
//код, анализирующий результат операции сохранения файла
textBox1.Text = "Сохранение Файла!";
break;
default:
ColorDialog colorDialog1 = new ColorDialog();
if (colorDialog1.ShowDialog()== DialogResult.OK)
this.textBox1.BackColor =colorDialog1.Color;
break;
}
}
В заключение взгляните на спроектированную
(рис 24.12) Форма с меню и инструментальной панелью
Drawing, хотя Drawing2D.
Объекты этого CreateGraphics Control - наследника Form - возвращает объект, ассоциированный с выводом
При
Graphics graph;
а в
graph = CreateGraphics();
Затем всюду в программе, где нужно работать с graph и его методы. Есть другой способ получения этого объекта - обработчики некоторых событий получают объект
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics gr = e.Graphics;
//перерисовка, использующая методы объекта gr
}
Для получения этого объекта можно использовать и статические методы самого
У
Для DrawString, позволяющий выводить Draw - DrawEllipse, DrawLine, DrawArc и другие, позволяющие цветным пером (объектом Fill - FillEllipse, FillPie, FillRectangle и другие, позволяющие
Методам группы Draw Color. Для выбора подходящего цвета можно использовать упоминавшееся выше диалоговое окно Color либо одно из многочисленных статических свойств Color, возвращающее требуемый цвет. Возможно и непосредственное задание элементов структуры в виде комбинации RGB - трех цветов - красного, зеленого и голубого. Вместо создания нового пера с помощью
SolidBrush - для сплошной закраски области заданным цветом;TextureBrush - для закраски области заданной картинкой (image);HatchBrush - для закраски области предопределенным узором;LinearGradientBrush - для сплошной закраски с переходом от одного цвета к другому, где изменение оттенков задается линейным градиентом;PathGradientBrush - для сплошной закраски с переходом от одного цвета к другому, где изменение оттенков задается более сложным путем.Первые два System.Drawing, остальные - в System.Drawing.Drawing2D.
У каждого из этих
Построим проект для
(рис 24.13) Кнопочная форма "кисть или перо"Выбор соответствующей командной
В
Прежде чем рассмотреть программный код, давайте посмотрим, как выглядят нарисованные программой
(рис 24.14) Паутина БезьеПерейдем к рассмотрению кода. Первым делом добавим в поля
//fields Point center; Point[] points = new Point[10]; Pen pen; Graphics graph; int count;
Точка center будет задавать общую начальную точку для всех рисуемых points будет задавать остальные точки, используемые при построении pen и graph, необходимых при count играет техническую роль, о которой скажу чуть позже, прямого отношения к
В MyInit, инициализирующий введенные объекты:
void MyInit()
{
int cx = ClientSize.Width;
int cy = ClientSize.Height;
points[0] = new Point(0,0);
points[1] = new Point(cx/2,0);
points[2] = new Point(cx,0);
points[3] = new Point(0,cy/2);
points[4] = new Point(cx,cy/2);
points[5] = new Point(0,cy);
points[6] = new Point(cx/2,cy);
points[7] = new Point(cx,cy);
points[8] = new Point(0,0);
points[9] = new Point(cx/2,0);
graph = this.CreateGraphics();
center = new Point(cx/2,cy/2);
count =1;
}
DrawWeb, устроенном очень просто. В цикле рисуется 8 кривых, используя точку center и массив points:
void DrawWeb()
{
for (int i = 0; i<8; i++)
graph.DrawBezier(pen,center,points[i],points[i+2],
points[i+1]);
}
Метод DrawBezier, вызываемый объектом graph Draw. Первым аргументом у всех этих методов является объект
Главный вопрос, требующий решения: где же вызывать сам метод DrawWeb, где инициализировать center будет следовать за курсором мыши, то естественно, чтобы MouseMove нашей BezierWeb. (Напомню, для подключения события
Вот текст обработчика этого события:
private void BezierWeb_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
pen = SystemPens.Control;
DrawWeb();
center.X = e.X; center.Y = e.Y;
//pen = new Pen(Color.Aquamarine);
pen = SystemPens.ControlText;
DrawWeb();
}
Метод DrawWeb вызывается дважды - первый раз с пером цвета фона, другой - с цветом, принятым системой для отображения текста. Обратите внимание, для создания нужного пера в данном случае не вызывается pen с помощью
Перед center получает координаты курсора мыши, передаваемые аргументом
Вызов метода DrawWeb добавлен еще и в
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
pen = SystemPens.ControlText;
DrawWeb();
Debug.WriteLine(count++);
}
Говоря о Invalidate - во всех этих случаях требуется перерисовать область. Тогда-то и возникает count, введенная нами, позволяет в режиме отладки подсчитывать число вызовов
MouseMove. Вместо этого переопределяют родительский метод OnPaint. (Как переопределяются родительские методы группы On, занимающиеся обработкой событий, другие методы OnPaint ). В результате появится заготовка для переопределения метода.)
В данном контексте перерисовка сводится, как это обычно делается, к вызову метода, выполняющего
Закончим на этом с
Создадим в нашем проекте новую RandomShapes, в которой будем рисовать и закрашивать Click. При каждом щелчке
На рис. 24.15 показана
А теперь приведем программный код, реализующий
//fields int cx,cy; Graphics graph; Brush brush; Color color; Random rnd;
Инициализация полей производится в методе MyInit, вызываемом
(рис 24.15) Рисование кистями разного типаvoid MyInit()
{
cx = ClientSize.Width;
cy = ClientSize.Height;
graph = CreateGraphics();
rnd = new Random();
}
Рассмотрим теперь основной метод, реализующий
void DrawShapes()
{
for(int i=0; i<3; i++)
{
//выбирается цвет - красный, желтый, голубой
int numcolor = rnd.Next(3);
switch (numcolor)
{
case 0:
color = Color.Blue; break;
case 1:
color = Color.Yellow; break;
case 2:
color = Color.Red; break;
}
//градиентной кистью рисуется эллипс,
//местоположение случайно
Point top = new Point(rnd.Next(cx), rnd.Next(cy));
Size sz = new Size(rnd.Next(cx-top.X), rnd.Next(cy-top.Y));
Rectangle rct = new Rectangle(top, sz);
Point bottom = top + sz;
brush = new LinearGradientBrush(top, bottom,
Color.White,color);
graph.FillEllipse(brush,rct);
//сплошной кистью рисуется сектор,
//местоположение случайно
top = new Point(rnd.Next(cx), rnd.Next(cy));
sz = new Size(rnd.Next(cx-top.X), rnd.Next(cy-top.Y));
rct = new Rectangle(top, sz);
brush = new SolidBrush(color);
graph.FillPie(brush,rct,30f,60f);
//узорной кистью рисуется прямоугольник,
//местоположение случайно
top = new Point(rnd.Next(cx), rnd.Next(cy));
sz = new Size(rnd.Next(cx-top.X), rnd.Next(cy-top.Y));
rct = new Rectangle(top, sz);
HatchStyle hs = (HatchStyle)rnd.Next(52);
brush = new HatchBrush(hs,Color.White, Color.Black);
graph.FillRectangle(brush,rct);
}
}
Приведу некоторые комментарии в дополнение к тем, что встроены в текст метода. Здесь многое построено на работе со случайными числами. Случайным образом выбирается один из возможных цветов для
brush = new LinearGradientBrush(top, bottom, Color.White,color);
то нужно в
Наиболее просто задается сплошная кисть:
brush = new SolidBrush(color);
Для нее достаточно указать только цвет. Для узорной кисти нужно задать предопределенный тип узора, всего их возможно 52. В нашем примере тип узора выбирается случайным образом:
HatchStyle hs = (HatchStyle)rnd.Next(52); brush = new HatchBrush(hs,Color.White, Color.Black);
Помимо первого аргумента, задающего тип узора, указываются еще два цвета - первый определяет цвет повторяющегося элемента, второй - цвет границы между элементами узора.
Непосредственное Fill:
graph.FillEllipse(brush,rct); graph.FillPie(brush,rct,30f,60f); graph.FillRectangle(brush,rct);
Первый аргумент всегда задает кисть, а остальные зависят от типа рисуемой фигуры. Как правило, всегда задается прямоугольник, ограничивающий данную фигуру.
Вызов метода DrawShapes, как уже говорилось, встроен в Click RandomShapes:
private void RandomShapes_Click(object sender, System.EventArgs e)
{
DrawShapes();
}
На этом поставим точку в рассмотрении данной темы. По сути, этим завершается и наш учебный курс. В последней лекции будет рассмотрен некоторый заключительный проект.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.