Hirvox
Hirvox

Reputation: 452

Setting project item subproperties in IWizard?

I have a custom item template that I am adding to a Sharepoint project. I need to ensure that my modules are only associated with my feature, even if the project already contains other features.

Replacing IDs in projectItemReference elements within .feature files is trivial to do by modifying the replacementsDictionary in the RunStarted method.

For example, I have the following SampleModule_WebParts.feature file:

<?xml version="1.0" encoding="utf-8"?>
<feature xmlns:dm0="http://schemas.microsoft.com/VisualStudio/2008/DslTools/Core" dslVersion="1.0.0.0" Id="$SampleFeatureID$" activateOnDefault="false" description="Sample Web Part" featureId="$SampleFeatureID$" scope="Site" solutionId="00000000-0000-0000-0000-000000000000" title="Contoso Intranet Sample Module Web Parts" version="" deploymentPath="$SharePoint.Project.FileNameWithoutExtension$_$SharePoint.Feature.FileNameWithoutExtension$" xmlns="http://schemas.microsoft.com/VisualStudio/2008/SharePointTools/FeatureModel">
  <projectItems>
    <projectItemReference itemId="$SampleModuleID$" />
  </projectItems>
</feature>

Replacing $SampleModuleID$ and $SampleFeatureID$ by modifying the replacementsDictionary in the IWizard.RunStarted method is trivial. But how can I modify the generated .csproj file snippet?

<None Include="Features\SampleModule_WebParts\SampleModule_WebParts.feature">
  <FeatureId>{78185D58-6398-4ED2-B0D0-3DC20946FF8F}</FeatureId>
</None>
<Compile Include="SPItems\SampleModule\SampleWebPart\SampleWebPart.cs" />
<Compile Include="SPItems\SampleModule\SampleWebPart\SampleWebPartUserControl.ascx.cs">
  <DependentUpon>SampleWebPartUserControl.ascx</DependentUpon>
  <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="SPItems\SampleModule\SampleWebPart\SampleWebPartUserControl.ascx.designer.cs">
  <DependentUpon>SampleWebPartUserControl.ascx.cs</DependentUpon>
</Compile>
<None Include="SPItems\SampleModule\SampleWebPart\SampleWebPart.webpart" />
<None Include="SPItems\SampleModule\SampleWebPart\SharePointProjectItem.spdata">
  <SharePointProjectItemId>{D982D304-E7FB-4E8C-899B-7D4096A55892}</SharePointProjectItemId>
</None>

In this case, I'd need to set the FeatureId and SharePointProjectItemId properties for the .feature and .spdata items. If I don't do anything, Visual Studio will autogenerate those GUIDs, but they won't match what I have in my replacementsDictionary. And that in turn leads to a broken reference in my .feature file and my module may get associated with the wrong feature.

How can I set those custom properties so that they are persisted into the .csproj file when the user saves it? IWizard.ProjectItemFinishedGenerating seems like the correct method to implement and I can inspect the ProjectItem parameter to figure out when the .spdata and .feature files have been generated, but what should I do there to set the FeatureId and SharePointProjectItemId properties?

Upvotes: 2

Views: 505

Answers (2)

Hirvox
Hirvox

Reputation: 452

The solution was to convert the Project reference into an ISharePointProject and calling ISharepointProject.Synchronize(). After that I could traverse the SharePoint project's object model.

var sp = project.DTE as Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
var projectService = new ServiceProvider(sp).GetService(typeof(ISharePointProjectService)) as ISharePointProjectService;
var sharepointProject = projectService.Convert<Project, ISharePointProject>(project);
sharepointProject.Synchronize();

After finding the my module and my feature from the collections returned by the ProjectItems and Features properties, I could simply associate the module with the feature:

sampleFeature.ProjectItems.Add(sampleModule);

Because I can fix the references programmatically, I can leave the old module GUID in the .feature file and clean up the invalid association by modifying the sampleFeature.Model.ProjectItems collection.

var invalidSampleModuleAssociation = (from projectItem in sampleFeature.Model.ProjectItems
                                      where projectItem.ItemId == originalSampleModuleID
                                      select projectItem).First();
sampleFeature.Model.ProjectItems.Remove(invalidSampleModuleAssociation);

Finally, I need to remove my module from any other features that the project might have, because Visual Studio automatically associates a new module with the first feature with an appropriate scope.

var unneededAssociations = (from otherFeature in sharepointProject.Features
                            where otherFeature.Name != sampleFeature.Name
                            from projectItem in otherFeature.ProjectItems
                            where projectItem.Name == sampleModule.Name
                            select new
                            {
                                Feature = otherFeature,
                                ModuleToRemove = projectItem
                            }).ToArray();
foreach (var moduleAssociation in unneededAssociations)
{
    moduleAssociation.Feature.ProjectItems.Remove(moduleAssociation.ModuleToRemove);
}

Upvotes: 1

sesispla
sesispla

Reputation: 300

AFAIK, you can make replacements also in any file, including the .csproj (in both filename and content)

If you have the Guids you wanna replace in the replacementDictionary, simply check the .vstemplate in order to be sure that file replacement is true:

<Project
    File="MyProject.proj"
    TargetFileName="$safeprojectname$.csproj"
    ReplaceParameters="true">
</Project>

And edit the .csproj properly, replacing the Guids:

<None Include="Features\SampleModule_WebParts\SampleModule_WebParts.feature">
  <FeatureId>{$SampleFeatureID$}</FeatureId>
</None>
<Compile Include="SPItems\SampleModule\SampleWebPart\SampleWebPart.cs" />
<Compile Include="SPItems\SampleModule\SampleWebPart\SampleWebPartUserControl.ascx.cs">
  <DependentUpon>SampleWebPartUserControl.ascx</DependentUpon>
  <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="SPItems\SampleModule\SampleWebPart\SampleWebPartUserControl.ascx.designer.cs">
  <DependentUpon>SampleWebPartUserControl.ascx.cs</DependentUpon>
</Compile>
<None Include="SPItems\SampleModule\SampleWebPart\SampleWebPart.webpart" />
<None Include="SPItems\SampleModule\SampleWebPart\SharePointProjectItem.spdata">
  <SharePointProjectItemId>{$SampleModuleID$}</SharePointProjectItemId>
</None>

Important! replacementDictionary, as IDictionaryl is case sensitive!!

Upvotes: 0

Related Questions