locked
Templates: TargetFileName in vstemplate does not work when used on the project file being created (csproj) RRS feed

  • Question

  • I've created a project template which attempts to create a project named Modules.x.csproj.  The vstemplate file looks like this:

    <VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
     <TemplateData>
      <Name>Prism Module</Name>
      <Description> Enter 'Login' as the name to create a project called 'LoginModule' and a view called 'LoginView</Description>
      <ProjectType>CSharp</ProjectType>
      <ProjectSubType>
      </ProjectSubType>
      <SortOrder>1000</SortOrder>
      <CreateNewFolder>true</CreateNewFolder>
      <DefaultName>MyMod</DefaultName>
      <ProvideDefaultName>true</ProvideDefaultName>
      <LocationField>Enabled</LocationField>
      <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
      <Icon>__TemplateIcon.ico</Icon>
     </TemplateData>
     <TemplateContent>
      <Project TargetFileName="Modules.$safeprojectname$.csproj" File="Modules.Login.csproj" ReplaceParameters="true">
       <ProjectItem ReplaceParameters="true" TargetFileName="$safeprojectname$Module.cs">LoginModule.cs</ProjectItem>
       <Folder Name="Properties" TargetFolderName="Properties">
        <ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
       </Folder>
       <Folder Name="Services" TargetFolderName="Services">
        <ProjectItem ReplaceParameters="true" TargetFileName="I$safeprojectname$Service.cs">ILoginService.cs</ProjectItem>
        <ProjectItem ReplaceParameters="true" TargetFileName="$safeprojectname$Service.cs">LoginService.cs</ProjectItem>
       </Folder>
       <Folder Name="Views" TargetFolderName="Views">
        <Folder Name="LoginView" TargetFolderName="$safeprojectname$View">
         <ProjectItem ReplaceParameters="true" TargetFileName="I$safeprojectname$PresentationModel.cs">ILoginPresentationModel.cs</ProjectItem>
         <ProjectItem ReplaceParameters="true" TargetFileName="I$safeprojectname$View.cs">ILoginView.cs</ProjectItem>
         <ProjectItem ReplaceParameters="true" TargetFileName="$safeprojectname$PresentationModel.cs">LoginPresentationModel.cs</ProjectItem>
         <ProjectItem ReplaceParameters="true" TargetFileName="$safeprojectname$View.xaml">LoginView.xaml</ProjectItem>
         <ProjectItem ReplaceParameters="true" TargetFileName="$safeprojectname$View.xaml.cs">LoginView.xaml.cs</ProjectItem>
        </Folder>
       </Folder>
      </Project>
     </TemplateContent>
    </VSTemplate>
    

    Note the use of the TargetFileName attribute on the Project.  When opening this template in VS2010 on XP, it does not respect the name.  It is simply the value entered in the dialog box.  For example, if I entered the name as Awesome, I'm expecting it to name the csproj and the name of the project as Modules.Awesome.csproj and Modules.Awesome.  However, it appears as simply Awesome.csproj / Awesome.  Is this a bug or by design?

     

    Thanks!

    Wednesday, August 11, 2010 12:26 AM

Answers

  • I found that I could rename the project name and csproj file using this code:

    //renaming code which would rename the project and csproj file, but not the directory it was contained within!
                string oldProjectName = item.SubProject.FileName;
                string path = Path.GetDirectoryName(project.FileName) + @"\Modules." + item.Name + ".csproj";
                MessageBox.Show("SaveAs into " + path);
                item.SubProject.SaveAs(path);
                File.Delete(oldProjectName);
    

    but it would leave the directory name containing that project as the old name.  To fix that, I had to do some major hacks which involved programatically creating the template project again with my chosen name, and then deleting the old project and directory.  At least it works:

    if (dontAddTemplateAgain) //stop it from recursively creating. this var set in RunStarted() based on presence of Modules. prefix
                {
                  return; 
                }
    string projFilename = project.FileName;
                DirectoryInfo slnRoot = new DirectoryInfo(Path.GetDirectoryName(projFilename)).Parent;
    
                Solution3 soln = (Solution3)dteObject.Solution;
                string templatePath = soln.GetProjectTemplate("PrismModule Template .8.zip", "CSharp");//create it again maybe this time with desired name?
                //MessageBox.Show("About to AddToProject using templatePath = " + templatePath + "\nDestination: " + slnRoot.FullName + @"\Modules." + item.Name);
                soln.AddFromTemplate(templatePath, slnRoot.FullName + @"\Modules." + item.Name, "Modules." + item.Name + ".csproj"); 
    
                //Remove and delete the original named project.
                //MessageBox.Show("Going to remove old proj");
                string oldProjectName = item.SubProject.FileName;
                dteObject.Solution.Remove(project);
                //MessageBox.Show("Project removed from sln");
                string dirToRemove = Path.GetDirectoryName(oldProjectName);
                Directory.Delete(dirToRemove, true);
                //sometimes it does not delete the root dir if it takes a while to delete the files within, this works
                if (Directory.Exists(dirToRemove))
                {
                  GC.WaitForPendingFinalizers();
                  Directory.Delete(dirToRemove);
                }
    

    Thursday, August 12, 2010 4:26 PM

