Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Другие элементы меню 28 страница




Как переопределяются родительские методы группы On, занимающиеся обработкой событий, другие методы классов родителей и базовых интерфейсов? В режиме проектирования в окне классов, отражающем структуру класса, нужно выбрать соответствующий класс (в нашем случае класс формы BezierWeb), раскрыть узел BasesAndInterfaces этого класса и из появившегося списка всех наследованных свойств и методов выбрать нужный (в нашем случае метод OnPaint). В результате появится заготовка для переопределения метода.

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

Закончим на этом с рисованием пером и перейдем к рассмотрению рисования кистью.

Кисти и краски

Создадим в нашем проекте новую форму RandomShapes, в которой будем рисовать и закрашивать геометрические фигуры трех разных типов – эллипсы, сектора, прямоугольники. Для каждого типа фигуры будем использовать свой тип кисти: эллипсы будем закрашивать градиентной кистью, сектора ­– сплошной, а прямоугольники – узорной. Цвет фигуры, ее размеры и положение будем выбирать случайным образом. Рисование фигур будет инициироваться в обработчике события Click. При каждом щелчке кнопкой мыши на форме будут рисоваться три новых экземпляра фигур каждого типа. В отличие от кривых Безье старые фигуры стираться не будут.

На рис. 24. 15 показана форма после нескольких щелчков кнопки мыши. Конечно, черно-белый рисунок в книге не может передать цвета, особенно смену оттенков для градиентной кисти. На экране дисплея или цветном рисунке все выглядит красивее.

<Рис. 24.15. Рисование кистями разного типа>

А теперь приведем программный код, реализующий рисование. Начнем, как обычно, с полей класса:

//fields

int cx,cy;

Graphics graph;

Brush brush;

Color color;

Random rnd;

Инициализация полей производится в методе MyInit, вызываемом конструктором класса:

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();

}

На этом поставим точку в рассмотрении данной темы. По сути этим завершается и наш учебный курс. В последней лекции будет рассмотрен некоторый заключительный проект.

Вариант 1

67. Объект класса Graphics можно создать:

q конструктором класса Graphics;

q статическим методом класса Graphics;

q используя статическое свойство класса Form;

q методом CreateGraphics;

q используя аргумент, переданный обработчику события.

68. Отметьте истинные высказывания:

q модальное окно нельзя покинуть, не закрыв его;

q при рисовании цвет рисунка может быть выбран только из фиксированного набора цветов;

q с пунктом меню всегда связан обработчик события Click;

q существует пять различных классов перьев;

q класс Menu является абстрактным классом.

69. Классы Control, Form и ListBox связаны следующими отношениями наследования:

q класс Control является наследником класса Form;

q класс Form является наследником класса Control;

q класс ListBox является наследником класса Form;

q класс ListBox является наследником класса Control;

q классы Control и Form не связаны отношением наследования.

Вариант 2

71. Объект, задающий кисть, можно получить:

q конструктором класса Brush;

q методом CreateBrush класса Control;

q конструктором класса PathGradientBrush;

q конструктором класса SolidBrush;

q используя аргумент, переданный обработчику события.

72. Отметьте истинные высказывания:

q при проектировании формы указывается, будет ли она модальной или немодальной;

q класс Color имеет фиксированное число статических свойств, задающих различные цвета;

q закрытие формы и скрытие формы – это разные понятия;

q каждая кнопка на инструментальной панели выполняет одну из команд меню;

q толщина пера и ширина пера – это разные понятия.

73. Элемент управления ListBox:

q может хранить объекты;

q хранит только строки;

q может отображать объекты;

q отображает только строки;

q позволяет задавать элементы на этапе проектирования.

Вариант 3

68. При наследовании форм:

q наследуемая форма должна принадлежать тому же проекту, что и родительская форма;

q родительская форма может принадлежать любому проекту;

q родительская форма может принадлежать только exe или dll проектам;

q наследуемая форма не может быть открыта в дизайнере форм;

q обработчики событий не наследуются.

69. Отметьте истинные высказывания:

q метод DrawString предназначен для вывода текста в графическом режиме;

q кисть класса LinearGradientBrush предназначена для рисования линий разного цвета;

q контекстное меню может быть связано как с формой, так и с элементом управления;

q любую форму можно открыть как модальную;

q новые кисти создаются конструктором класса Brush.

70. Для форм справедливы следующие утверждения:

q действие методов Hide и Show эквивалентно установке свойства Visible в false или true;

q действия методов Hide и Close эквивалентны;

