dotnet / project-system

The .NET Project System for Visual Studio
MIT License
969 stars 387 forks source link

XAML files are not supported #1467

Closed srivatsn closed 5 years ago

srivatsn commented 7 years ago

From @tannergooding on February 3, 2017 22:56

Attached is a simple repro solution. WpfCps.zip

Issue 1: XAML files are treated as None, when they should be treated as Page with Generator=MSBuild:Compile and SubType=Designer metadata. Issue 2: Page items are not visible in the Solution Explorer Issue 3: xaml.cs files are not properly nested under the xaml file Issue 4: Compilation fails with: 1>C:\windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.WinFx.targets(419,45): error MSB4057: The target "CoreCompile" does not exist in the project.

Copied from original issue: dotnet/sdk#810

srivatsn commented 7 years ago

From @tannergooding on February 3, 2017 22:56

FYI. @srivatsn, @jinujoseph, @nguerrera

srivatsn commented 7 years ago

From @tannergooding on February 3, 2017 22:57

This completely blocks the porting of WPF based applications to CPS (I imagine it would also block UWP and anything else that uses XAML).

srivatsn commented 7 years ago

Yes WPF and any other desktop projects are not supported yet. Currently only .NET Core projects are supported.

clairernovotny commented 7 years ago

This should work with the right LanguageTargets, shouldn't it?

aienabled commented 7 years ago

For me it works good with VS2017 RC and the class library project (.NET Framework, not .NET Standard). I can include XAML files and everything is properly compiled, Intellisense works, etc. The only issue I have is when I use globbing - the dependent .xaml.cs files are not nested. Reported there https://github.com/Microsoft/VSProjectSystem/issues/169

UPD. I also checked default class library project (the template with .NET Standard). After switching target to net461 it works but indeed it treats XAML as None... and manual setting it to Page leads to The target "CoreCompile" does not exist in the project error.

aienabled commented 7 years ago

I was able to properly setup XAML files building with CPS for VS2017. @onovotny was right, with proper targets it works. I've added <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />. Also I added missing XAML rule for Page. And I've found a workaround for the issue with the dependent .xaml.cs files for files globbing (see link above).

tannergooding commented 7 years ago

Ok. I now understand this much better (@srivatsn).

WPF (and some other project types) generate a temporary project file that does not use the same extension as the original (WPF uses .tmp_proj, for example).

