diff ServerMonitor/Forms/CheckForm.cs @ 15:23f2e0da1094

- Fix the last execution status being lost after a check is edited. - Add comments.
author Brad Greco <brad@bgreco.net>
date Mon, 22 Apr 2019 21:11:27 -0400
parents d92176c5398a
children 7626b099aefd
line wrap: on
line diff
--- a/ServerMonitor/Forms/CheckForm.cs	Mon Apr 22 21:10:42 2019 -0400
+++ b/ServerMonitor/Forms/CheckForm.cs	Mon Apr 22 21:11:27 2019 -0400
@@ -10,9 +10,13 @@
 
 namespace ServerMonitorApp
 {
+    /// <summary>Form for creating and editing a check.</summary>
+    /// <remarks>
+    /// This form contains controls common to editing all types of checks.
+    /// Additional controls for specific check types may be shown in a panel below.
+    /// </remarks>
     public partial class CheckForm : Form
     {
-        private readonly List<CheckControl> checkControls = new List<CheckControl>();
         private bool helpShown;
         private CancellationTokenSource cancellationTokenSource;
         private Check workCheck;
@@ -21,14 +25,22 @@
         private Server server;
         private ServerMonitor monitor;
 
+        /// <summary>Raised when the Help button's location on the screen changes.</summary>
         public event EventHandler<HelpLocationChangedEventArgs> HelpLocationChanged;
 
+        /// <summary>The check being edited by the form.</summary>
+        /// <remarks>This check object is not updated with the form values until the OK button is clicked.</remarks>
         public Check Check { get; private set; }
 
+        /// <summary>The ID of the check being edited.</summary>
         public int CheckId { get; private set; }
 
+        /// <summary>The Help button's location on the screen.</summary>
         public Point HelpLocation => TypeHelpPictureBox.PointToScreen(Point.Empty);
 
+        /// <summary>Creates a check form to edit an existing check.</summary>
+        /// <param name="monitor">The server monitor.</param>
+        /// <param name="check">The check to edit.</param>
         public CheckForm(ServerMonitor monitor, Check check)
         {
             InitializeComponent();
@@ -39,6 +51,9 @@
             this.monitor = monitor;
         }
 
+        /// <summary>Creates a check form for creating a new check.</summary>
+        /// <param name="monitor">The server monitor.</param>
+        /// <param name="server">The server the new check will run on.</param>
         public CheckForm(ServerMonitor monitor, Server server)
         {
             InitializeComponent();
@@ -48,51 +63,65 @@
 
         private void CheckForm_Load(object sender, EventArgs e)
         {
+            // Set up control default values.
             CheckTypeComboBox.Items.AddRange(Check.CheckTypes);
             SeverityComboBox.Items.AddRange(new object[] { CheckStatus.Error, CheckStatus.Warning, CheckStatus.Information });
             SeverityComboBox.SelectedIndex = 0;
             FrequencyUnitsComboBox.DataSource = Enum.GetValues(typeof(FrequencyUnits));
             Helpers.FormatImageButton(RunButton);
             Helpers.FormatImageButton(CancelRunButton);
+            Icon = Resources.icon;
 
+            // Bind event listeners.
             Move += CheckForm_Move;
             CheckTypePanel.LocationChanged += CheckTypePanel_LocationChanged;
             GeneralGroupBox.Click += Control_Click;
             CheckSettingsPanel.Click += Control_Click;
 
+            // Set control values from the check.
             CheckTypeComboBox.SelectedItem = Check?.GetType();
             SetTitle();
-            Icon = Resources.icon;
             if (Check != null)
                 LoadCheck(Check);
         }
 
+        /// <summary>Sets the form title from the check, or a default value for a new check.</summary>
         private void SetTitle()
         {
             string name = NameTextBox.Text.IsNullOrEmpty() ? "New Check" : NameTextBox.Text;
             Text = string.Format("{0}: {1}", server.Name, name);
         }
 
+        /// <summary>Handles the check type combo box changing.</summary>
         private void CheckTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
         {
+            // Show the check control for the selected check type.
             ShowCheckControl();
+            // Create a temporary instance of the selected check type that will be used for validation
+            // and execution that can be discarded if the Cancel button is clicked.
             workCheck = (Check)Activator.CreateInstance((Type)CheckTypeComboBox.SelectedItem);
         }
 
+        /// <summary>Shows the display name of each check type in the combo box.</summary>
         private void CheckTypeComboBox_Format(object sender, ListControlConvertEventArgs e)
         {
             e.Value = Helpers.GetDisplayName((Type)e.ListItem);
         }
 
+        /// <summary>Shows a check control containing settings for the selected check type.</summary>
         private void ShowCheckControl()
         {
+            // Hide the existing check control.
             IEnumerable<CheckControl> checkControls = CheckSettingsPanel.Controls.OfType<CheckControl>();
             foreach (CheckControl control in checkControls)
                 control.Hide();
+            // Show the check control for the selected check type if it has already been created.
+            // Allows switching check types without losing data.
             Type type = (Type)CheckTypeComboBox.SelectedItem;
             checkControl = checkControls.FirstOrDefault(c => c.CheckType == type);
             if (checkControl == null)
             {
+                // Create a new check control if one has not been created yet for this check type.
                 checkControl = CheckControl.Create(type);
                 if (checkControl != null)
                 {
@@ -107,6 +136,7 @@
             }
         }
 
+        /// <summary>Populates common check controls from a check.</summary>
         private void LoadCheck(Check check)
         {
             Icon = Check.LastRunStatus.GetIcon();
@@ -119,12 +149,23 @@
             FrequencyUpDown.Value = check.Schedule.Frequency;
             StartTimePicker.Value = new DateTime(1970, 1, 1) + check.Schedule.StartTime;
             EndTimePicker.Value = new DateTime(1970, 1, 1) + check.Schedule.EndTime;
+            // Populate the controls specific to this check type.
             checkControl?.LoadCheck(check);
         }
 
+        /// <summary>Updates a check with user inputs.</summary>
+        /// <param name="check">The check to be updated.</param>
+        /// <param name="saving">
+        /// Whether the check is being saved.
+        /// Checks are validated before being saved and before being executed. This parameter allows
+        /// them to distinguish between these cases.
+        /// </param>
+        /// <returns>Whether the check was updated successfully.</returns>
         private bool UpdateCheck(Check check, bool saving = true)
         {
+            // The validation result message. An empty value indicates that validation succeeded.
             string result;
+            // Make sure we have a valid check type before doing anything else.
             if (CheckTypeComboBox.SelectedIndex == -1)
             {
                 result = "Check type cannot be blank.";
@@ -139,13 +180,16 @@
                 check.FailStatus = (CheckStatus)SeverityComboBox.SelectedItem;
                 check.MaxConsecutiveFailures = (int)FailuresInput.Value;
                 check.Schedule = new Schedule((FrequencyUnits)FrequencyUnitsComboBox.SelectedItem, (int)FrequencyUpDown.Value, StartTimePicker.Value.TimeOfDay, EndTimePicker.Value.TimeOfDay);
+                // Attempt to update the check from the check control inputs.
                 try
                 {
                     checkControl?.UpdateCheck(check);
+                    // If the update succeeded, run the check's own validation.
                     result = check.Validate(saving);
                 }
                 catch (UpdateCheckException e)
                 {
+                    // If the update failed, set the validation message to the error message.
                     result = e.Message;
                 }
             }
@@ -157,26 +201,43 @@
             return true;
         }
 
+        /// <summary>Saves the check and closes the form.</summary>
         private void OkButton_Click(object sender, EventArgs e)
         {
             if (!UpdateCheck(workCheck))
                 return;
-            Check = workCheck;
+            if (Check == null)
+            {
+                // If this is a new check, just use the check object created by this form.
+                Check = workCheck;
+            }
+            else
+            {
+                // When editing an existing check, update it now that we know the validation will succeed.
+                // Don't replace it with the temporary check object because the temporary check
+                // is not a complete copy; some properties are missing such as LastRunStatus.
+                UpdateCheck(Check);
+            }
+            UpdateCheck(Check);
             server.UpdateCheck(Check);
             monitor.SaveServers();
             Close();
         }
 
+        /// <summary>Cancels the form.</summary>
         private void CancelCheckButton_Click(object sender, EventArgs e)
         {
             Close();
         }
 
+        /// <summary>Executes the check with the current user inputs.</summary>
         private async void RunButton_Click(object sender, EventArgs e)
         {
+            // If the the inputs are invalid, show the error and return.
             if (!UpdateCheck(workCheck, false))
                 return;
 
+            // Update controls with the running status.
             RunButton.Enabled = false;
             RunButton.Text = "Running";
             ResultLabel.Visible = ResultIconPictureBox.Visible = false;
@@ -196,9 +257,13 @@
             localCancellationTokenSource = null;
         }
 
+        /// <summary>Cancels a check execution.</summary>
         private void CancelRunButton_Click(object sender, EventArgs e)
         {
+            // Request the check to cancel. Some check types are not cancellable.
             cancellationTokenSource.Cancel();
+            // Immediately update the UI so the user doesn't have to wait
+            // if a check is uncancellable.
             OnRunFinished();
         }
 
@@ -209,6 +274,7 @@
             RunButton.Enabled = true;
             RunButton.Text = "Run";
             CancelRunButton.Visible = false;
+            // If the check was not cancelled, show the results.
             if (result != null)
             {
                 ResultLabel.Text = result.Message;
@@ -217,18 +283,23 @@
             }
         }
 
+        /// <summary>Gathers the descriptions of all check types to show in the Help window.</summary>
         private string BuildHelpText()
         {
+            // Build the string as RTF to allow bold text.
             StringBuilder rtf = new StringBuilder(@"{\rtf1\ansi ");
             foreach (Type checkType in Check.CheckTypes)
             {
-                // Arguments to AppendLine() must end with a \ for the RichTextBox to render the newline character
+                // Show the check type name in bold, and the check type description.
+                // Arguments to AppendLine() must end with a \ for the RichTextBox to render the newline character.
                 rtf.Append(@"\b ").Append(Helpers.GetDisplayName(checkType)).AppendLine(@"\b0 \")
-                    .Append(checkType.GetAttribute<DescriptionAttribute>()?.Description ?? "No description provided.").AppendLine(@"\").AppendLine(@"\");
+                    .Append(checkType.GetAttribute<DescriptionAttribute>()?.Description ?? "No description provided.")
+                    .AppendLine(@"\").AppendLine(@"\");
             }
             return rtf.ToString().TrimEnd(' ', '\r', '\n', '\\');
         }
 
+        /// <summary>Shows or hides the Help popup window when the Help button is clicked.</summary>
         private void TypeHelpPictureBox_Click(object sender, EventArgs e)
         {
             if (helpShown)
@@ -240,12 +311,15 @@
                 helpForm = new QuickHelpForm(BuildHelpText());
                 helpForm.FormClosed += QuickHelpForm_FormClosed;
                 helpForm.Show(this);
+                // Keep focus on this form.
                 Activate();
                 helpShown = true;
+                // Trigger the location changed event so the popup will appear in the correct location.
                 OnHelpLocationChanged();
             }
         }
 
+        /// <summary>Handles the closing of the Help popup.</summary>
         private void QuickHelpForm_FormClosed(object sender, FormClosedEventArgs e)
         {
             helpShown = false;
@@ -253,27 +327,32 @@
             helpForm.Dispose();
         }
 
+        /// <summary>Notifies event subscribers that the location of the Help button on the screen changed.</summary>
         private void OnHelpLocationChanged()
         {
             HelpLocationChanged?.Invoke(this, new HelpLocationChangedEventArgs(HelpLocation));
         }
 
+        /// <summary>The Help button location changes if its panel location changes (such as when the window is resized).</summary>
         private void CheckTypePanel_LocationChanged(object sender, EventArgs e)
         {
             OnHelpLocationChanged();
         }
 
+        /// <summary>The Help button location changes if the window is moved.</summary>
         private void CheckForm_Move(object sender, EventArgs e)
         {
             OnHelpLocationChanged();
         }
 
+        /// <summary>Closes the Help popup when another control is clicked.</summary>
         private void Control_Click(object sender, EventArgs e)
         {
             if (helpShown)
                 helpForm.Close();
         }
 
+        /// <summary>Hides the Help popup when ESC is pressed.</summary>
         protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
         {
             if (keyData == Keys.Escape && helpShown)
@@ -284,22 +363,28 @@
             return base.ProcessCmdKey(ref msg, keyData);
         }
 
+        /// <summary>Shows appropriate schedule controls depending on the time interval selected.</summary>
         private void FrequencyUnitsComboBox_SelectedIndexChanged(object sender, EventArgs e)
         {
+            // Show a single time input for daily schedules.
+            // For more frequent schedules, show a time range.
             ScheduleBetweenPanel.Visible = !(ScheduleAtPanel.Visible = FrequencyUnitsComboBox.SelectedIndex == 3);
         }
 
+        /// <summary>Formats the FrequencyUnits enum as a string.</summary>
         private void FrequencyUnitsComboBox_Format(object sender, ListControlConvertEventArgs e)
         {
             e.Value = e.Value.ToString().ToLower() + "s";
         }
 
+        /// <summary>Updates the form title when the check's name is changed..</summary>
         private void NameTextBox_TextChanged(object sender, EventArgs e)
         {
             SetTitle();
         }
     }
 
+    /// <summary>Event arguments containing the location of the Help button.</summary>
     public class HelpLocationChangedEventArgs : EventArgs
     {
         public Point HelpLocation { get; private set; }