q выполнение метода Close для главной формы приводит к завершению приложения;

q все формы могут быть скрыты, а приложение может продолжать выполняться;

q выполнение метода Close для любой формы приводит к завершению приложения.

 

Лекция 25. Финальный проект

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

В финальном проекте создается семейство классов, описывающих геометрические фигуры. Проектирование начинается с абстрактного класса поведения, описывающего общие свойства и методы, характерные для всех фигур семейства. Затем, используя наследование, создаются классы конкретных геометрических фигур, начиная с простейших, таких как круги и прямоугольники, до составных, таких как класс Person. Мы добавим в наш проект динамические структуры данных, такие как список с курсором, для хранения в нем фигур семейства. Наконец, мы создадим интерфейс, включающий меню с десятками команд и панель с инструментальными кнопками. Интерфейс позволяет конечному пользователю выполнять различные действия над геометрическими фигурами – создавать, рисовать их на форме, перемещать их с помощью команд и перетаскивать их мышью, менять их размеры и цвет, сохранять в списке и удалять из списка, отображать все фигуры списка или очистить его полностью.

Проект может служить образцом полноценного Windows-приложения, примером проектирования в классах с демонстрацией преимуществ, предоставляемых наследованием. Закончим на этом рекламную часть и приступим к делу. Хочу предупредить, вас ждут программные тексты, почти без всяких комментариев. Все нужные комментарии были даны в предыдущих лекциях. С моей точки зрения наиболее интересная часть программистских книжек – это та, в которой приводится программный код. С этих позиций эта глава самая интересная.

Абстрактный класс Figure

Приведем код класса:

using System;

using System.Drawing;

 

namespace Shapes

{

/// <summary>

/// Figure - это абстрактный класс;

/// прародитель семейства классов геометрических фигур.

/// Все фигуры имеют:

/// центр - center,

/// масштаб - scale.

/// статус перетаскивания - dragged

/// center - объект встроенного класса (структуры) Point.

/// Этот объект задает характерную точку фигуры - чаще всего ее центр (тяжести)

/// scale задает масштаб фигуры, первоначально единичный.

/// drugged = true, когда фигура следует за курсором мыши.

/// над фигурами определены операции:

/// параллельный перенос - Move(a,b)

/// масштабирование - Scale(s)

/// Показ фигуры - Show

/// Область захвата - Region_Capture

/// возвращает прямоугольник, характерный для фигуры,

/// перетаскивание фигуры возможно при установке курсора мыши в области захвата.

/// </summary>

 

abstract public class Figure

{

/// <summary>

/// закрытые для клиентов атрибуты класса - center, scale

/// </summary>

protected Point center;

protected double scale;

protected bool dragged;

protected Color color;

 

//Доступ к свойствам

public Point center_figure

{

get { return (center);}

set {center = value;}

}

public double scale_figure

{

get { return (scale);}

set {scale = value;}

}

public bool dragged_figure

{

get { return (dragged);}

set {dragged = value;}

}

public Color color_figure

{

get { return (color);}

set {color = value;}

}

/// <summary>

/// базовый конструктор фигур

/// </summary>

/// <param name="x"> координата X характерной точки фигуры </param>

/// <param name="y"> Координата Y характерной точки фигуры </param>

public Figure(int x, int y)

{

center = new Point(x,y);

scale = 1;

dragged = false;

color = Color.ForestGreen;

}

 

/// <summary>

/// отложенный метод

/// Параллельный перенос фигуры на (a,b)

/// require: true;

/// ensure: для любой точки фигуры p(x,y):

/// x = old(x) +a; y = old(y) + b;

/// </summary>

/// <param name="a"> a - перемещение по горизонтали вправо </param>

/// <param name="b"> b - перемещение по вертикали вниз </param>

/// Замечание: Для того, чтобы фигура при рисовании была полностью видимой,

/// координаты всех ее точек должны быть в пределах области рисования.

public void Move (int a, int b)

{

center.X +=a; center.Y += b;

}

 

/// <summary>

/// изменяет масштаб фигуры

/// </summary>

/// <param name="s"> масштаб изменяется в s раз </param>

public void Scale(double s)

{

scale*=s;

}

 

/// <summary>

/// рисование фигуры в окне, передающем объекты g и pen

/// </summary>

/// <param name="g"> графический объект, методы которого рисуют фигуру </param>

/// <param name="pen"> перо рисования </param>

public abstract void Show(Graphics g, Pen pen, Brush brush);

public abstract System.Drawing.Rectangle Region_Capture();

}

}