The SDK.targets uses the project extension to set LanguageTargets (provided LanguageTargets has not already been set). It then imports whatever LanguageTargets is set to (C# should be setting it to $(MSBuildToolsPath)\Microsoft.CSharp.targets, for example).

So the workaround is to ensure that LanguageTargets is set on any project type that hits the above issue.

I would think the proper fix, however, is to get the XAML targets (and any other targets that generate temporary projects) to use the original project extension.

aienabled commented 7 years ago

@tannergooding, WPF is using temporary project because it need two-pass building to properly process XAML markup as described in this article on MSDN. So I'm afraid it might not be possible to avoid .tmp_proj.

davkean commented 7 years ago

@aienabled Keep any bugs you file for managed concepts on this repro.

tannergooding commented 7 years ago

@aienabled, i wasn't indicating it should avoid .tmp_proj, I'm saying t should usee the original projects file extension when doing so. Meaning instead of .tmp_proj, it should use .tmp_proj.csproj or just .csproj.

srivatsn commented 7 years ago

@tannergooding can you file that as an internal bug on XAML?

tannergooding commented 7 years ago

@srivatsn, I have logged VSO Bug: 382095 (not sure if the area path is correct, it seemed to be the best fit though).

gulbanana commented 7 years ago

XAML needs PresentationBuildTasks, right, so is it correct that making this work in the SDK won't automatically make it work with dotnet build?

tmat commented 7 years ago

Related: https://github.com/dotnet/sdk/issues/1367

tmat commented 7 years ago

Workaround in RepoToolset (included via Directory.Build.props):

  <Choose>
    <When Condition="'$(MSBuildProjectExtension)' != '.csproj' and '$(MSBuildProjectExtension)' != '.vbproj'">
        <PropertyGroup>
          <LanguageTargets Condition="Exists('$(MSBuildProjectDirectory)\$(AssemblyName).csproj')">$(MSBuildToolsPath)\Microsoft.CSharp.targets</LanguageTargets>
          <LanguageTargets Condition="Exists('$(MSBuildProjectDirectory)\$(AssemblyName).vbproj')">$(MSBuildToolsPath)\Microsoft.VisualBasic.targets</LanguageTargets>
        </PropertyGroup>
    </When>
  </Choose>

Turns out the XAML build sets the AssemblyName property to the original value.

bdovaz commented 7 years ago

Anyone got this working? I'm able to compile it but I lost intellisense in VS 2017.

This is my csproj:


<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets>
    <OutputType>winexe</OutputType>
    <TargetFramework>net462</TargetFramework>
    <DebugType>Full</DebugType>
    <ApplicationIcon>res\ico\icon.ico</ApplicationIcon>
    <OutputTypeEx>winexe</OutputTypeEx>
    <StartupObject />
  </PropertyGroup>

  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
    <TransformOnBuild>True</TransformOnBuild>
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
  </PropertyGroup>

  <Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />

  <ItemGroup>
    <!-- App.xaml -->
    <ApplicationDefinition Include="App.xaml" SubType="Designer" Generator="MSBuild:UpdateDesignTimeXaml" />

    <!-- XAML elements -->
    <Page Include="**\*.xaml" SubType="Designer" Generator="MSBuild:UpdateDesignTimeXaml" Exclude="App.xaml" />
    <Compile Update="**\*.xaml.cs" SubType="Designer" DependentUpon="%(Filename)" />
    <Compile Include="$(IntermediateOutputPath)**\*.g.cs" Visible="false" />
    <None Include="$(ProjectDir)obj" Visible="false" />

    <!-- Resources -->
    <EmbeddedResource Update="Properties\Resources.resx" Generator="ResXFileCodeGenerator" LastGenOutput="Resources.Designer.cs" />
    <Compile Update="Properties\Resources.Designer.cs" AutoGen="True" DependentUpon="Resources.resx" DesignTime="True" />

    <!-- Settings -->
    <None Update="Properties\Settings.settings" Generator="SettingsSingleFileGenerator" LastGenOutput="Settings.Designer.cs" />
    <Compile Update="Properties\Settings.Designer.cs" AutoGen="True" DependentUpon="Settings.settings" />

    <None Update="App.config">
      <TransformOnBuild>true</TransformOnBuild>
    </None>
    <None Update="App.Debug.config">
      <IsTransformFile>True</IsTransformFile>
    </None>
    <None Update="App.Release.config">
      <IsTransformFile>True</IsTransformFile>
    </None>
  </ItemGroup>

  <ItemGroup>
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
    <Reference Include="System.Xaml" />
    <Reference Include="WindowsBase" />
  </ItemGroup>

  <ItemGroup>
    <Compile Remove="Publish\**" />
    <EmbeddedResource Remove="Publish\**" />
    <None Remove="Publish\**" />
    <Page Remove="Publish\**" />
  </ItemGroup>

  <PropertyGroup>
    <AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
  </PropertyGroup>

  <Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
</Project>
sharwell commented 7 years ago

❓ @bdovaz I notice you have Generator="MSBuild:UpdateDesignTimeXaml". Is there a reason you aren't using Generator="MSBuild:Compile"?

Here's one project that I've been working on that uses XAML. I need to look back and see if IntelliSense is working properly.

https://github.com/Microsoft/perfview/blob/aa6bfb7d5dec6144979b3399c46818907e068885/src/PerfView/PerfView.csproj#L99

bdovaz commented 7 years ago

@sharwell to be honest I don't know, my csproj is being written by seeing other people examples that are trying to achieve the same goal.

Also when I mean intellisense I mean that I can't reference almost anything. Example:

image

Note: The code that is not recognizing it's in another referenced project.

I really want to use the new format but I need a working example that works. One example that:

I've tried to update my csproj seeing that example but I have the same problem: it compiles but I have no Intellisense.

Any help?

aienabled commented 7 years ago

@bdovaz, I'm using it successfully for WPF (multiple projects referencing each other) and IntelliSense works well (in C# and XAML files).

Do you need this for WPF/UWP or Xamarin project? Are you able to see XAML files in the solution explorer? I'm asking because there is no Page item type rule in netstandard and without it XAML files added as Page are not visible in the solution explorer but you didn't mention about it. To workaround this, I'm using custom targets which add the Page rule. I will prepare a small sample.

bdominguez commented 7 years ago

@aienabled in my case I'm using it with WPF. Thanks I'm also looking forward to that example.

aienabled commented 7 years ago

Done https://github.com/aienabled/WpfNetStandardSample but it has a problem with VS IntelliSense for WPF generated code (from intermediate folder) as I've described in the readme there. I hope someone from the Project System team will take a look at that sample and figure out why VS IntelliSense doesn't work properly. Meanwhile, I'm using it with ReSharper without any issues.

sharwell commented 7 years ago

@bdovaz I figured out the problem. You need to apply a workaround for #2488, as seen in icsharpcode/ILSpy#847.

@aienabled Your solution includes some workarounds that I haven't done yet. Maybe we can put together a NuGet package that someone can install to "just work"?

clairernovotny commented 7 years ago

@sharwell Please add that to the MSBuild.Sdk.Extras package as that's the goal of the project. Sounds like a perfect addition.

aienabled commented 7 years ago

@sharwell, you can go ahead. Unfortunately, I don't have time now to pack this into a NuGet package and ensure it works properly. UPD. I can confirm the fix for VS IntelliSense works properly. I've updated my repository to include this workaround.

bdominguez commented 7 years ago

I'm still having problems with this.

I randomly lost intellisense and to get it back it's a combination of making a change on the project (for example: deleting a file) or closing and opening VS 2017 or unloading or loading the project...

It's a nightmare...

Also on stackoverflow someone called RB had the same problem:

https://stackoverflow.com/questions/44154271/wpf-controls-not-recognized-in-code-behind-when-using-new-csproj-format

sharwell commented 7 years ago

@bdominguez Did you apply this workaround as well?

https://github.com/sharwell/ILSpy/blob/88616e828f7721fa793b3a551667984dceda5bcb/ILSpy/ILSpy.csproj#L366-L374

bdominguez commented 7 years ago

Yes, I already included it.

mhutch commented 7 years ago

FWIW, Xamarin Forms targets now have a XAML wildcard that handles nesting:

https://github.com/xamarin/Xamarin.Forms/blob/master/.nuspec/Xamarin.Forms.DefaultItems.props https://github.com/xamarin/Xamarin.Forms/blob/master/.nuspec/Xamarin.Forms.DefaultItems.targets

glennawatson commented 6 years ago

We been trying a lot of different solutions but we been having issues using the WPF with the CI engine for the first build. Eg we get a lot of errors where it mentioned it can't find the .g.cs files, then subsequent builds it works. It seems like the xaml parsing happens after the build stage or something? Eg we get errors like:

CSC : error CS2001: Source file 'dialogs\newworlddialog.g.cs' could not be found.
80>CSC : error CS2001: Source file 'dialogs\openworlddialog.g.cs' could not be found.
80>CSC : error CS2001: Source file 'editors\worldeditor.g.cs' could not be found.
80>CSC : error CS2001: Source file 'dialogs\assetpickerdialog.g.cs' could not be found.
80>CSC : error CS2001: Source file 'bookmarkpropertiesview.g.cs' could not be found.
clairernovotny commented 6 years ago

The MSBuild.Sdk.Extras supports this now with changes based on @sharwell's work

bdovaz commented 6 years ago

@onovotny what do we need to do in our csproj files to get it working with WPF projects? Is there any official docs on this? I only want to use new csproj format with WPF projects.

aienabled commented 6 years ago

@bdovaz, a few months ago I've made this repository https://github.com/aienabled/WpfNewProjectSystemSample I've just updated it to support latest version of VS2017 and include my latest workarounds. Maybe it will be helpful to you.

clairernovotny commented 6 years ago

@aienabled there's a bit more to it than that, unfortunately. You have to deal with the tmp_proj 2-pass of WPF.

Using MSBuild.Sdk..Extras, I have an example here:

https://github.com/NuGetPackageExplorer/NuGetPackageExplorer/blob/master/PackageExplorer/NuGetPackageExplorer.csproj

Note the workarounds currently needed at the top/bottom of the csproj.

I'll get some updated docs on the project that show how.

bdovaz commented 6 years ago

We need an official response on this otherwise we will be really lost...

clairernovotny commented 6 years ago

@bdovaz at this point, I think what I have in my Extras is as good as it gets. Official word is that it's not supported in VS 2017 and it's on the roadmap for the next release: https://github.com/dotnet/project-system/blob/master/docs/repo/roadmap.md

If you don't want to wait, as I don't, then everything is on a best-effort "we'll try to find workarounds" basis.

tannergooding commented 6 years ago

@bdovaz, the workarounds @onovotny has looks correct.

Any project containing XAML files (with the appropriate ItemGroup) will undergo a two-pass compilation.

Said two pass compilation ends up creating a .tmp_proj file that lives next to the origin project file, is compiled, and then the tmp_proj file is deleted.

The primary issue with this approach is that anything that relies on the original project name or extension can/will break.

For example, with SDK based projects the different extension prevents the SDK from automatically determining what language your project is targeting and so the Default props/targets are imported rather than the C# props/targets (for example). The workaround for this issue is to set the language targets in your projects: https://github.com/dotnet/project-system/issues/1467#issuecomment-278180128

For both SDK and non-SDK projects, this can interfere with NuGet, which automatically imports props/targets based on the name of the project file. @onovotny has worked around this with <Import Project="obj\*.props" Condition=" '$(MSBuildProjectExtension)' == '.tmp_proj'" /> and <Import Project="obj\*.targets" Condition=" '$(MSBuildProjectExtension)' == '.tmp_proj'" />.

tannergooding commented 6 years ago

I believe there has been some investigation into resolving the issues, but they are non-trivial.

The XAML props/targets actually exist as part of the Desktop framework and are therefore part of Windows(C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Xaml.targets, for example). This makes the barrier to fixing the issue at the source already much higher (it requires updating "core" system files).

It also has a risk of being a breaking change, as anyone dependent on the tmp_proj being created, or doing something special as part of the tmp_proj build could potentially break or have different behavior. WPF has been around for over 10 years and is a fairly popular framework, so that is a lot of code that could potentially break.

BrunoJuchli commented 6 years ago

@tannergooding We've been working on our WPF application for several years and are still creating new ones. We (can't speak for others...) would prefer, by far, a breaking change than not having CPS support. A breaking change we can deal with. We notice it, we learn about it, we apply the changes and we move on. The split between CPS and old style projects has already created issues for us several times as we've also got .net core frontends. Using CPS everywhere would enable us to share more of our build chain and make our build chain/infrastructure simpler, too. Currently we have to duplicate quite some code and that creates continuous friction - much worse than a breaking change.

