diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs index dc9fd1d24f8..80f8c110f88 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information. +using Microsoft.Build.Construction; using Microsoft.VisualStudio.ProjectSystem.VS; using Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms; using static Microsoft.VisualStudio.ProjectSystem.Properties.PropertyNames; @@ -33,21 +34,24 @@ internal sealed class ApplicationFrameworkValueProvider : InterceptingPropertyVa private readonly UnconfiguredProject _project; private readonly IProjectItemProvider _sourceItemsProvider; private readonly IMyAppFileAccessor _myAppXmlFileAccessor; + private readonly IProjectAccessor _projectAccessor; [ImportingConstructor] public ApplicationFrameworkValueProvider( UnconfiguredProject project, [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, - IMyAppFileAccessor myAppXamlFileAccessor) + IMyAppFileAccessor myAppXamlFileAccessor, + IProjectAccessor projectAccessor) { _project = project; _sourceItemsProvider = sourceItemsProvider; _myAppXmlFileAccessor = myAppXamlFileAccessor; + _projectAccessor = projectAccessor; } public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) { - if (propertyName == ApplicationFramework) + if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) { if (await IsWPFApplicationAsync(defaultProperties)) { @@ -66,7 +70,7 @@ public ApplicationFrameworkValueProvider( public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) { - if (propertyName == ApplicationFramework) + if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) { return GetPropertyValueAsync(defaultProperties); } @@ -78,7 +82,7 @@ public override Task OnGetEvaluatedPropertyValueAsync(string propertyNam public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) { - if (propertyName == ApplicationFramework) + if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) { return GetPropertyValueAsync(defaultProperties); } @@ -165,10 +169,11 @@ private async Task IsWPFApplicationAsync(IProjectProperties defaultPropert if (startupObjectValue is not null) { + string prefix = rootNamespace + "."; // Use StringComparison.OrdinalIgnoreCase because VB is _not_ case-sensitive - if (startupObjectValue.StartsWith(rootNamespace + ".", StringComparison.OrdinalIgnoreCase)) + if (startupObjectValue.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { - startupObjectValue = startupObjectValue.Substring((rootNamespace + ".").Length); + startupObjectValue = startupObjectValue.Substring(prefix.Length); } await _myAppXmlFileAccessor.SetMainFormAsync(startupObjectValue); } @@ -207,17 +212,30 @@ private async Task IsWPFApplicationAsync(IProjectProperties defaultPropert { // Enabled - // Create the Application.xaml if it doesn't exist. We don't care about the path, we just need it to be created. - string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: true); - if (appXamlFilePath is not null) + string? appXamlFileName = Path.GetFileName(await GetAppXamlRelativeFilePathAsync(create: false)); + + // Project accessor to remove the ApplicationDefinition property if re-enabled + // Note: this code causes CPS tree validation warning in Debug builds only, but we are ignorning + await _projectAccessor.OpenProjectXmlForWriteAsync(_project, project => { - IEnumerable matchingItems = await _sourceItemsProvider.GetItemsAsync(NoneItemType, appXamlFilePath); - IProjectItem? appXamlItem = matchingItems.FirstOrDefault(); - if (appXamlItem is not null) + foreach (ProjectItemGroupElement itemGroup in project.ItemGroups) { - await appXamlItem.SetItemTypeAsync(ApplicationDefinitionItemType); + foreach (ProjectItemElement item in itemGroup.Items.ToList()) + { + if (StringComparers.ItemTypes.Equals(item.ItemType, ApplicationDefinitionItemType)) + { + itemGroup.RemoveChild(item); + } + else if (StringComparers.ItemTypes.Equals(item.ItemType, NoneItemType) && StringComparers.Paths.Equals(item.Include, appXamlFileName)) + { + itemGroup.RemoveChild(item); + } + } } - } + }); + + // Create the Application.xaml if it doesn't exist. We don't care about the path, we just need it to be created. + string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: true); // Clear out the StartupObject if it has a value. string? startupObject = await defaultProperties.GetUnevaluatedPropertyValueAsync(StartupObjectMSBuild); @@ -237,7 +255,7 @@ private async Task IsWPFApplicationAsync(IProjectProperties defaultPropert await defaultProperties.SetPropertyValueAsync(StartupObjectMSBuild, "Sub Main"); } - // Set the Application.xaml file's build action to None. + // Set the Application.xaml file's build action to None, which will also remove it from the ApplicationDefinition item group. string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: false); if (appXamlFilePath is not null) {