Абстрактный класс, относящийся к этапу проектирования системы, вместе с тем является важнейшим элементом заключительного семейства классов. В этом проявляется мощь объектно-ориентированного подхода к разработке программных систем. Заметьте, на этом уровне большая часть текста представляет документацию, являющуюся неотъемлемой частью программного проекта. Документация записана в тегах summary, что позволяет автоматически ее извлечь и сохранить в виде XML-отчета.

Классы семейства геометрических фигур

Приведем теперь программные коды классов, являющихся потомками класса Figure.

Класс Ellipse

Вот программный код этого класса:

using System;

using System.Drawing;

namespace Shapes

{

/// <summary>

/// Класс Ellipse - потомок класса Figure.

/// </summary>

public class Ellipse: Figure

{

int axisA,axisB;

Rectangle rect;

public Ellipse(int A, int B, int x, int y): base (x,y)

{

axisA = A; axisB = B;

rect =Init();

}

 

public override void Show(Graphics g, Pen pen, Brush brush)

{

rect = Init();

g.DrawEllipse(pen,rect);

g.FillEllipse(brush, rect);

}

public override Rectangle Region_Capture()

{

rect = Init();

return rect;

}

Rectangle Init()

{

int a =Convert.ToInt32(axisA*scale);

int b =Convert.ToInt32(axisB*scale);

int leftupX = center.X - a;

int leftupY = center.Y - b;

return (new Rectangle(leftupX,leftupY,2*a,2*b));

}

}

}

Класс Circle

Этот класс является потомком класса Ellipse:

using System;

using System.Drawing;

 

namespace Shapes

{

/// <summary>

/// Класс Circle - потомок класса Ellipse.

/// </summary>

public class Circle: Ellipse

{

public Circle(int radius, int x, int y): base (radius,radius,x,y)

{

//Круг - это эллипс с равными полуосями (радиусом круга)

}

}

}

Здесь, опять-таки проявляется мощь наследования. Потомок наследует все свойства и методы родителя. Ему остается только указать собственный конструктор объектов класса, да и в нем главное состоит в вызове конструктора родительского класса с передачей ему нужных аргументов.

Класс LittleCircle

Этот класс, задающие маленькие кружочки фиксированного радиуса, в свою очередь является наследником класса Circle. Устроен он также чрезвычайно просто:

using System;

 

namespace Shapes

{

/// <summary>

/// Класс LittleCircle- потомок класса Circle.

/// </summary>

public class LittleCircle:Circle

{

public LittleCircle(int x, int y): base (4,x,y)

{

// маленький круг радиуса 4

}

}

}

Класс Rect

Этот класс является еще одним прямым потомком класса Figure:

using System;

using System.Drawing;

 

namespace Shapes

{

/// <summary>

/// Класс Rect - потомок класса Figure.

/// </summary>

public class Rect:Figure

{

int sideA, sideB;

Rectangle rect;

public Rect(int sideA, int sideB, int x, int y): base (x,y)

{

this. sideA = sideA; this. sideB = sideB;

rect =Init();

}

 

public override void Show(Graphics g, Pen pen, Brush brush)

{

rect = Init();

g.DrawRectangle(pen,rect);

g.FillRectangle(brush,rect);

}

public override Rectangle Region_Capture()

{

rect = Init();

return rect;

}

Rectangle Init()

{

int a =Convert.ToInt32(sideA*scale);

int b =Convert.ToInt32(sideB*scale);

int leftupX = center.X - a/2;

int leftupY = center.Y - b/2;

return (new Rectangle(leftupX,leftupY,a,b));

}

}

}

Класс Square

Квадрат – это частный случай прямоугольников. Соответствующий класс является потомком класса Rect:

using System;

 

namespace Shapes

{

/// <summary>

/// Класс Square - потомок класса Rect.

/// </summary>

public class Square:Rect

{

public Square(int side, int x, int y): base (side,side,x,y)

{

//квадрат - это прямоугольник с равными сторонами

}

}

}

Класс Person

Этот класс является прямым потомком класса Figure. Вместе с тем класс является клиентом трех других классов семейства – Circle, Rect и LittleCircle, поскольку элементы фигуры, составляющие человечка, являются объектами этих классов.

namespace Shapes

