КАТЕГОРИИ: Архитектура-(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; Просмотров: 592; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |