From 2906487bf3de348938e28477290be2a94de635f0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 26 May 2012 17:15:32 +0200 Subject: [PATCH] Allow switching between normal .NET framework and portable library in existing projects. Add dialog for picking portable target framework. --- .../Project/Src/Project/CSharpProject.cs | 41 +---- .../Project/Src/Project/VBNetProject.cs | 40 +---- .../Project/ICSharpCode.SharpDevelop.addin | 12 ++ .../Project/ICSharpCode.SharpDevelop.csproj | 7 + .../Project/Src/Project/AbstractProject.cs | 7 + .../Project/Src/Project/CompilableProject.cs | 3 +- .../Src/Project/Converter/UpgradeView.xaml.cs | 12 +- .../Src/Project/MSBuildBasedProject.cs | 26 +++ ...ConvertToPortableLibraryProjectBehavior.cs | 72 +++++++++ .../PickPortableTargetFramework.cs | 57 +++++++ .../PortableLibraryProjectBehavior.cs | 57 +++++-- .../Src/Project/PortableLibrary/Profile.cs | 11 +- .../PortableLibrary/SelectProfileDialog.xaml | 65 ++++++++ .../SelectProfileDialog.xaml.cs | 151 ++++++++++++++++++ .../PortableLibrary/SupportedFramework.cs | 61 ++++--- .../Project/Src/Project/TargetFramework.cs | 10 ++ .../BoolToVisibilityConverter.cs | 1 + .../ContextActionsBulbControl.xaml | 3 +- .../Project/BoolToVisibilityConverter.cs | 36 +++++ .../ICSharpCode.SharpDevelop.Widgets.csproj | 1 + 20 files changed, 558 insertions(+), 115 deletions(-) create mode 100644 src/Main/Base/Project/Src/Project/PortableLibrary/ConvertToPortableLibraryProjectBehavior.cs create mode 100644 src/Main/Base/Project/Src/Project/PortableLibrary/PickPortableTargetFramework.cs create mode 100644 src/Main/Base/Project/Src/Project/PortableLibrary/SelectProfileDialog.xaml create mode 100644 src/Main/Base/Project/Src/Project/PortableLibrary/SelectProfileDialog.xaml.cs create mode 100644 src/Main/ICSharpCode.SharpDevelop.Widgets/Project/BoolToVisibilityConverter.cs diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpProject.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpProject.cs index 96a12fef88..461fbb2633 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpProject.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpProject.cs @@ -46,8 +46,7 @@ namespace CSharpBinding Init(); } - public const string DefaultTargetsFile = @"$(MSBuildBinPath)\Microsoft.CSharp.Targets"; - public const string ExtendedTargetsFile = @"$(SharpDevelopBinPath)\SharpDevelop.Build.CSharp.targets"; + public const string DefaultTargetsFile = @"$(MSBuildToolsPath)\Microsoft.CSharp.targets"; public CSharpProject(ProjectCreateInformation info) : base(info) @@ -80,44 +79,6 @@ namespace CSharpBinding } } - /* - protected override void AddOrRemoveExtensions() - { - // Test if SharpDevelop-Build extensions are required - bool needExtensions = false; - - foreach (var p in GetAllProperties("TargetFrameworkVersion")) { - if (p.IsImported == false) { - if (p.Value.StartsWith("CF")) { - needExtensions = true; - } - } - } - - foreach (Microsoft.Build.BuildEngine.Import import in MSBuildProject.Imports) { - if (needExtensions) { - if (DefaultTargetsFile.Equals(import.ProjectPath, StringComparison.OrdinalIgnoreCase)) { - //import.ProjectPath = extendedTargets; - MSBuildInternals.SetImportProjectPath(this, import, ExtendedTargetsFile); - // Workaround for SD2-1490. It would be better if the project browser could refresh itself - // when necessary. - ProjectBrowserPad.Instance.ProjectBrowserControl.RefreshView(); - break; - } - } else { - if (ExtendedTargetsFile.Equals(import.ProjectPath, StringComparison.OrdinalIgnoreCase)) { - //import.ProjectPath = defaultTargets; - MSBuildInternals.SetImportProjectPath(this, import, DefaultTargetsFile); - // Workaround for SD2-1490. It would be better if the project browser could refresh itself - // when necessary. - ProjectBrowserPad.Instance.ProjectBrowserControl.RefreshView(); - break; - } - } - } - } - */ - protected override ProjectBehavior CreateDefaultBehavior() { return new CSharpProjectBehavior(this, base.CreateDefaultBehavior()); diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/VBNetProject.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/VBNetProject.cs index 334d1ebe1f..9a2de37c59 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/VBNetProject.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/VBNetProject.cs @@ -49,8 +49,7 @@ namespace ICSharpCode.VBNetBinding InitVB(); } - public const string DefaultTargetsFile = @"$(MSBuildBinPath)\Microsoft.VisualBasic.Targets"; - public const string ExtendedTargetsFile = @"$(SharpDevelopBinPath)\SharpDevelop.Build.VisualBasic.targets"; + public const string DefaultTargetsFile = @"$(MSBuildToolsPath)\Microsoft.VisualBasic.targets"; public VBNetProject(ProjectCreateInformation info) : base(info) @@ -108,43 +107,6 @@ namespace ICSharpCode.VBNetBinding } } - /* - protected override void AddOrRemoveExtensions() - { - // Test if SharpDevelop-Build extensions are required - bool needExtensions = false; - - foreach (var p in GetAllProperties("TargetFrameworkVersion")) { - if (p.IsImported == false) { - if (p.Value.StartsWith("CF")) { - needExtensions = true; - } - } - } - - foreach (Microsoft.Build.BuildEngine.Import import in MSBuildProject.Imports) { - if (needExtensions) { - if (DefaultTargetsFile.Equals(import.ProjectPath, StringComparison.OrdinalIgnoreCase)) { - //import.ProjectPath = extendedTargets; - MSBuildInternals.SetImportProjectPath(this, import, ExtendedTargetsFile); - // Workaround for SD2-1490. It would be better if the project browser could refresh itself - // when necessary. - ProjectBrowserPad.Instance.ProjectBrowserControl.RefreshView(); - break; - } - } else { - if (ExtendedTargetsFile.Equals(import.ProjectPath, StringComparison.OrdinalIgnoreCase)) { - //import.ProjectPath = defaultTargets; - MSBuildInternals.SetImportProjectPath(this, import, DefaultTargetsFile); - // Workaround for SD2-1490. It would be better if the project browser could refresh itself - // when necessary. - ProjectBrowserPad.Instance.ProjectBrowserControl.RefreshView(); - break; - } - } - } - }*/ - public Nullable OptionInfer { get { return GetValue("OptionInfer", false); } } diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.addin b/src/Main/Base/Project/ICSharpCode.SharpDevelop.addin index 7d27f0f110..02d7504228 100755 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.addin +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.addin @@ -2365,5 +2365,17 @@ + + + + + + + + + + + + diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index cfeff7aaa7..1dac314e93 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -380,10 +380,16 @@ + + + + SelectProfileDialog.xaml + Code + @@ -905,6 +911,7 @@ + diff --git a/src/Main/Base/Project/Src/Project/AbstractProject.cs b/src/Main/Base/Project/Src/Project/AbstractProject.cs index 11e51fb147..f67e11ad8d 100644 --- a/src/Main/Base/Project/Src/Project/AbstractProject.cs +++ b/src/Main/Base/Project/Src/Project/AbstractProject.cs @@ -598,6 +598,13 @@ namespace ICSharpCode.SharpDevelop.Project } } + protected virtual void InvalidateBehavior() + { + lock (SyncRoot) { + projectBehavior = null; + } + } + public virtual bool HasProjectType(Guid projectTypeGuid) { Guid myGuid; diff --git a/src/Main/Base/Project/Src/Project/CompilableProject.cs b/src/Main/Base/Project/Src/Project/CompilableProject.cs index b6bcb398b7..5bdbf3b71d 100644 --- a/src/Main/Base/Project/Src/Project/CompilableProject.cs +++ b/src/Main/Base/Project/Src/Project/CompilableProject.cs @@ -296,7 +296,8 @@ namespace ICSharpCode.SharpDevelop.Project public virtual void UpgradeProject(CompilerVersion newVersion, TargetFramework newFramework) { - GetOrCreateBehavior().UpgradeProject(newVersion, newFramework); + if (!ReadOnly) + GetOrCreateBehavior().UpgradeProject(newVersion, newFramework); } public static FileName GetAppConfigFile(IProject project, bool createIfNotExists) diff --git a/src/Main/Base/Project/Src/Project/Converter/UpgradeView.xaml.cs b/src/Main/Base/Project/Src/Project/Converter/UpgradeView.xaml.cs index db10d3d1ec..da017777a3 100644 --- a/src/Main/Base/Project/Src/Project/Converter/UpgradeView.xaml.cs +++ b/src/Main/Base/Project/Src/Project/Converter/UpgradeView.xaml.cs @@ -221,8 +221,11 @@ namespace ICSharpCode.SharpDevelop.Project.Converter TargetFramework selectedFramework = newFrameworkComboBox.SelectedValue as TargetFramework; if (selectedCompiler is UnchangedCompilerVersion) selectedCompiler = null; - if (selectedFramework is UnchangedTargetFramework) - selectedFramework = null; + if (selectedFramework != null) { + // Show dialog for picking target frameworks for portable library. + // This also handles UnchangedTargetFramework + selectedFramework = selectedFramework.PickFramework(listView.SelectedItems.Cast().Select(entry => entry.Project).ToList()); + } foreach (Entry entry in listView.SelectedItems) { @@ -261,6 +264,11 @@ namespace ICSharpCode.SharpDevelop.Project.Converter { } + public override TargetFramework PickFramework(IEnumerable selectedProjects) + { + return null; + } + public override bool Equals(object obj) { return obj is UnchangedTargetFramework; diff --git a/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs b/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs index b01cbbd892..de647d5a4c 100644 --- a/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs +++ b/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs @@ -1630,6 +1630,7 @@ namespace ICSharpCode.SharpDevelop.Project } #endregion + #region HasProjectType public override bool HasProjectType(Guid projectTypeGuid) { string guidList = GetEvaluatedProperty("ProjectTypeGuids"); @@ -1642,5 +1643,30 @@ namespace ICSharpCode.SharpDevelop.Project } return false; } + + public void AddProjectType(Guid projectTypeGuid) + { + string guidList = GetEvaluatedProperty("ProjectTypeGuids"); + if (string.IsNullOrEmpty(guidList)) + guidList = this.TypeGuid; + SetProperty("ProjectTypeGuids", guidList + ";" + projectTypeGuid.ToString("B").ToUpperInvariant(), false); + InvalidateBehavior(); + } + + public void RemoveProjectType(Guid projectTypeGuid) + { + string guidList = GetEvaluatedProperty("ProjectTypeGuids"); + if (!string.IsNullOrEmpty(guidList)) { + List remainingGuids = new List(); + foreach (string guid in guidList.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { + Guid result; + if (!(Guid.TryParse(guid, out result) && projectTypeGuid == result)) + remainingGuids.Add(guid); + } + SetProperty("ProjectTypeGuids", string.Join(";", remainingGuids), false); + InvalidateBehavior(); + } + } + #endregion } } diff --git a/src/Main/Base/Project/Src/Project/PortableLibrary/ConvertToPortableLibraryProjectBehavior.cs b/src/Main/Base/Project/Src/Project/PortableLibrary/ConvertToPortableLibraryProjectBehavior.cs new file mode 100644 index 0000000000..912c8b382f --- /dev/null +++ b/src/Main/Base/Project/Src/Project/PortableLibrary/ConvertToPortableLibraryProjectBehavior.cs @@ -0,0 +1,72 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ICSharpCode.Build.Tasks; +using ICSharpCode.SharpDevelop.Project.Converter; + +namespace ICSharpCode.SharpDevelop.Project.PortableLibrary +{ + /// + /// Project behavior attached to all non-portable C# and VB projects. + /// + public class ConvertToPortableLibraryProjectBehavior : ProjectBehavior + { + public override IEnumerable GetAvailableTargetFrameworks() + { + return base.GetAvailableTargetFrameworks().Concat(new [] { new PickPortableTargetFramework() }); + } + + public override void UpgradeProject(CompilerVersion newVersion, TargetFramework newFramework) + { + PortableTargetFramework newFx = newFramework as PortableTargetFramework; + if (newFx != null) { + // Convert to portable library + Core.AnalyticsMonitorService.TrackFeature(GetType(), "ConvertToPortableLibrary"); + var project = (CompilableProject)Project; + lock (project.SyncRoot) { + if (newVersion != null && GetAvailableCompilerVersions().Contains(newVersion)) { + project.SetToolsVersion(newVersion.MSBuildVersion.Major + "." + newVersion.MSBuildVersion.Minor); + } + project.SetProperty(null, null, "TargetFrameworkProfile", newFx.TargetFrameworkProfile, PropertyStorageLocations.Base, true); + project.SetProperty(null, null, "TargetFrameworkVersion", newFx.TargetFrameworkVersion, PropertyStorageLocations.Base, true); + // Convert + project.PerformUpdateOnProjectFile( + delegate { + foreach (var import in project.MSBuildProjectFile.Imports) { + if (import.Project.EndsWith(PortableLibraryProjectBehavior.NormalCSharpTargets, StringComparison.OrdinalIgnoreCase)) { + import.Project = PortableLibraryProjectBehavior.PortableTargetsPath + PortableLibraryProjectBehavior.PortableCSharpTargets; + break; + } else if (import.Project.EndsWith(PortableLibraryProjectBehavior.NormalVBTargets, StringComparison.OrdinalIgnoreCase)) { + import.Project = PortableLibraryProjectBehavior.PortableTargetsPath + PortableLibraryProjectBehavior.PortableVBTargets; + break; + } + } + }); + // Remove references + foreach (var referenceItem in project.GetItemsOfType(ItemType.Reference).ToArray()) { + // get short assembly name: + string assemblyName = referenceItem.Include; + if (assemblyName.IndexOf(',') >= 0) + assemblyName = assemblyName.Substring(0, assemblyName.IndexOf(',')); + assemblyName += ","; + if (KnownFrameworkAssemblies.FullAssemblyNames.Any(fullName => fullName.StartsWith(assemblyName, StringComparison.OrdinalIgnoreCase))) { + // If it's a framework assembly, remove the reference + // (portable libraries automatically reference all available framework assemblies) + ProjectService.RemoveProjectItem(project, referenceItem); + } + } + project.AddProjectType(ProjectTypeGuids.PortableLibrary); + project.Save(); + ProjectBrowserPad.RefreshViewAsync(); + } + } else { + base.UpgradeProject(newVersion, newFramework); + } + } + } +} diff --git a/src/Main/Base/Project/Src/Project/PortableLibrary/PickPortableTargetFramework.cs b/src/Main/Base/Project/Src/Project/PortableLibrary/PickPortableTargetFramework.cs new file mode 100644 index 0000000000..c3448baf1d --- /dev/null +++ b/src/Main/Base/Project/Src/Project/PortableLibrary/PickPortableTargetFramework.cs @@ -0,0 +1,57 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Project.Converter; + +namespace ICSharpCode.SharpDevelop.Project.PortableLibrary +{ + /// + /// Dummy TargetFramework that displays the SelectProfileDialog. + /// + sealed class PickPortableTargetFramework : TargetFramework + { + public PickPortableTargetFramework() + : base("PickPortableTargetFramework", ".NET Portable Subset (choose target frameworks)") + { + this.MinimumMSBuildVersion = new Version(4, 0); + } + + public override bool Equals(object obj) + { + return obj is PickPortableTargetFramework; + } + + public override int GetHashCode() + { + return 0; + } + + public override TargetFramework PickFramework(IEnumerable selectedProjects) + { + if (ProfileList.IsPortableLibraryInstalled()) { + SelectProfileDialog dlg = new SelectProfileDialog(ProfileList.Instance); + dlg.Owner = WorkbenchSingleton.MainWindow; + if (selectedProjects != null) { + var project = selectedProjects.FirstOrDefault() as CompilableProject; + if (project != null) { + Profile profile = Profile.LoadProfile(project.TargetFrameworkVersion, project.TargetFrameworkProfile); + if (profile != null) + dlg.SelectedProfile = profile; + } + } + if (dlg.ShowDialog() == true && dlg.SelectedProfile != null) { + return new PortableTargetFramework(dlg.SelectedProfile); + } + } else { + new CheckPortableLibraryInstalled().Run(); + } + return null; + } + } +} diff --git a/src/Main/Base/Project/Src/Project/PortableLibrary/PortableLibraryProjectBehavior.cs b/src/Main/Base/Project/Src/Project/PortableLibrary/PortableLibraryProjectBehavior.cs index 0bafbbae55..ebbbcc7781 100644 --- a/src/Main/Base/Project/Src/Project/PortableLibrary/PortableLibraryProjectBehavior.cs +++ b/src/Main/Base/Project/Src/Project/PortableLibrary/PortableLibraryProjectBehavior.cs @@ -10,16 +10,28 @@ using ICSharpCode.SharpDevelop.Project.Converter; namespace ICSharpCode.SharpDevelop.Project.PortableLibrary { + /// + /// Project behavior for portable library projects. + /// public class PortableLibraryProjectBehavior : ProjectBehavior { - public override IEnumerable GetAvailableCompilerVersions() - { - return base.GetAvailableCompilerVersions().Where(c => c.MSBuildVersion == new Version(4, 0)); - } + internal const string PortableTargetsPath = @"$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\"; + internal const string NormalTargetsPath = @"$(MSBuildToolsPath)\"; + internal const string PortableCSharpTargets = "Microsoft.Portable.CSharp.targets"; + internal const string NormalCSharpTargets = "Microsoft.CSharp.targets"; + internal const string PortableVBTargets = "Microsoft.Portable.VisualBasic.targets"; + internal const string NormalVBTargets = "Microsoft.VisualBasic.targets"; public override IEnumerable GetAvailableTargetFrameworks() { - return ProfileList.Instance.AllProfiles.Select(p => new PortableTargetFramework(p)).Union(new[] { this.CurrentTargetFramework }); + TargetFramework[] portableTargets = { this.CurrentTargetFramework, new PickPortableTargetFramework() }; + if (Project.Language == "C#" || Project.Language == "VBNet") { + // we support converting back to regular projects + return base.GetAvailableTargetFrameworks().Union(portableTargets); + } else { + // project must stay portable + return portableTargets; + } } public override TargetFramework CurrentTargetFramework { @@ -37,21 +49,48 @@ namespace ICSharpCode.SharpDevelop.Project.PortableLibrary public override void UpgradeProject(CompilerVersion newVersion, TargetFramework newFramework) { var project = (CompilableProject)Project; - if (project.ReadOnly) + var newFx = newFramework as PortableTargetFramework; + if (newFramework != null && newFx == null) { + // convert to normal .NET project (not portable) + Core.AnalyticsMonitorService.TrackFeature(GetType(), "DowngradePortableLibrary"); + project.PerformUpdateOnProjectFile( + delegate { + foreach (var import in project.MSBuildProjectFile.Imports) { + if (import.Project.EndsWith(PortableCSharpTargets, StringComparison.OrdinalIgnoreCase)) { + import.Project = NormalTargetsPath + NormalCSharpTargets; + break; + } else if (import.Project.EndsWith(PortableVBTargets, StringComparison.OrdinalIgnoreCase)) { + import.Project = NormalTargetsPath + NormalVBTargets; + break; + } + } + }); + project.RemoveProjectType(ProjectTypeGuids.PortableLibrary); + AddReferenceIfNotExists("System"); + AddReferenceIfNotExists("System.Xml"); + if (newFramework.IsBasedOn(TargetFramework.Net35) || newFramework.IsBasedOn(TargetFramework.Net35Client)) + AddReferenceIfNotExists("System.Core"); + base.UpgradeProject(newVersion, newFramework); return; + } lock (project.SyncRoot) { if (newVersion != null && GetAvailableCompilerVersions().Contains(newVersion)) { project.SetToolsVersion(newVersion.MSBuildVersion.Major + "." + newVersion.MSBuildVersion.Minor); } - var newFx = newFramework as PortableTargetFramework; if (newFx != null) { project.SetProperty(null, null, "TargetFrameworkProfile", newFx.TargetFrameworkProfile, PropertyStorageLocations.Base, true); project.SetProperty(null, null, "TargetFrameworkVersion", newFx.TargetFrameworkVersion, PropertyStorageLocations.Base, true); } - Project.Save(); - ResXConverter.UpdateResourceFiles(project); + project.Save(); ProjectBrowserPad.RefreshViewAsync(); } } + + void AddReferenceIfNotExists(string name) + { + if (!(Project.GetItemsOfType(ItemType.Reference).Any(r => string.Equals(r.Include, name, StringComparison.OrdinalIgnoreCase)))) { + ProjectService.AddProjectItem(Project, new ReferenceProjectItem(Project, name)); + } + } } } diff --git a/src/Main/Base/Project/Src/Project/PortableLibrary/Profile.cs b/src/Main/Base/Project/Src/Project/PortableLibrary/Profile.cs index e9667eafd4..4e68f02d7b 100644 --- a/src/Main/Base/Project/Src/Project/PortableLibrary/Profile.cs +++ b/src/Main/Base/Project/Src/Project/PortableLibrary/Profile.cs @@ -44,10 +44,11 @@ namespace ICSharpCode.SharpDevelop.Project.PortableLibrary } } - public readonly string TargetFrameworkVersion; - public readonly string TargetFrameworkProfile; - public readonly string DisplayName; - public readonly IList SupportedFrameworks; + // These must be properties for WPF data binding + public string TargetFrameworkVersion { get; private set; } + public string TargetFrameworkProfile { get; private set; } + public string DisplayName { get; private set; } + public IList SupportedFrameworks { get; private set; } public Profile(string targetFrameworkVersion, string targetFrameworkProfile, IList supportedFrameworks) { @@ -61,7 +62,7 @@ namespace ICSharpCode.SharpDevelop.Project.PortableLibrary public bool Supports(IList frameworks) { return frameworks.All( - requiredFx => SupportedFrameworks.Any(fx => fx.Identifier == requiredFx.Identifier && fx.MinimumVersion >= requiredFx.MinimumVersion) + requiredFx => SupportedFrameworks.Any(fx => fx.IsMoreGeneralThan(requiredFx)) ); } } diff --git a/src/Main/Base/Project/Src/Project/PortableLibrary/SelectProfileDialog.xaml b/src/Main/Base/Project/Src/Project/PortableLibrary/SelectProfileDialog.xaml new file mode 100644 index 0000000000..92d0d9467c --- /dev/null +++ b/src/Main/Base/Project/Src/Project/PortableLibrary/SelectProfileDialog.xaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Selected profile: + + + Two or more frameworks must be selected. + + +