/// <summary>
        /// Deploy a template as a new instance to be run.
        /// </summary>
        /// <param name="sender">The object that has sent this event.</param>
        /// <param name="e">The arguments that describe this event.</param>
        private void btnDeploy_Click(object sender, RoutedEventArgs e)
        {
            //
            // Check if this instance name is valid.
            //
            string targetPath = System.IO.Path.Combine(Support.GetInstancesPath(), txtName.Text);
            bool   isValid    = !string.IsNullOrWhiteSpace(txtName.Text) && !Directory.Exists(targetPath);

            if (!isValid)
            {
                MessageBox.Show("That instance name already exists or is invalid.");
                return;
            }

            //
            // Get the path to the template ZIP file.
            //
            var    items   = cbTemplates.ItemsSource as List <string>;
            string file    = items[cbTemplates.SelectedIndex] + ".zip";
            string zipfile = System.IO.Path.Combine(Support.GetTemplatesPath(), file);

            //
            // Disable any UI controls that should not be available while deploying.
            //
            btnDeploy.IsEnabled = btnDelete.IsEnabled = false;

            //
            // Deploy the template as a new instance.
            //
            new Task(() =>
            {
                //
                // Extract the zip file to the target instance path.
                //
                Support.ExtractZipFile(zipfile, Path.Combine(targetPath, "RockWeb"), (progress) =>
                {
                    Dispatcher.Invoke(() =>
                    {
                        txtStatus.Text = string.Format("Extracting {0:n0}%...", Math.Floor(progress * 100));
                    });
                });

                //
                // Update the UI to indicate that it is deployed.
                //
                Dispatcher.Invoke(() =>
                {
                    txtStatus.Text = "Deployed";
                    UpdateState();

                    InstancesView.UpdateInstances();
                });
            }).Start();
        }
        /// <summary>
        /// Delete the selected template from disk.
        /// </summary>
        /// <param name="sender">The object that has sent this event.</param>
        /// <param name="e">The arguments that describe this event.</param>
        private void btnDelete_Click(object sender, RoutedEventArgs e)
        {
            var result = MessageBox.Show("Delete this template?", "Confirmation", MessageBoxButton.YesNo, MessageBoxImage.Question);

            if (result == MessageBoxResult.Yes)
            {
                var items = cbTemplates.ItemsSource as List <string>;

                string file = items[cbTemplates.SelectedIndex] + ".zip";
                string path = System.IO.Path.Combine(Support.GetTemplatesPath(), file);

                File.Delete(path);

                new Thread(LoadData).Start();
            }
        }
        /// <summary>
        /// Load all data in the background.
        /// </summary>
        private void LoadData()
        {
            var templates = Directory.GetFiles(Support.GetTemplatesPath(), "*.zip")
                            .Select(d => System.IO.Path.GetFileName(d))
                            .Select(f => f.Substring(0, f.Length - 4))
                            .ToList();

            Dispatcher.Invoke(() =>
            {
                cbTemplates.ItemsSource = templates;
                if (templates.Count > 0)
                {
                    cbTemplates.SelectedIndex = 0;
                }

                UpdateState();
            });
        }
        /// <summary>
        /// Import the given github version as a new template.
        /// </summary>
        /// <param name="sender">The object that sent this event.</param>
        /// <param name="e">The event arguments.</param>
        private void btnImport_Click(object sender, RoutedEventArgs e)
        {
            var tags   = cbTags.ItemsSource as List <GitHubTag>;
            var tag    = tags[cbTags.SelectedIndex];
            var vsList = cbVisualStudio.ItemsSource as List <VisualStudioInstall>;
            var vs     = vsList[cbVisualStudio.SelectedIndex];

            //
            // Check if this template already exists.
            //
            var templateName = "RockBase-" + tag.Name;

            if (File.Exists(Path.Combine(Support.GetTemplatesPath(), templateName + ".zip")))
            {
                MessageBox.Show("A template with the name " + templateName + " already exists.", "Cannot import", MessageBoxButton.OK, MessageBoxImage.Hand);
                return;
            }

            //
            // Initialize a new release builder to process this import operation.
            //
            ReleaseBuilder = new ReleaseBuilder();
            ReleaseBuilder.StatusTextChanged += ReleaseBuilder_StatusTextChanged;
            ReleaseBuilder.ConsoleOutput     += ReleaseBuilder_ConsoleOutput;
            ReleaseBuilder.BuildCompleted    += ReleaseBuilder_BuildCompleted;

            //
            // Prepare the UI for the import operation.
            //
            btnImport.IsEnabled = false;
            txtConsole.Text     = string.Empty;

            //
            // Start a task in the background to download and build the github release.
            //
            new Task(() =>
            {
                ReleaseBuilder.DownloadRelease(tag.ZipballUrl, vs.GetExecutable(), "RockBase-" + tag.Name);
            }).Start();
        }
        /// <summary>
        /// Compress the RockWeb folder into a ZIP file as a template. Then do final cleanup.
        /// </summary>
        private void BuildTemplate()
        {
            //
            // Compress the RockWeb folder into a template ZIP file.
            //
            UpdateStatusText("Compressing RockWeb...");
            var zipFile = Path.Combine(Support.GetTemplatesPath(), TemplateName + ".zip");
            var rockWeb = Path.Combine(Support.GetBuildPath(), "RockWeb");

            Support.CreateZipFromFolder(zipFile, rockWeb);

            //
            // Cleanup temporary files.
            //
            UpdateStatusText("Cleaning up...");
            Directory.Delete(Support.GetBuildPath(), true);
            string tempfilename = Path.Combine(Support.GetDataPath(), "temp.zip");

            File.Delete(tempfilename);

            UpdateStatusText("Template has been created.");

            BuildCompleted?.Invoke(this, new EventArgs());
        }