WPF Libraries and Apps Under New csproj Format

Microsoft changed the default Visual Studio project file formats in VS 2015 and then again in VS 2017. The 2015 format — project.json — was well-received by many because it used JSON syntax and had Intellisense support built-in (even for locating software packages to reference in the project). Unfortunately, it was apparently not compatible with the vast pool of older NET Framework projects.

The new and improved 2017 version goes back to its XML roots (which is unfortunate, because XML is much wordier than JSON), but in a drastically slimmed down way. A lot of stuff you used to have to specify in the project file is now just set, by default, by the build environment, making editing the new project files a lot simpler than its XML-based ancestors.

One remaining challenge, though, involves non-mainstream project types, like WPF libraries and applications. Because the project templates for those have not yet been updated, creating a new WPF library or application under VS 2017 uses the old-style, very wordy, XML-based project file format. These can’t be edited from within Visual Studio, unlike their 2017 counterparts, and complicate (I think) incorporating projects by reference (i.e., sharing a project across solutions).

But it is possible to migrate to the 2017 versions. It just requires a bit of legwork. My thanx to Niek Jannink (https://stackoverflow.com/questions/43693591/how-to-migrate-wpf-projects-to-the-new-vs2017-format) and Fruchtzwerg (https://stackoverflow.com/questions/44140673/wpf-app-using-new-csproj-format) for providing a lot of the information needed.

Here’s a 2017-style project file for a WPF library:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets>
    <TargetFramework>net462</TargetFramework>
    <RootNamespace>Olbert.JumpForJoy.WPF</RootNamespace>
    <AssemblyName>Olbert.JumpForJoy.UI</AssemblyName>
  </PropertyGroup>

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

    <Page Include="**\*.xaml" SubType="Designer" Generator="MSBuild:Compile" />
    <Compile Update="**\*.xaml.cs" SubType="Designer" DependentUpon="%(Filename)" />

    <Resource Include="assets\*.*" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="MvvmLightLibs" Version="5.3.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\WpfConverters\WpfConverters.csproj" />
  </ItemGroup>

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

</Project>

Lines 4, 11, 12, 14, 15 and 17 are the important ones here.

Important note: the highlighted line numbers may not correspond to the lines that are discussed in the text. The new Gutenberg editor so badly fouled up the syntax highlighters I was using, for so long, that when they were finally updated by their authors to work with Gutenberg I’d lost track of at least some of the lines I meant to highlight. Sorry!

I’m not sure what line 4 does.

Lines 11 and 12 take care of the Resources object, while line 17 includes static asset files (which in this example are just some image files).

The real magic is in lines 14 and 15, which compile your xaml files (in this case for a user control) and their corresponding code-behind files.

And here’s one for a WPF application:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets>
    <OutputType>winexe</OutputType>
    <TargetFramework>net47</TargetFramework>
    <ApplicationIcon />
    <OutputTypeEx>winexe</OutputTypeEx>
    <StartupObject />
  </PropertyGroup>
  <ItemGroup>
    <EmbeddedResource Update="Properties\Resources.resx" Generator="ResXFileCodeGenerator" LastGenOutput="Resources.Designer.cs" />
    <Compile Update="Properties\Resources.Designer.cs" DesignTime="True" AutoGen="True" DependentUpon="Resources.resx" />
    <Compile Update="Settings.Designer.cs" AutoGen="True" DependentUpon="Settings.settings" />
    <None Update="Settings.settings" LastGenOutput="Settings.Designer.cs" Generator="SettingsSingleFileGenerator" />
    <Page Include="**\*.xaml" SubType="Designer" Generator="MSBuild:Compile" Exclude="App.xaml" />
    <Compile Update="**\*.xaml.cs" SubType="Designer" DependentUpon="%(Filename)" />
    <Resource Include="assets\*.*" />
    <ApplicationDefinition Include="App.xaml">
      <Generator>MsBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </ApplicationDefinition>
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Autofac" Version="4.6.0" />
    <PackageReference Include="Autofac.Extras.CommonServiceLocator" Version="4.0.0" />
    <PackageReference Include="Extended.Wpf.Toolkit" Version="3.0.0" />
    <PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.0.8" />
    <PackageReference Include="MaterialDesignColors" Version="1.1.3" />
    <PackageReference Include="MaterialDesignThemes" Version="2.3.0.823" />
    <PackageReference Include="MvvmLightLibs" Version="5.3.0" />
    <PackageReference Include="Serilog" Version="2.4.0" />
    <PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.0" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\WPFUtilities\J4JUI\J4JUI.csproj" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="System.ComponentModel.DataAnnotations" />
  </ItemGroup>
  <Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
</Project>

Lines 5, 7 and 8 are what make this a WPF application project.

Line 18 is the same as what you saw earlier for a WPF class library, with one critical change: the App.xaml file is excluded. If you don’t do that, the build system will attempt to compile App.xaml.cs twice, causing the compilation to fail.

Lines 23 – 26 are what tell the build system about the entry point to the executable. If you leave them out, the build will fail because it can’t find the entry point needed for an executable file to function.

I’m not sure what Line 50 does.

Two final tweaks you’ll have to make:

  • For both library and executable projects, you’ll have to delete the AssemblyInfo.cs file that the old-style WPF project template creates. The information stored in that file is now part of the project file, allowing the build system to create an AssemblyInfo.cs file on-the-fly.
  • To debug executable projects under Visual Studio you’ll have to tweak the project Debug settings (on the project’s property page) to launch the executable, rather than “launch” the project (which is the default). If you don’t make this change, the debugger will complain about the RunCommand property not being set, and refuse to start. I think this is a bug in Visual Studio 2017.

Making the second tweak causes Visual Studio to create a launchSettings.json file under the project’s Properties node. It’s a simple file which looks like this:

{
  "profiles": {
    "ConsoleApp1": {
      "commandName": "Executable",
      "executablePath": "C:\\Programming\\LanHistory\\LanHistory\\bin\\Debug\\net47\\LanHistory.exe"
    }
  }
}

It’s nice to see that json files are still in use… :)

1 thought on “WPF Libraries and Apps Under New csproj Format”

  1. Thanks for your project “templates” for WPF. Running on VS 2017 15.5.0 Preview 5.0 the last step changing the Debug settings “to launch the executable, rather than “launch” the project” seems no longer necessary.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Archives
Categories