Во-первых, нет смысла использовать три Timers
. Один Timer
может обрабатывать все три раза. Во-вторых, исходя из того, что вы опубликовали, нет смысла использовать Timer
. Единственная причина, по которой я мог видеть, что Timer
был бы полезен, - это постоянное отображение текущего истекшего времени в пользовательском интерфейсе, но вы этого не делаете. Повторно устанавливать эти String
переменные бессмысленно, если вы не собираетесь их отображать. Просто получите значение Elapsed
из соответствующего Stopwatch
, если и когда вам это нужно.
Что касается вашего обработчика событий Buttons'
Click
, это тоже ужасно. Весь смысл обычного обработчика событий в том, что вы хотите сделать одно и то же для каждого объекта, поэтому вы должны написать код только один раз. Если в итоге вы пишете отдельный код для каждого объекта в этом общем обработчике событий, то это лишает вас смысла и делает ваш код более сложным, а не меньшим. Вы должны использовать отдельные обработчики событий для каждого Button
.
Если вы собираетесь использовать go с общим обработчиком событий, хотя бы выньте общий код. У вас есть одинаковые два цикла For Each
во всех трех блоках Case
. Это должно быть сделано до Select Case
, а затем только запускать соответствующие Stopwatch
в каждом Case
.
Я не думаю, что вы должны использовать Buttons
. На самом деле вы должны использовать RadioButtons
. Вы можете установить для их свойства Appearance
значение Button
, и тогда они будут выглядеть как обычные Buttons
, но при этом вести себя как RadioButtons
. Когда вы нажимаете один, он сохраняет подавленное отображение, чтобы указать, что он отмечен, и если щелкнуть другой, будет выпущен ранее подавленный. В этом случае ваш код может выглядеть следующим образом:
Private ReadOnly driveStopwatch As New Stopwatch
Private ReadOnly waitStopwatch As New Stopwatch
Private ReadOnly walkStopwatch As New Stopwatch
Private Sub driveRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged
If driveRadioButton.Checked Then
driveStopwatch.Start()
Else
driveStopwatch.Stop()
End If
End Sub
Private Sub waitRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles waitRadioButton.CheckedChanged
If waitRadioButton.Checked Then
waitStopwatch.Start()
Else
waitStopwatch.Stop()
End If
End Sub
Private Sub walkRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles walkRadioButton.CheckedChanged
If walkRadioButton.Checked Then
walkStopwatch.Start()
Else
walkStopwatch.Stop()
End If
End Sub
Поскольку при проверке RadioButton
автоматически снимается любая другая, каждый обработчик CheckedChanged
должен беспокоиться только о своих Stopwatch
. * 1034. *
Если вы хотите отобразить прошедшее время для определенного Stopwatch
, когда оно останавливается, вы делаете это, когда оно останавливается, например,
Private Sub driveRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged
If driveRadioButton.Checked Then
driveStopwatch.Start()
Else
driveStopwatch.Stop()
driveLabel.Text = driveStopwatch.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
. Эта перегрузка TimeSpan.ToString
была впервые доступна в. NET 4.5 Я думаю, поэтому вам следует использовать его, если вы не нацеливаетесь. NET 4.0 или ранее.
Если вы хотите постоянно отображать текущее прошедшее время, то, как я уже сказал, вы только нужен один Timer
. Вы бы просто позволили ему работать все время и обновлялись соответствующим образом на основе Stopwatch
, который в данный момент запущен, например,
Private Sub displayTimer_Tick(sender As Object, e As EventArgs) Handles displayTimer.Tick
If driveStopwatch.IsRunning Then
driveLabel.Text = driveStopwatch.Elapsed.ToString("hh\:mm\:ss")
ElseIf waitStopwatch.IsRunning Then
waitLabel.Text = waitStopwatch.Elapsed.ToString("hh\:mm\:ss")
ElseIf walkStopwatch.IsRunning Then
walkLabel.Text = walkStopwatch.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
Вы не показали нам, как отображаете прошедшее время, так что это немного догадки В этом сценарии вы определенно должны обновлять Label
, когда Stopwatch
останавливается, потому что Timer
не будет обновлять Label
в следующие Tick
.
Вы, вероятно, захотите Button
где-нибудь, что может остановить и / или сбросить все три Stopwatches
. Это означало бы установить Checked
на False
на всех трех RadioButtons
и затем вызвать Reset
на всех трех Stopwatches
. Вы, вероятно, захотите очистить / сбросить Labels
тоже.
Существует также потенциальная ошибка, использующая RadioButtons
, подобную этой. Если один из ваших RadioButtons
является первым в порядке табуляции, он получит фокус по умолчанию при загрузке формы. Фокусировка на RadioButton
проверит его, так что это будет означать, что вы начнете Stopwatch
по умолчанию. Если это не то, что вам нужно, убедитесь, что какой-то другой элемент управления находится сначала в порядке табуляции. Если по какой-то причине вы не можете этого сделать, обработайте событие Shown
формы, установите ActiveControl
на Nothing
, снимите флажок с этого RadioButton
и сбросьте соответствующие Stopwatch
и Label
.
В качестве заключительного общего замечания обратите внимание, что я назвал все так, чтобы даже тот, кто не имел предварительных знаний о проекте, не сомневался, что все это было и для чего оно было. Такие имена, как SW
, SW1
и SW2
плохие. Даже если вы поняли, что SW
означает Stopwatch
, вы не представляете, для чего на самом деле каждый из них. В этот день Intellisense просто лениво использовать такие имена. Каждый опытный разработчик может рассказать вам историю о том, как вернуться к чтению собственного кода через некоторое время и не имея представления о том, что они подразумевают под различными вещами. Не попадайтесь в эту ловушку и убедитесь, что у вас хорошие привычки рано.
РЕДАКТИРОВАТЬ:
В качестве бонуса, вот способ, которым вы можете правильно использовать общий обработчик событий. Во-первых, определите пользовательский класс Stopwatch
, который имеет связанный Label
:
Public Class StopwatchEx
Inherits Stopwatch
Public Property Label As Label
End Class
После того, как вы создадите эту связь, вы автоматически узнаете, какой Label
использовать для отображения истекшего времени для Stopwatch
. Затем определите пользовательский класс RadioButton
, с которым связан Stopwatch
:
Public Class RadioButtonEx
Inherits RadioButton
Public Property Stopwatch As StopwatchEx
End Class
Далее, используйте этот пользовательский класс в своей форме вместо стандартного RadioButtons
. Вы можете добавить их прямо из панели инструментов (ваш пользовательский элемент управления будет добавлен автоматически после создания проекта) или вы можете отредактировать файл кода дизайнера и изменить тип элементов управления в коде. В последнем варианте есть определенный риск, поэтому обязательно создайте резервную копию заранее. Как только это будет сделано, измените тип вашего Stopwatches
и обработайте событие Load
формы, чтобы создать ассоциации:
Private ReadOnly driveStopwatch As New StopwatchEx
Private ReadOnly waitStopwatch As New StopwatchEx
Private ReadOnly walkStopwatch As New StopwatchEx
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Associate Stopwatches with RadioButtons
driveRadioButton.Stopwatch = driveStopwatch
waitRadioButton.Stopwatch = waitStopwatch
walkRadioButton.Stopwatch = walkStopwatch
'Associate Labels with Stopwatches
driveStopwatch.Label = driveLabel
waitStopwatch.Label = waitLabel
walkStopwatch.Label = walkLabel
End Sub
Теперь вы можете использовать один метод для обработки CheckedChanged
событие для всех трех RadioButtons
, потому что теперь вы можете сделать то же самое для всех трех из них:
Private Sub RadioButtons_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged,
waitRadioButton.CheckedChanged,
walkRadioButton.CheckedChanged
Dim rb = DirectCast(sender, RadioButtonEx)
Dim sw = rb.Stopwatch
If rb.Checked Then
sw.Start()
Else
sw.Stop()
sw.Label.Text = sw.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
RadioButton
, вызвавший событие, говорит вам, какой Stopwatch
использовать, и это говорит Вы должны Label
использовать, поэтому нет необходимости писать разные коды для каждого из них.
Обработчик событий Tick
Timer
также может обрабатывать каждый Stopwatch
с помощью общего кода:
Private Sub displayTimer_Tick(sender As Object, e As EventArgs) Handles displayTimer.Tick
For Each sw In {driveStopwatch, waitStopwatch, walkStopwatch}
If sw.IsRunning Then
sw.Label.Text = sw.Elapsed.ToString("hh\:mm\:ss")
Exit For
End If
Next
End Sub
Вы можете создать массив на уровне класса, но, поскольку он используется только в этом одном месте, имеет смысл создать его здесь. Падение производительности незначительно и делает код более читабельным, создавая вещи, где они используются.
Обратите внимание, что в этом коде я использовал сокращения для имен переменных. Это по двум причинам. Во-первых, это переменные, которые будут ссылаться на разные объекты в разное время. Это означает, что использование имени, указывающего c для цели объекта, невозможно. Вы можете использовать контекстное имя, например, currentRadioButton
, но я не делаю этого здесь по второй причине.
Вторая причина заключается в том, что они являются локальными переменными, используемыми в очень ограниченной области. Переменные rb
и sw
используются не более чем на несколько строк от того места, где они были объявлены, поэтому трудно не понять, что они из себя представляют. Если вы называете поле таким образом, тогда, когда вы видите его в коде, вы должны искать в другом месте, чтобы узнать, что это такое. В этом коде, если вы смотрите на использование одной из этих переменных, то объявление тоже бросается в глаза, так что вы должны быть слепы, чтобы не видеть, с каким типом вы имеете дело. По сути, если переменная используется далеко от ее объявления, я предлагаю содержательное, описательное имя. Если он используется только в нескольких строках своего объявления, краткое имя в порядке. Я обычно склонен использовать инициалы типа, как я сделал здесь. Если вам нужно несколько локальных переменных этого типа, я обычно предпочитаю использовать описательные имена для устранения их неоднозначности, а не числа. Иногда, однако, на самом деле не существует конкретного * c способа сделать это, в этом случае числа в порядке, например, сравнение двух Strings
без контекста может использовать s1
и s2
в качестве имен переменных.