How to Multi target a library project with WPF controls - c#

I have a class library project that needs to target .NET 3.5 and .NET 4.0, and the way it is done now is the typical way of creating separate projects per target framework and linking files in each project to the same source.
I would like to take advantage of the new csproj format that has come out with .NET Core projects because multitargeting is much simpler with the new csproj format.
I created a new Class Library (.NET Core) project and started to try porting my existing library over.
I don't really need to target .netcoreapp2.0, so my target frameworks look like this
<PropertyGroup>
<TargetFrameworks>net35;net40</TargetFrameworks>
</PropertyGroup>
and I have the following block of code to help with the .NET 3.5 oddities with the new csproj format.
<PropertyGroup>
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client</FrameworkPathOverride>
</PropertyGroup>
So far so good. Where things started going downhill is the fact that my class library has WPF Controls. I was getting compile errors because it couldn't find System.Windows and other WPF related items.
I found I could add references to other windows assemblies, so I added the following
<ItemGroup>
<Reference Include="PresentationFramework" />
<Reference Include="PresentationCore" />
<Reference Include="WindowsBase" />
</ItemGroup>
This got rid of most of my errors, but now I am getting errors like The name 'InitializeComponent' does not exist in the current context

Some WPF items migrated to a new library System.Xaml starting at .NET 4.0
The error The name 'InitializeComponent' does not exist in the current context is being thrown only when the .NET 4.0 target is being built.
To fix this, the following block needs to be added to the csproj file
<ItemGroup Condition="'$(TargetFramework)'=='net40'">
<Reference Include="System.Xaml" />
</ItemGroup>
Also, the xaml pages need to be built as a page, so the following also needs to be added to the csproj file
All xaml files that need to be compiled as page.
<ItemGroup>
...
<Page Include="Path\to\SomeWindow.xaml" />
<Page Include="Path\to\SomeOtherWindow.xaml" />
...
</ItemGroup>
This will remove the xaml files from your solution explorer, so a workaround was found here that adds the following blocks to get xaml pages built but still showing up in the Solution Explorer.
<ItemGroup>
<Page Update="#(Page)" SubType="Designer" Generator="MSBuild:Compile" />
</ItemGroup>
<ItemGroup>
<None Include="#(Page)" />
</ItemGroup>

Related

Resource in .Net Standard project is not added to DLL

A resource added to a .NET standard project does not get compiled into the DLL.
I'm porting a .NET Framework project to .NET Standard. My original project has some resources, marked as "Build Action : Resource" which are being consumed by other assemblies.
The .NET Standard project file.
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup> *** not sure why this is added by VS2019 **
<None Remove="Resources\ErrorLarge.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\ErrorLarge.png" />
</ItemGroup>
</Project>
DLL content and size does not change with "Build Action" property changed from "None" to "Resource". Naturally my consumer assemblies will return a IOException: Cannot locate resource 'resources/errorlarge.png'. error.
VS2019 - 16.2.4
It sounds like you want EmbeddedResource in your CSProj file rather than Resource.
This seems useful at describing adding & reading them.

Including a .targets file in .csproj to globally define project & MSBuild variables