That said, however, we'd also be fine with CPS WPF support being an opt-in. Let's say you could add a *.targets import to every WPF csproj or something like that.

gulbanana commented 6 years ago

I'm in exactly the same position - tons of old wpf, tons of new asp.net core, we want interoperability and can accept breaking changes to achieve it, whether they require an opt-in or not.

bdominguez commented 6 years ago

Same here, If we have that breaking changes documented, there is no problem.

gulbanana commented 6 years ago

bear in mind that for anyone using wpf clickonce, the last couple of .net framework and visual studio releases have already been massively breaking :)

Latency commented 6 years ago

@sharwell the last bit of that .csproj file in the link you referenced, it will break loading of the Assemblies. Doesn't work! I am using v4.7.1 w/ CPS build configs.

<!--
  Work around to fix Intellisense file generation for XAML projects https://github.com/dotnet/project-system/issues/2488
-->
<Target Name="WorkaroundForXAMLIntellisenseBuildIssue" AfterTargets="_CheckCompileDesignTimePrerequisite">
  <PropertyGroup>
    <BuildingProject>false</BuildingProject>
  </PropertyGroup>
</Target>

Reverting to using this instead:

<!-- App.xaml --> 
<ApplicationDefinition Include="App.xaml" SubType="Designer" Generator="MSBuild:XamlIntelliSenseFileGenerator" />

