diff --git a/src/CodingWithCalvin.ProjectRenamifier/CodingWithCalvin.ProjectRenamifier.csproj b/src/CodingWithCalvin.ProjectRenamifier/CodingWithCalvin.ProjectRenamifier.csproj
index e5e3a35..6b11371 100644
--- a/src/CodingWithCalvin.ProjectRenamifier/CodingWithCalvin.ProjectRenamifier.csproj
+++ b/src/CodingWithCalvin.ProjectRenamifier/CodingWithCalvin.ProjectRenamifier.csproj
@@ -69,6 +69,9 @@
RenameProjectDialog.xaml
+
+ RenameProgressDialog.xaml
+
@@ -90,6 +93,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
diff --git a/src/CodingWithCalvin.ProjectRenamifier/Commands/RenamifyProjectCommand.cs b/src/CodingWithCalvin.ProjectRenamifier/Commands/RenamifyProjectCommand.cs
index 9434166..3d59534 100644
--- a/src/CodingWithCalvin.ProjectRenamifier/Commands/RenamifyProjectCommand.cs
+++ b/src/CodingWithCalvin.ProjectRenamifier/Commands/RenamifyProjectCommand.cs
@@ -1,6 +1,7 @@
using System.ComponentModel.Design;
using System.IO;
using System.Windows.Interop;
+using System.Windows.Threading;
using CodingWithCalvin.ProjectRenamifier.Dialogs;
using CodingWithCalvin.ProjectRenamifier.Services;
using EnvDTE;
@@ -80,41 +81,116 @@ private void RenameProject(Project project, DTE2 dte)
var newName = dialog.NewProjectName;
var projectFilePath = project.FullName;
- // Collect projects that reference this project before removal
- var referencingProjects = ProjectReferenceService.FindProjectsReferencingTarget(dte.Solution, projectFilePath);
+ // Show progress dialog
+ var progressDialog = new RenameProgressDialog(currentName);
+ var progressHelper = new WindowInteropHelper(progressDialog)
+ {
+ Owner = dte.MainWindow.HWnd
+ };
+ progressDialog.Show();
+
+ var stepIndex = 0;
+
+ // Step 1: Collect projects that reference this project before removal
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ return ProjectReferenceService.FindProjectsReferencingTarget(dte.Solution, projectFilePath);
+ }, out var referencingProjects);
var oldProjectFilePath = projectFilePath;
- // Capture the parent solution folder before removal
- var parentSolutionFolder = SolutionFolderService.GetParentSolutionFolder(project);
+ // Step 2: Capture the parent solution folder before removal
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ return SolutionFolderService.GetParentSolutionFolder(project);
+ }, out var parentSolutionFolder);
- // Remove project from solution before file operations
- dte.Solution.Remove(project);
+ // Step 3: Remove project from solution before file operations
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ dte.Solution.Remove(project);
+ });
+
+ // Step 4: Update RootNamespace and AssemblyName in .csproj
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ ProjectFileService.UpdateProjectFile(projectFilePath, currentName, newName);
+ });
- // Update RootNamespace and AssemblyName in .csproj
- ProjectFileService.UpdateProjectFile(projectFilePath, currentName, newName);
+ // Step 5: Update namespace declarations in source files
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ SourceFileService.UpdateNamespacesInProject(projectFilePath, currentName, newName);
+ });
- // Update namespace declarations in source files
- SourceFileService.UpdateNamespacesInProject(projectFilePath, currentName, newName);
+ // Step 6: Rename the project file on disk
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ return ProjectFileService.RenameProjectFile(projectFilePath, newName);
+ }, out projectFilePath);
- // Rename the project file on disk
- projectFilePath = ProjectFileService.RenameProjectFile(projectFilePath, newName);
+ // Step 7: Rename parent directory if it matches the old project name
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ return ProjectFileService.RenameParentDirectoryIfMatches(projectFilePath, currentName, newName);
+ }, out projectFilePath);
- // Rename parent directory if it matches the old project name
- projectFilePath = ProjectFileService.RenameParentDirectoryIfMatches(projectFilePath, currentName, newName);
+ // Step 8: Update references in projects that referenced this project
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ ProjectReferenceService.UpdateProjectReferences(referencingProjects, oldProjectFilePath, projectFilePath);
+ });
- // Update references in projects that referenced this project
- ProjectReferenceService.UpdateProjectReferences(referencingProjects, oldProjectFilePath, projectFilePath);
+ // Step 9: Re-add project to solution, preserving solution folder location
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ SolutionFolderService.AddProjectToSolution(dte.Solution, projectFilePath, parentSolutionFolder);
+ });
- // Re-add project to solution, preserving solution folder location
- SolutionFolderService.AddProjectToSolution(dte.Solution, projectFilePath, parentSolutionFolder);
+ // Step 10: Update using statements across the entire solution
+ ExecuteStep(progressDialog, stepIndex++, () =>
+ {
+ SourceFileService.UpdateUsingStatementsInSolution(dte.Solution, currentName, newName);
+ });
- // Update using statements across the entire solution
- SourceFileService.UpdateUsingStatementsInSolution(dte.Solution, currentName, newName);
+ // Mark as complete and close after a brief delay
+ progressDialog.Complete();
+ DoEvents();
+ System.Threading.Thread.Sleep(500);
+ progressDialog.Close();
// TODO: Implement remaining rename operations
// See open issues for requirements:
- // - #12: Progress indication
// - #13: Error handling and rollback
}
+
+ private void ExecuteStep(RenameProgressDialog dialog, int stepIndex, Action action)
+ {
+ dialog.StartStep(stepIndex);
+ DoEvents();
+ action();
+ dialog.CompleteStep(stepIndex);
+ DoEvents();
+ }
+
+ private void ExecuteStep(RenameProgressDialog dialog, int stepIndex, Func func, out T result)
+ {
+ dialog.StartStep(stepIndex);
+ DoEvents();
+ result = func();
+ dialog.CompleteStep(stepIndex);
+ DoEvents();
+ }
+
+ private void DoEvents()
+ {
+ var frame = new DispatcherFrame();
+ Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
+ new DispatcherOperationCallback(obj =>
+ {
+ ((DispatcherFrame)obj).Continue = false;
+ return null;
+ }), frame);
+ Dispatcher.PushFrame(frame);
+ }
}
}
diff --git a/src/CodingWithCalvin.ProjectRenamifier/Dialogs/RenameProgressDialog.xaml b/src/CodingWithCalvin.ProjectRenamifier/Dialogs/RenameProgressDialog.xaml
new file mode 100644
index 0000000..7c94dc3
--- /dev/null
+++ b/src/CodingWithCalvin.ProjectRenamifier/Dialogs/RenameProgressDialog.xaml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CodingWithCalvin.ProjectRenamifier/Dialogs/RenameProgressDialog.xaml.cs b/src/CodingWithCalvin.ProjectRenamifier/Dialogs/RenameProgressDialog.xaml.cs
new file mode 100644
index 0000000..3613c3e
--- /dev/null
+++ b/src/CodingWithCalvin.ProjectRenamifier/Dialogs/RenameProgressDialog.xaml.cs
@@ -0,0 +1,140 @@
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Media;
+
+namespace CodingWithCalvin.ProjectRenamifier.Dialogs
+{
+ public partial class RenameProgressDialog : Window
+ {
+ public ObservableCollection Steps { get; }
+
+ public RenameProgressDialog(string projectName)
+ {
+ InitializeComponent();
+
+ HeaderText.Text = $"Renaming '{projectName}'...";
+
+ Steps = new ObservableCollection
+ {
+ new ProgressStep("Analyzing project references"),
+ new ProgressStep("Capturing solution structure"),
+ new ProgressStep("Removing project from solution"),
+ new ProgressStep("Updating project file"),
+ new ProgressStep("Updating namespace declarations"),
+ new ProgressStep("Renaming project file"),
+ new ProgressStep("Renaming project directory"),
+ new ProgressStep("Updating project references"),
+ new ProgressStep("Re-adding project to solution"),
+ new ProgressStep("Updating using statements")
+ };
+
+ StepsList.ItemsSource = Steps;
+ }
+
+ public void StartStep(int stepIndex)
+ {
+ if (stepIndex >= 0 && stepIndex < Steps.Count)
+ {
+ Steps[stepIndex].Status = StepStatus.InProgress;
+ UpdateProgress(stepIndex);
+ }
+ }
+
+ public void CompleteStep(int stepIndex)
+ {
+ if (stepIndex >= 0 && stepIndex < Steps.Count)
+ {
+ Steps[stepIndex].Status = StepStatus.Completed;
+ UpdateProgress(stepIndex + 1);
+ }
+ }
+
+ public void FailStep(int stepIndex, string error)
+ {
+ if (stepIndex >= 0 && stepIndex < Steps.Count)
+ {
+ Steps[stepIndex].Status = StepStatus.Failed;
+ Steps[stepIndex].Description += $" - {error}";
+ }
+ }
+
+ public void Complete()
+ {
+ HeaderText.Text = "Rename completed successfully!";
+ ProgressBar.Value = 100;
+ }
+
+ private void UpdateProgress(int completedSteps)
+ {
+ ProgressBar.Value = (completedSteps * 100.0) / Steps.Count;
+ }
+ }
+
+ public enum StepStatus
+ {
+ Pending,
+ InProgress,
+ Completed,
+ Failed
+ }
+
+ public class ProgressStep : INotifyPropertyChanged
+ {
+ private string _description;
+ private StepStatus _status;
+
+ public ProgressStep(string description)
+ {
+ _description = description;
+ _status = StepStatus.Pending;
+ }
+
+ public string Description
+ {
+ get => _description;
+ set
+ {
+ _description = value;
+ OnPropertyChanged(nameof(Description));
+ }
+ }
+
+ public StepStatus Status
+ {
+ get => _status;
+ set
+ {
+ _status = value;
+ OnPropertyChanged(nameof(Status));
+ OnPropertyChanged(nameof(StatusIcon));
+ OnPropertyChanged(nameof(TextColor));
+ }
+ }
+
+ public string StatusIcon => _status switch
+ {
+ StepStatus.Pending => "\u2022", // Bullet
+ StepStatus.InProgress => "\u25B6", // Play arrow
+ StepStatus.Completed => "\u2714", // Check mark
+ StepStatus.Failed => "\u2716", // X mark
+ _ => "\u2022"
+ };
+
+ public Brush TextColor => _status switch
+ {
+ StepStatus.Pending => Brushes.Gray,
+ StepStatus.InProgress => Brushes.Black,
+ StepStatus.Completed => Brushes.Green,
+ StepStatus.Failed => Brushes.Red,
+ _ => Brushes.Gray
+ };
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void OnPropertyChanged(string propertyName)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}