I need to have a single place to define variables for a solution that has about 13 projects, each having a varying combination of external dependencies from the same few locations. Right now, it's easy enough to include these as a variable in a PropertyGroup, but if something changes (like a version number), we don't want to have to update each project file with that change.
I tried creating a targets file that has the variables that get used from project to project and included it into the csproj file, just before the assembly references. This appears to have worked beautifully in a website project, but not in a class library project. The references are not found.
How am I supposed to do this in a way that's safe and usable across project types? (No, Nuget is not an option in this case.)
Example of the Global Targets file:
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Product1Version>02.01.01</Product1Version>
<Product2Version>03.02.01</Product2Version>
<ReferencesPath>..\..\References</ReferencesPath>
<Product1ReferencePath>$(ReferencesPath)\Product1\$(Product1Version)</Product1ReferencePath>
<Product2ReferencePath>$(ReferencesPath)\Product2\$(Product2Version)</Product2ReferencePath>
</PropertyGroup>
</Project>
Here is an example of how I intend to use this in a csproj file:
<Import Project="..\..\Build\SolutionReferences.targets" Condition="false" />
<ItemGroup>
<Reference Include="Product1">
<SpecificVersion>False</SpecificVersion>
<HintPath>$(Product1ReferencePath)\Product1.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Product2">
<SpecificVersion>False</SpecificVersion>
<HintPath>$(Product2ReferencePath)\Product2.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
Ugh... All I had to do was remove Condition="false" from the Import command. :(

Dotnet build fails for projects containing UserControl (InitializeComponent does not exist in the current context)

I have a project with the .csproj defined using the .NET Core format. The project contains user control files consisting of a .xaml file and an associated .cs file. Previously I got a compilation error on those user controls until I added the following content to the .csproj file:
<ItemGroup>
<Page Include="**\*.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</Page>
<Compile Update="**\*.xaml.cs" SubType="Code" DependentUpon="%(Filename)" />
</ItemGroup>
At that point the solution began to compile within Visual Studio. However, when I attempt to build from the command line using dotnet build, the build fails with the error I had previously seen in Visual Studio: The name 'InitializeComponent' does not exist in the current context.
Any ideas why the solution builds in VS2017 but not using dotnet build, or how I go about fixing this?
EDIT: .csproj content without PackageReferences
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LanguageTargets>$(MSBuildToolsPath)\Microsoft.CSharp.targets</LanguageTargets>
<TargetFramework>net45</TargetFramework>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<Page Include="**\*.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</Page>
<Compile Update="**\*.xaml.cs" SubType="Code" DependentUpon="%(Filename)" />
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
</ItemGroup>
</Project>
Dotnet build fails for projects containing UserControl (InitializeComponent does not exist in the current context)
At this moment, I am afraid you have to use the MSBuild cli instead of using dotnet build. That because XAML files are not supported by the dotnet cli at this moment. MS team is working hard to solve this problem.
Although, The new sdk csproj (net461) works after compiling and deploying, but Visual Studio can't parse .xaml files during development as it expects .NET core views.
So, to resolve this issue, you can use the MSBuild cli instead of dotnet build.
See XAML files are not supported for more details.

How to reference System.Windows.Forms from a net462 targeting SDK project

Is there any possibility of referencing System.Windows.Forms from an Sdk project (VS 2017) that targets net462? Something like:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net462</TargetFrameworks>
...
</PropertyGroup>
<PackageReference Include="System.Windows.Forms" Version="4.0.0" />
...
Or to generalize the question: how can system libraries residing in the GAC be used from Sdk projects? According to this discussion it seems it is not something supported out-of-the-box.
The part that confuses me is that targeting net462 gives the impression that one has access to the full framework. However, if GAC assemblies are an issue, the framework is not so "full" anymore. Thus, my question.
The following compiles for me:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms" />
</ItemGroup>
</Project>
using System.Windows.Forms;
namespace ClassLibrary1
{
public class Class1
{
public Class1()
{
var form = new Form();
}
}
}
Note: Instead of editing the project file directly, you can add these references the same way you would in a Framework project, by right clicking on Dependencies in the Solution Explorer and choosing Add Reference. Click on Assemblies and then Framework, and select the assemblies you want.

Use Newtonsoft library in NetStandard 2.0 class library

I am developing a class library based on the NetStandard 2.0 framework for multiple platform compatibility sakes, and I need to serialize and deserialize objects. So I added a reference to the Newtonsoft library.
The problem is that I have the following exception at runtime:
System.IO.FileNotFoundException: 'Could not load file or assembly 'System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.'
I tried to manually add a reference to the System.ComponentModel.Annotations version 4.2.0.0 but this version is not available.
Is there a way to use Newtonsoft with NetStandard 2.0, or an alternative to perform serialization/deserialization operations?
Update: it seems that adding a reference to System.ComponentModel.Annotations" Version="4.4.1" and rebuilding the solution fixed the problem.
Here is the content of my csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.1" />
</ItemGroup>
</Project>
So I have been looking at referencing Newtonsoft.Json from the .NETStandard 2.0. It's all there and ready in version Newtonsoft.Json.11.0.2.
~/packages/Newtonsoft.Json.11.0.2/
Just reference it in csproj like so...
<Reference Include="Newtonsoft.Json">
<HintPath>..\APAS.WebInterface\packages\Newtonsoft.Json.11.0.2\lib\netstandard2.0\Newtonsoft.Json.dll</HintPath>
</Reference>
The solution of #user9200027 to add a reference didn't work for me.
However referencing as content does work, but it has the side effect of showing up in the solution explorer file list.
But note that if targeting multiple frameworks one should add a condition for the .net standard framework, otherwise it will override the file for the non .net standard frameworks as well.
Here is a sample .csproj entry:
<Content Condition="$(TargetFramework)=='netstandard2.0'"
Include="$(NuGetPackageRoot)\newtonsoft.json\12.0.2\lib\netstandard2.0\Newtonsoft.Json.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Visible>False</Visible>
</Content>

Categories