Reputation: 452
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
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
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