Модификация приложения Windows Forms : вкладка «Векторные часы»



 

Данная вкладка отображает текущее время на стилизованном «аналоговом» циферблате (без использования изображений и сторонних ресурсов). В ней использован большой набор примитивов: прямоугольники, эллипсы и отрезки прямых. Для расчёта их координат используются не геометрические преобразования GDI+, а простые вычисления. Что действительно заслуживает внимания, так это отображение стрелок часов. Каждая стрелка рисуется всего одним вызовом метода DrawLine!

 

Для часиков создаём четвёртую вкладку tabPage 4 со свойствами:

 

TabPage:

Text: Векторные часы
Cursor: AppStarting

 

Добавляем на форму таймер (Timer) со свойствами:

 

Timer:

(Name): timer1
Interval: 1000

 

Инициализируем событие первого таймера Tick:

 

   private void timer1_Tick(object sender, EventArgs e)

   {

       tabPage4.Text = "Векторные часы: " + DateTime.Now.ToLongTimeString();

       Invalidate();

   }

 

Инициализируем событие Selected для главного элемента TabControl со следующим кодом:

 

  private void TB_Main_Selected(object sender, TabControlEventArgs e)

   {

       if (e.TabPage.Name == tabPage4.Name) timer1.Enabled = true;

       else timer1.Enabled = false;

   }

 

Этот код нужен вот для чего: если выбираем активной вкладкой, вкладку номер четыре, то запускаем таймер.

 

Инициализируем событие загрузки формы Load:

 

   private void LWP19Main_Load(object sender, EventArgs e)

   {

       SetStyle(ControlStyles.AllPaintingInWmPaint, true);

       SetStyle(ControlStyles.DoubleBuffer, true);

       SetStyle(ControlStyles.UserPaint, true);

   }

 

И, наконец, инициализируем событие Paint вкладки tabPage4, а также дополнительный метод:

 

   private Point RadialPoint(int radius, int seconds)

  {

       Point ptCenter = new Point(tabPage4.ClientRectangle.Width / 2, tabPage4.ClientRectangle.Height / 2);

       double angle = -((seconds - 15) % 60) * Math.PI / 30; // Вычисляем угол на "окружности" (шаг)

       Point ret = new Point(

           ptCenter.X + (int)(radius * Math.Cos(angle)),

           ptCenter.Y - (int)(radius * Math.Sin(angle)));

       return ret;

   }

 

   private void tabPage4_Paint(object sender, PaintEventArgs e)

   {

       // Создаём объект часов

       DateTime dt = DateTime.Now;

       Graphics g = e.Graphics;

       g.SmoothingMode = SmoothingMode.HighQuality;

       // Вычисляем центральную точку для циферблата

       Point ptCenter = new Point(tabPage4.ClientRectangle.Width / 2, tabPage4.ClientRectangle.Height / 2);

       // Вычисляем радиус циферблата на основе размеров вкладки

       int radius = Math.Min(tabPage4.ClientRectangle.Width, tabPage4.ClientRectangle.Height) / 2;

       using (LinearGradientBrush br = new LinearGradientBrush(tabPage4.ClientRectangle, Color.White, Color.Azure, LinearGradientMode.BackwardDiagonal))

       {

           //br.SetSigmaBellShape(.5f, 1.0f);

           // Заполняем циферблат градиентом

           g.FillEllipse(br, ptCenter.X - radius, ptCenter.Y - radius, radius * 2, radius * 2);

       }

       // Рисуем эллипс цифербалата

       using (Pen pen = new Pen(Color.Black))

           g.DrawEllipse(pen, ptCenter.X - radius, ptCenter.Y - radius, radius * 2, radius * 2);

       // Цикл минуты

       for (int minute = 0; minute < 60; minute++)

       {

           Point pt = RadialPoint(radius - 10, minute);

           using (SolidBrush br = new SolidBrush(Color.Black))

           {

               if ((minute % 5) == 0)

                   g.FillRectangle(br, pt.X - 3, pt.Y - 3, 6, 6);

               else

                   g.FillRectangle(br, pt.X - 1, pt.Y - 1, 2, 2);

           }

       }

 

       // Рисуем стрелку часов (от центра до позиции на циферблате)

       using (Pen pen = new Pen(Color.Black, 8))

       {

           pen.StartCap = LineCap.Flat;

           pen.EndCap = LineCap.DiamondAnchor;

           float[] compVals = new float[] { 0.0f, 0.2f, 0.5f, 0.7f, 0.9f, 1.0f };

           pen.CompoundArray = compVals;

           g.DrawLine(pen, RadialPoint(15, 30 + dt.Hour * 5 + dt.Minute / 12), RadialPoint((int)(radius * 0.75), dt.Hour * 5 + dt.Minute / 12));

       }

 

       // Рисуем минутную стрелку

       using (Pen pen = new Pen(Color.FromArgb(100, 0, 0, 0), 6))

       {

           pen.StartCap = LineCap.RoundAnchor;

           pen.EndCap = LineCap.Round;

           g.DrawLine(pen, RadialPoint(15, 30 + dt.Minute), RadialPoint((int)(radius * 0.8), dt.Minute));

       }

 

       // Русуем секундную стрелку

       using (Pen pen = new Pen(Color.FromArgb(50, 150, 50, 25), 4))

       {

           pen.CustomEndCap = new AdjustableArrowCap(4, 6, true);

           g.DrawLine(pen, RadialPoint(20, dt.Second + 30), RadialPoint(radius - 2, dt.Second));

       }

       using (SolidBrush br = new SolidBrush(Color.FromArgb(100, Color.Wheat)))

           g.FillEllipse(br, ptCenter.X - 5, ptCenter.Y - 5, 10, 10);

   }

 

