Графически часы в C#
Элемент DateTimePicker позволяет выбирать даты и диапазоны дат, но нет стандартного элемента управления, который позволяет вам выбирать часы. В этом примере показан один из способов сделать это с помощью PictureBox.
Большая часть этой программы - это метод DrawHours, показанный в следующем коде. Он набирает выбранные часы.
// Выбранные часы. private int StartHour = 6; private int StopHour = 18; // Нарисуем индикатор часа на этом PictureBox. private void DrawHours(PictureBox pic, Graphics gr, int start_hour, int stop_hour) { gr.Clear(Color.LightGreen); // Масштабировать в соответствии с 24-часовым периодом. const int margin = 3; float scale_x = XScale(pic); float hgt = pic.ClientSize.Height; float y1 = margin; float y2 = hgt - margin; // Нарисуем выбранный временной диапазон. RectangleF hours_rect = new RectangleF( start_hour * scale_x, y1, (stop_hour - start_hour) * scale_x, y2 - y1); gr.FillRectangle(Brushes.Blue, hours_rect); // Рисуем метки. float x = 0; for (int i = 0; i <= 24; i++) { gr.DrawLine(thin_pen, x, 0, x, hgt); x += scale_x; } // Время рисования. gr.RotateTransform(-90); int xmid = -pic.ClientSize.Height / 2; using (Font font = new Font(FontFamily.GenericSansSerif, 12)) { using (StringFormat sf = new StringFormat()) { sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; x = 0; for (int i = 0; i <= 24; i++) { gr.DrawString(HourToString(i), font, Brushes.Black, xmid, x, sf); x += scale_x; } } } }
Этот метод очищает PictureBox светло-зеленым цветом. Затем он вычисляет коэффициент масштабирования по горизонтали scale_x для использования при рисовании и использует его для заполнения прямоугольника, представляющего выбранные часы синим цветом. Затем код пеет более 25 часов (от 0 до 24), добавляя отметки, чтобы показать, где часы находятся на чертеже.
Затем метод добавляет поворот на 90 градусов (90 градусов против часовой стрелки) к объекту Graphics, чтобы повернуть будущий рисунок. Он заканчивается, повторяя часы, чтобы нарисовать имя каждого часа. Он использует следующий метод HourToString для преобразования числа часов в его имя.
// Возвращает час, отформатированный так, как мы хотим его отобразить. private string HourToString(int hour) { if (hour == 0) return "midnight"; if (hour == 12) return "noon"; if (hour == 24) return "midnight"; if (hour <= 12) return hour.ToString() + "am"; return (hour - 12).ToString() + "pm"; }
Метод HourToString просто возвращает строку, соответствующую часовому числу.
В следующем коде показан метод XScale, который программа использует для вычисления коэффициента масштабирования по горизонтали.
// Получите коэффициент масштабирования по горизонтали. private float XScale(PictureBox pic) { return pic.ClientSize.Width / 24; }
Масштабный коэффициент - это только ширина клиентской области элемента управления PictureBox, деленная на количество часов, которые программа рисует.
Остальная часть интересного кода обрабатывает события мыши, которые позволяют пользователю выбирать часы. Следующий процесс MouseDown запускает процесс.
// Обработать события мыши. private bool Drawing = false; private int DrawingStartHour, DrawingStopHour; private void picHours_MouseDown(object sender, MouseEventArgs e) { Drawing = true; DrawingStartHour = (int)Math.Round(e.X / XScale(picHours)); DrawingStopHour = DrawingStartHour; StartHour = DrawingStartHour; StopHour = DrawingStartHour; picHours.Refresh(); }
Обработчик событий устанавливает Drawing = true, чтобы указать, что пользователь нажал кнопку мыши. Он делит положение X мыши на коэффициент масштабирования по горизонтали, чтобы увидеть, какой час будет выбран, округление до ближайшего часа. Обработчик события сохраняет час в переменных DrawingStartHour, DrawingStopHour, StartHour и StopHour и обновляет PictureBox, чтобы сделать его перерисовкой.
В следующем коде показан обработчик события MouseMove.
private void picHours_MouseMove(object sender, MouseEventArgs e) { if (!Drawing) return; // Вычислить значение и отобразить всплывающую подсказку. int hour = (int)Math.Round(e.X / XScale(picHours)); string new_tip = HourToString(hour); if (tipHour.GetToolTip(picHours) != new_tip) tipHour.SetToolTip(picHours, new_tip); // Сохраним новое значение. DrawingStopHour = hour; // Перерисовать. if (DrawingStartHour < DrawingStopHour) { StopHour = DrawingStopHour; StartHour = DrawingStartHour; } else { StartHour = DrawingStopHour; StopHour = DrawingStartHour; } picHours.Refresh(); }
Этот код получает час под мышью так же, как это делает обработчик событий MouseDown. Он вызывает HourToString, чтобы получить имя часа и сравнивает его с текущей подсказкой элемента управления PictureBox. Если имя отличается от текущей всплывающей подсказки, код устанавливает всплывающую подсказку в новое имя часа. (Вы можете удалить это, если подсказка вас раздражает. Я нахожусь на заборе на этом.)
Затем код сохраняет час в DrawingStopHour. Затем он устанавливает StartHour и StopHour, поэтому StartHour <= StopHour. (Если вы этого не сделаете, метод DrawHours ранее неправильно рисует выбранные часы, потому что он пытается нарисовать RectangleF с отрицательной шириной и Graphics не будет делать этого.)
Наконец, метод обновляет PictureBox, чтобы сделать его перерисовкой.
Последним интересным фрагментом кода является следующий обработчик событий MouseUp.
private void picHours_MouseUp(object sender, MouseEventArgs e) { if (!Drawing) return; tipHour.SetToolTip(picHours, ""); Drawing = false; DisplayTimes(); }
Этот обработчик событий просто очищает всплывающую подсказку элемента PictureBox и вызывает следующий метод DisplayTimes, чтобы отобразить выбранный временной диапазон в текстовых окнах.
// Показывать время в TextBoxes. private void DisplayTimes() { DateTime start_time = new DateTime(2000, 1, 1, StartHour, 0, 0); DateTime stop_time = new DateTime(2000, 1, 1, StopHour, 0, 0); txtStartTime.Text = start_time.ToShortTimeString(); txtStopTime.Text = stop_time.ToShortTimeString(); }
Этот метод преобразует выбранные времена в значения DateTime, а затем использует их метод ToShortTimeString для получения хорошо отформатированного вывода.