<!-- XAML elements -->
<Page Include="**\*.xaml" SubType="Designer" Generator="MSBuild:XamlIntelliSenseFileGenerator" Exclude="App.xaml" />
<Compile Update="**\*.xaml.cs" SubType="Designer" DependentUpon="%(Filename)" />
<Compile Include="$(IntermediateOutputPath)**\*.g.cs" Visible="false" />
<None Include="$(ProjectDir)obj" Visible="false" />

@everyone as for the expected abstract target for CoreCompile, Adding the target Imports for old conventions do not work for me. It is very problematic and doesn't appear to be correct anyway.

I recommend using a pseudo target to get around it asking for the built-in default target for now.

<Target Name="CoreCompile" />

sharwell commented 6 years ago

@Latency Can you be more specific? The code you referenced as broken is currently used in several projects, including Roslyn where I spend the most time. We have observed a few rare edge cases where things don't work right, but generally that appears to be a required inclusion.

Latency commented 6 years ago

@sharwell I managed to get something to work within the past 24 hrs.. but it does not contain that target rule like mentioned in my previous thread. That target breaks my project.

Here is what I have:

<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
  <PropertyGroup>
    <LanguageTargets Condition="Exists('$(MSBuildProjectDirectory)\$(AssemblyName).csproj')">$(MSBuildToolsPath)\Microsoft.CSharp.targets</LanguageTargets>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net471</TargetFramework>
    <StartupObject>HearthStone.UnInstaller.App</StartupObject>
    <PackageId>HearthStone.UnInstaller</PackageId>
    <AssemblyName>HearthStone.UnInstaller</AssemblyName>
    <RootNamespace>HearthStone.UnInstaller</RootNamespace>
    <Version>1.0.0</Version>
    <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
    <Reference Include="System.Xaml" />
    <Reference Include="WindowsBase" />
    <!-- App.xaml -->
    <ApplicationDefinition Include="App.xaml" SubType="Designer" Generator="MSBuild:XamlIntelliSenseFileGenerator" />
    <!-- XAML elements -->
    <Page Include="**\*.xaml" SubType="Designer" Generator="MSBuild:Compile" Exclude="App.xaml" />
    <Compile Update="**\*.xaml.cs" SubType="Designer" DependentUpon="%(Filename)" />
    <Compile Update="$(IntermediateOutputPath)**\*.g.cs" Visible="false" />
  </ItemGroup>