Компилируем (Debug) и запускаем. Переходим на закладку «Векторные часы», чем запускаем таймер, заголовок вкладки будет отображать текущее время, а на самой вкладке видим круглые стрелочные часы собранные из примитивов. Мило!

 

8. Модификация приложения Windows Forms : вкладка «Огонь!!!»

 

На этот раз будем имитировать горение огня (стена огня). Для создания эффекта огня будем использовать генерацию случайных чисел каждую миллисекунду. Однако, отображать сам огонь будем не на вкладке TabControl, а в новой форме. Это связано с «некрасивым» обновлением вкладки при рисовании огня (возникает заметное мерцание).

 

Создаём новую вкладку со свойствами:

 

TabPage:

Text: Огонь!!!
Cursor: No

 

Добавляем новую форму с именем LWP 19 Fire и не меняем какие-либо свойства формы (ну, разве что значок). Добавить форму можно так: Проект -> Добавить форму Windows ... (а затем переименовать класс формы); либо использовать стандартное добавление: Проект -> Добавить новый элемент... (Ctrl+Shift+A) и далее указать Имя: LWP 19 Fire).

 

Редактируем код события Selected для TabControl. Добавляем туда строчки:

 

       if (e.TabPage.Name == tabPage5.Name)

       {

           tabPage5.BackColor = Color.Black;

           LWP19Fire Fire = new LWP19Fire();

           Fire.ShowDialog();

       }

 

Выбор данной вкладки («Огонь!!!») вызовет экземпляр модальной формы LWP 19 Fire.

 

Теперь редактируем код новой формы. Вначале добавим следующий код (в директивы using):

 

using System.Drawing.Imaging;

 

Теперь, найдём в коде формы строчки:

 

public partial class LWP19Fire : Form

{

 

Добавим после:

 

   // Начало: вкладка "Огонь!!!"

   private Bitmap buf;                                      // Буфер для графики

   private Random random = new Random(DateTime.Now.Millisecond); // Истоник данных (Random)

   private const int width = 640;

   private const int height = 320;

   // Конец: вкладка "Огонь!!!"

 

Метод LWP 19 Fire () редактируем так:

 

   public LWP19Fire()

   {

       InitializeComponent();

       // Инициализируем источник для последующего использования

       random = new Random();

       // Устанавливаем начальные параметры вкладки

       this.Text = "Расширенная работа с GDI+ (C#) :: форма для вкладки \"Огонь!!!\"";

       this.ClientSize = new Size(width, height);

       this.MaximizeBox = false;

       this.FormBorderStyle = FormBorderStyle.FixedDialog;

       this.BackColor = Color.Black;

       SetStyle(ControlStyles.Opaque, true);

       // Вызываем событие рисования (для метода DoFire)

       this.Paint += new PaintEventHandler(this.DoFire);

       // Генерируем палитру 640x480 (на основе Bimtap с 256 цветами на пиксель)

       buf = new Bitmap(width, height, PixelFormat.Format8bppIndexed);

       ColorPalette pal = buf.Palette;

       // Заполняем палитру, используя 64-цветные блоки:

       // чёрный с красным, красный с желтым, желтый с белым, белый.

       // Так как каждый диапазон составляет 64 цветов и RGB охватывает 256 значений,

       // использовуем левый сдвиг (<<) по массиву

       for (int i = 0; i < 64; i++)

       {

           pal.Entries[i] = Color.FromArgb(i << 2, 0, 0);

           pal.Entries[i + 64] = Color.FromArgb(255, i << 2, 0);

           pal.Entries[i + 128] = Color.FromArgb(255, 255, i << 2);

           pal.Entries[i + 192] = Color.FromArgb(255, 255, 255);

       }

       buf.Palette = pal;

   }

 

И добавим метод для переопределённого события Paint формы:

 

   // Метод рисования "огня"

   private void DoFire(object src, PaintEventArgs e)

   {

       // Блокируем растр, чтобы мы смогли записывать в него напрямую

       BitmapData buflock = buf.LockBits(

           new Rectangle(Point.Empty, buf.Size),

           ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);

        // Записываем "огонь"

       // Используем указатели, потому блок "unsafe"

       unsafe

       {

           // Извлекает указатель на верхнюю строку развёртки изображения

           Byte* bufdata = (Byte*)buflock.Scan0;

              Byte* bufbottom = bufdata + ((height - 1) * width);

           Byte* i; int v;

           // Используем случайную нижней линию в качестве источника "огня"

           for (int x = 0; x < width; x++)

           {

               *(bufbottom + x) = (Byte)random.Next(100, 255);

           }

           // Для каждого пикселя в изображении,

           // среднее значение пикселей, один справа, один снизу

           // и один в левом нижнем углу. Достигаем порог 0,

           // и пишем на текущей позиции

           for (i = bufdata; i < bufbottom; i++)

           {

               v = *i + *(i + 1) + *(i + height) + *(i + height - 1);

               v /= 4;

               if (v < 0) v = 0;

               *i = (Byte)v;

           }

       }

       // Разблокируем изображение и рисуем на его на форме

       buf.UnlockBits(buflock);

       e.Graphics.DrawImageUnscaled(buf, 0, 0);

       this.Invalidate();

   }

 

Компилируем (Debug) и запускаем. Переходим на закладку «Огонь!!!», любуемся на огонь (можно любоваться вечно, не правда ли?).

 


Дата добавления: 2019-09-13; просмотров: 203; Мы поможем в написании вашей работы!

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






Мы поможем в написании ваших работ!