All replies

  • I've found the same issue has been present since 2005 and still has not been fixed.  Can someone please open a bug request so we can get this resolved?  Thank you!

     

    See: http://social.msdn.microsoft.com/Forums/en-US/vsx/thread/65b915c6-707a-4e4c-a855-9c33eb5acccd

    Wednesday, August 11, 2010 12:28 AM
  •  Visual Studio issues can be reported at http://connect.microsoft.com/VisualStudio/ (can be reached from Help->Report a Bug inside Visual Studio).

    The reason for ignoring the target name is to honor end user choice. Users put a lot of thought (and at times follow company guidelines) in picking up project names. For e.g., one may have have the requirement that project names be MyCompany.MyProject.Foobar.csproj as that's how they want to produce the final assembly. If that's not honored, it can frustrate the user.

    It may be possible to rename the project after it has been generated. To achieve this one, one needs to provide template wizard and do the rename in IWizard.ProjectFinishedGenerating method(http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.templatewizard.iwizard.projectfinishedgenerating(VS.80).aspx).

     


    http://blogs.msdn.com/saurabhjain
    Wednesday, August 11, 2010 5:52 AM
  • Thanks for your reply Saurabh.  The ironic thing is that we _are_ trying to make it easier for our employees to follow a company policy by enforcing a standard naming method (via use of our templates).  If it is not possible to override the name using the TargetFileName attribute in the vstemplate file, that attribute should be removed from the Project element so that the schema validation fails.  That way we will not be scratching our heads wondering why it isn't working.  Documenting this would also be nice.

    I will try the IWizard approach, but its somewhat more work for the employees since I believe it needs to be installed in the GAC.

    Wednesday, August 11, 2010 4:17 PM
  • I tried setting the Name and Filename[1] properties of the ProjectItem I was adding, but the first threw NOT_IMPLEMENTED and the second would not compile.  Do you have any other ideas on how to change the project name / filename with DTE?

     

    Thanks!

    Wednesday, August 11, 2010 7:05 PM
  • I found that I could rename the project name and csproj file using this code:

    //renaming code which would rename the project and csproj file, but not the directory it was contained within!
                string oldProjectName = item.SubProject.FileName;
                string path = Path.GetDirectoryName(project.FileName) + @"\Modules." + item.Name + ".csproj";
                MessageBox.Show("SaveAs into " + path);
                item.SubProject.SaveAs(path);
                File.Delete(oldProjectName);
    

    but it would leave the directory name containing that project as the old name.  To fix that, I had to do some major hacks which involved programatically creating the template project again with my chosen name, and then deleting the old project and directory.  At least it works:

    if (dontAddTemplateAgain) //stop it from recursively creating. this var set in RunStarted() based on presence of Modules. prefix
                {
                  return; 
                }
    string projFilename = project.FileName;
                DirectoryInfo slnRoot = new DirectoryInfo(Path.GetDirectoryName(projFilename)).Parent;
    
                Solution3 soln = (Solution3)dteObject.Solution;
                string templatePath = soln.GetProjectTemplate("PrismModule Template .8.zip", "CSharp");//create it again maybe this time with desired name?
                //MessageBox.Show("About to AddToProject using templatePath = " + templatePath + "\nDestination: " + slnRoot.FullName + @"\Modules." + item.Name);
                soln.AddFromTemplate(templatePath, slnRoot.FullName + @"\Modules." + item.Name, "Modules." + item.Name + ".csproj"); 
    
                //Remove and delete the original named project.
                //MessageBox.Show("Going to remove old proj");
                string oldProjectName = item.SubProject.FileName;
                dteObject.Solution.Remove(project);
                //MessageBox.Show("Project removed from sln");
                string dirToRemove = Path.GetDirectoryName(oldProjectName);
                Directory.Delete(dirToRemove, true);
                //sometimes it does not delete the root dir if it takes a while to delete the files within, this works
                if (Directory.Exists(dirToRemove))
                {
                  GC.WaitForPendingFinalizers();
                  Directory.Delete(dirToRemove);
                }
    

    Thursday, August 12, 2010 4:26 PM
  • The wizards don't need to be in the GAC. They can be in the VSIX is you are using VS 2010. Or they can be PrivateAssemblies folder (next to devenv.exe) for all versions of Visual Studio.


    http://blogs.msdn.com/saurabhjain
    Thursday, August 12, 2010 5:00 PM
  • Hi,

     

    could you send me the full source code or a sample solution?

     

    Thanks a lot!

     

    Apo.

    Sunday, July 31, 2011 11:04 AM