</Project>

The <LanguageTargets ...> seems to be a key component to getting this to work, along with using the appropriate referenced assemblies to be included for XAML.

DNF-SaS commented 6 years ago

Could you replace your <LanguageTargets ...> with <LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets> It makes it compile on my machine, maybe it helps you, too.

sharwell commented 6 years ago

@DNF-SaS Interesting, I missed that difference. I use the same form you did: https://github.com/sharwell/ILSpy/blob/88616e828f7721fa793b3a551667984dceda5bcb/ILSpy/ILSpy.csproj#L37-L40

clairernovotny commented 6 years ago

@Latency I would strongly suggest you use my MSBuild.Sdk.Extras package instead of adding that code to your own project. I have the WPF workarounds/"stuff" in there based on @sharwell and others' work. If there's other issues/enhancements, I'm happy to take PR's to fix.

My objective with that package is to fill the box and make it clean/simple to use with SDK style projects until there's direct support from Microsoft.

clairernovotny commented 6 years ago

@sharwell I'll give you the same advice too for ILSpy ;) Iet's keep our efforts/workarounds consolidated if we can.

Latency commented 6 years ago

@onovotny I will be happy to give that a try.. It looks solid!

To be clear, I am removing the entire <ItemGroup> section and the <LanguageTargets> line also as shown in my snip-it with this?

On initial compile.. it appears to pass the pre-checks. Since, my project is broke down for reconstruction on a major build ATM, I have lots of debugging to do.. and can not validate this!

What I can tell you is that something is missing from being able to compile the XAML files now after using your .nupkg.

I am getting missing references to override OnClosing() in a few of my XAML files and about 100 less errors than my version which seems suspicious. I am not sure what order the compiler is checking things here.

Q: I assume that I would need to resolve these OnClosing() issues before seeing the other 100 problems with the project that still remain to be debugged?

Q: What am I missing now and which of these rules I posted redundant in your package? (too busy to investigate)

I have had to revert to keeping the rules from my above listed example to finish debugging my project until I can get a more clarity on which rules I must keep in conjunction with your Extensions package.

Q: Is the Workaround .nupkg of yours deprecated and been replaced with the Extensions one now?

Q: For Blend 2017, it appears it is not supporting the new build configuration formats. Any thoughts regarding that? Are we supposed to wait for a fix from Microsoft on this?

bergmeister commented 5 years ago

Tried to convert a XAML project to the new project format today and neither the LanguageTargets nor the MSBuild.Sdk.Extras worked although I also use the latest SDK. Given that .Net Core 3.0 is going to come soon, when are we going to see support for this?