Я выбрал решение с использованием Мастеров с шаблонами проектов , главным образом потому, что для некоторых из моих шаблонов уже требовались мастера.
Я создал базовый класс, который должны расширяться всеми остальными мастерами или который может использоваться сам по себе только для базовых функций:
public class AddTargetsWizard : IWizard
{
private const string RELATIVE_PATH_TO_TARGETS = @"..\..\..\..\PATH\TO\Custom.Tasks.Targets";
private const string TASK_NOT_FOUND_MESSAGE = @"A project of this type should be created under a specific path in order for the custom build task to be properly executed.
The build task could not be found at the following location:
{0}
Including the build task would result in unexpected behavior in Visual Studio.
The project was created, but the build task WILL NOT BE INCLUDED.
This project's builds WILL NOT benefit from the custom build task.";
private string _newProjectFileName;
private bool _addTaskToProject;
private Window _mainWindow;
public AddTargetsWizard()
{
this._addTaskToProject = true;
}
protected Window MainWindow
{
get
{
return this._mainWindow;
}
}
public virtual void BeforeOpeningFile(EnvDTE.ProjectItem projectItem)
{
}
public virtual void ProjectFinishedGenerating(EnvDTE.Project project)
{
this._newProjectFileName = project.FullName;
var projectDirectory = Path.GetDirectoryName(this._newProjectFileName);
var taskPath = Path.GetFullPath(Path.Combine(projectDirectory, RELATIVE_PATH_TO_TARGETS));
if (!File.Exists(taskPath))
{
MessageBox.Show(
this.MainWindow,
string.Format(TASK_NOT_FOUND_MESSAGE, taskPath),
"Project Creation Error",
MessageBoxButton.OK,
MessageBoxImage.Error,
MessageBoxResult.OK,
MessageBoxOptions.None);
this._addTaskToProject = false;
}
}
public virtual void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
{
}
public virtual void RunFinished()
{
if (this._addTaskToProject)
{
var project = new Microsoft.Build.Evaluation.Project(this._newProjectFileName);
project.Xml.AddImport(RELATIVE_PATH_TO_TARGETS);
project.Save();
}
}
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
var dte = (EnvDTE80.DTE2)automationObject;
var mainWindow = dte.MainWindow;
foreach (var proc in System.Diagnostics.Process.GetProcesses())
{
if (proc.MainWindowTitle.Equals(mainWindow.Caption))
{
var source = HwndSource.FromHwnd(proc.MainWindowHandle);
this._mainWindow = source.RootVisual as System.Windows.Window;
break;
}
}
this.OnRunStarted(automationObject, replacementsDictionary, runKind, customParams);
}
protected virtual void OnRunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
}
public virtual bool ShouldAddProjectItem(string filePath)
{
return true;
}
}
В этом пошаговом руководстве содержится довольно хорошее объяснение того, как связать мастера с шаблоном проекта (или элемента).
Вы заметите, что я предоставляю виртуальный OnRunStarted
метод для дочерних мастеров, который должен был бы обеспечить дополнительные функции, такие как отображение окон мастера, заполнение словаря замен и т. Д.
Что мне не нравится в этом подходе и / или моей реализации:
- Это намного сложнее, чем простой шаблон проекта.
- Чтобы мои окна мастера - все WPF - были реальными модальными окнами с владельцем Visual Studio, я не нашел лучшего способа, чем использовать заголовок текущего экземпляра для определения HWND и связанного
Window
.
- Все хорошо, когда проекты создаются в ожидаемой иерархии папок, но Visual Studio ведет себя странно (то есть всплывают бесполезные диалоги) в противном случае. Вот почему я решил отобразить сообщение об ошибке и не вставлять
Import
, если местоположение текущего проекта не работает с моим относительным путем.
Если у кого-то есть другие идеи, у меня все еще уши.