{

/// <summary>

/// Класс Person - потомок класса Figure,

/// клиент классов Circle, Rect, LittleCircle.

/// </summary>

public class Person:Figure

{

int head_h;

Circle head;

Rect body;

LittleCircle nose;

public Person(int head_h, int x, int y): base(x,y)

{

//head_h - радиус головы, x,y - ее центр.

//остальные размеры считаются относительно размера головы.

this.head_h = head_h;

head = new Circle(head_h,x,y);

int body_x = x;

int body_y = y + 3*head_h;

int body_w =2*head_h;

int body_h = 4*head_h;

body = new Rect(body_w, body_h, body_x,body_y);

nose = new LittleCircle(x+head_h +2, y);

}

 

public override void Show(System.Drawing.Graphics g, System.Drawing.Pen pen, System.Drawing.Brush brush)

{

int h = Convert.ToInt32(head_h*scale);

//head

int top_x = center.X - h;

int top_y = center.Y - h;

g.DrawEllipse(pen, top_x,top_y, 2*h,2*h);

g.FillEllipse(brush, top_x,top_y, 2*h,2*h);

//body

top_y += 2*h;

g.DrawRectangle(pen, top_x,top_y, 2*h,4*h);

g.FillRectangle(brush, top_x,top_y, 2*h,4*h);

//nose

top_y -=h;

top_x += 2*h;

g.DrawEllipse(pen, top_x,top_y, 8,8);

g.FillEllipse(brush, top_x,top_y, 8,8);

}

public override System.Drawing.Rectangle Region_Capture()

{

int h = Convert.ToInt32(head_h*scale);

int top_x = center.X - h;

int top_y = center.Y - h;

return new System.Drawing.Rectangle(top_x,top_y,2*h,2*h);

}

}

}

Список с курсором. Динамические структуры данных

Добавим в проект классы, задающие динамические структуры данных. Конечно, можно было бы воспользоваться стандартными классами. Но для обучения крайне полезно уметь создавать собственные классы, задающие такие структуры данных. Список с курсором – один из важнейших образцов подобных классов.

using System;

namespace Shapes

{

/// <summary>

/// Класс TwoWayList(G) описывает двусвязный список с курсором.

/// Элементами списка являются объекты TwoLinkable,

/// хранящие помимо указателей на двух преемников объекты типа G.

/// Курсор будет определять текущий (активный) элемент списка.

/// Класс будет определять симметричные операции по отношению к курсору.

/// Конструкторы:

/// Конструктор без параметров будет создавать пустой список

/// Запросы:

/// empty: require: true; возвращает true для непустого списка

/// item: require: not empty(); возвращает активный элемент типа G;

/// count: require: true; возвращает число элементов списка; count in[0,n]

/// (count == 0) eqviv empty();

/// index: require: not empty(); возвращает индекс активного элемента.

/// search_res: require: true; возвращает true,

/// если последний поиск был успешным.

/// Команды:

/// put_left(elem): require: true;

/// ensure: добавить новый элемент (elem) слева от курсора;

/// put_right(elem): require: true;

/// ensure: добавить новый элемент (elem) справа от курсора;

/// remove: require: not empty();

/// ensure: удалить активный элемент;

/// особо обрабатывается удаление последнего и единственного элементов

/// операции с курсором:

/// start: require: true;

/// ensure: сделать активным первый элемент;

/// finish: require: true;

/// ensure: сделать активным последний элемент;

/// go_prev: require: not (index = 1);

/// ensure: сделать активным предыдущий элемент;

/// go_next: require: not (index = count);

/// ensure: сделать активным последующий элемент;

/// go_i(i): require: (i in [1, count]);

/// ensure: сделать активным элемент с индексом i;

/// операции поиска:

/// search_prev(elem): require: not (index = 1);

/// ensure: сделать активным первый элемент elem слева от курсора;

/// успех или неуспех поиска сохранять в булевской переменной search_res

/// search_next: require: not (index = count);

/// ensure: сделать активным первый элемент elem справа от курсора;

/// успех или неуспех поиска сохранять в булевской переменной search_res

///

/// </summary>

public class TwoWayList

{

public TwoWayList()

{

first = cursor = last = null;

count = index = 0;

search_res = false;

}// конструктор

///<summary>

/// first, cursor,last - ссылки на первый, активный и последний элементы списка

/// Запросы count, index search_res также реализуются атрибутами.

/// Запросы empty, item реализуются функциями

///</summary>

protected TwoLinkable first, cursor, last;

protected int count, index;

protected bool search_res;

 

//доступ на чтение к закрытым свойствам;

public int Count

{




Поделиться с друзьями:


Дата добавления: 2014-12-25; Просмотров: 567; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.279 сек.