Why is a generated file (via SourceGenerator) in .NET7-Windows csproj file not included, if a local namespace in xaml file is used? - wpf

The .NET7-Windows project with SourceGenerator e.g. MimeTypes is working.
When adding a local namespace to the WPF-UserControl the generated file is not included anymore.
How can I modify the project file, that all generated files are automatically included?
Reproducable Example:
Example.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<LangVersion>10</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<NoWarn>CA1416</NoWarn>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MimeTypes" Version="2.4.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
UserControl1.xaml:
<UserControl x:Class="Example.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Grid/>
</UserControl>
UserControl1.xaml.cs:
using System.Windows.Controls;
namespace Example
{
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
var x = MimeTypes.GetMimeType("Testfile.exe");
}
}
}
This is working as expected, but when adding:
xmlns:local="clr-namespace:Example"
The generated MimeTypes file is not included anymore, which leads to the compiler error CS0103 (The name 'MimeTypes' does not exist in the current context)

Related

Making Intellisense see resources generated by custom MSBuild target (WPF)

In my net4.8 WPF project I have an Art folder which contains .svg files.
I have created an MSBuild target which:
Invokes inkscape to convert .svg files to .xaml, placing the generated files in the project's intermediate output directory.
Includes the generated .xaml files into the target binary as resources.
Then, in the XAML of my application, I have <Frame> elements that reference the .xaml resources to display the images.
So far, so good; all this works.
The problem is that when I do this, intellisense knows nothing of my generated resource files. I would like intellisense to know those files, and give me suggestions, because the application is bound to have lots of them, so using blind binding-by-name is soon going to be problematic.
Steps to reproduce the problem:
STEP 1: make sure you have a relatively recent version of inkscape installed and accessible via your path.
STEP 2: Create a new C# Windows Desktop "WPF App (.NET Framework)" application project (+solution) targeting .Net Framework 4.8.
STEP 3: Add a XamlFromSvg.targets file with the following content:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Ensure Visual Studio puts the XamlFromSvg item in the Build Action dropdown -->
<ItemGroup>
<AvailableItemName Include="XamlFromSvg"/>
</ItemGroup>
<!-- Instruct MsBuild to include our "Compile" and "Resource" targets in the Build -->
<PropertyGroup>
<BuildDependsOn>
XamlFromSvgCompile;
XamlFromSvgResource;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
<!-- Convert ("compile") .svg to .svg.xaml -->
<Target Name="XamlFromSvgCompile" Condition="#(XamlFromSvg)!=''" BeforeTargets="BeforeBuild;BeforeRebuild"
Inputs="#(XamlFromSvg)"
Outputs="$(IntermediateOutputPath)X\%(XamlFromSvg.Identity).xaml">
<Message Importance="High" Text="XamlFromSvgCompile: %(XamlFromSvg.Identity) -> $(IntermediateOutputPath)X\%(XamlFromSvg.Identity).xaml"/>
<MakeDir Directories="$(IntermediateOutputPath)X\%(XamlFromSvg.RelativeDir)" Condition="!Exists('$(IntermediateOutputPath)X\%(XamlFromSvg.RelativeDir)')"/>
<Exec Command="cmd /c inkscape "%(XamlFromSvg.Identity)" --export-filename="$(IntermediateOutputPath)X\%(XamlFromSvg.Identity).xaml""
Outputs="$(IntermediateOutputPath)X\%(XamlFromSvg.Identity).xaml">
</Exec>
</Target>
<!-- Add .svg.xaml as resource -->
<Target Name="XamlFromSvgResource" Condition="#(XamlFromSvg)!=''">
<ItemGroup>
<GeneratedXamlFromSvg Include="$(IntermediateOutputPath)X\%(XamlFromSvg.Identity).xaml"/>
</ItemGroup>
<Message Importance="High" Text="XamlFromSvgResource: $(IntermediateOutputPath)X\#(XamlFromSvg.Identity).xaml -> %(GeneratedXamlFromSvg.Identity)"/>
<CreateItem Include="%(GeneratedXamlFromSvg.Identity)">
<Output TaskParameter="Include" ItemName="Resource"/>
</CreateItem>
</Target>
<!--This is here just to prevent "unknown item group" warnings. -->
<ItemGroup>
<XamlFromSvg Include="foo" Condition="False"/>
</ItemGroup>
</Project>
STEP 4: Near the end of WpfApp1.csproj, after the <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> line, add the following line:
<Import Project="XamlFromSvg.targets" />
STEP 5: Add an /Art folder with an .svg file (Say, Misc.HamburgerMenu.svg.) You can use the following sample .svg file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg8" width="500" height="500" viewBox="0 0 500 500">
<g id="g6" transform="matrix(27.777778,0,0,27.777778,-83.333334,-81.333336)">
<path d="M 3,6 H 21 V 8 H 3 V 6 m 0,5 h 18 v 2 H 3 v -2 m 0,5 h 18 v 2 H 3 Z" id="path4" />
</g>
</svg>
STEP 6: From within Solution Explorer, right-click on the .svg file and select "Include in project".
STEP 7: Right-click on the .svg file again, select "Properties", and set the Build Action to XamlFromSvg.
STEP 8: In App.xaml insert the following:
<Frame x:Shared="false" x:Key="Icon.Misc.HamburgerMenu" Source="/X/Art/Misc.HamburgerMenu.svg.xaml" />
STEP 9: In MainWindow.xaml, replace <Grid></Grid> with the following:
<ContentControl Content="{StaticResource Icon.Misc.HamburgerMenu}" Margin="50"/>
STEP 10: Build and run; You should see a window displaying your .svg file.
STEP 11: Now go back to App.xaml. If you place the cursor on /X/ and hit Ctrl+Space.
Intellisense will suggest anything but /X/Art/Misc.HamburgerMenu.svg.xaml. Intellisense knows nothing of this resource.
And ideas how to solve this problem or achieve the same result by other means would be welcome.
(However, any ideas aiming to achieve the same result by other means must actually achieve the same result, which is to begin with .svg files and to have these files displayed in a net4.8 WPF application without the need to perform any manual steps whatsoever.)
Note: I know netcore supports .svg files, but I am sticking with net4.8 for now, which does not. And I have heard of 'XAML Islands', it seems like a clunky additional moving part held together by shoestrings, so I do not want to try it.
Also, any suggestions on how to improve XamlFromSvg.targets would be welcome. (I just compiled it from samples, I hardly know what I am doing.)

Where are the settings saved in a .NET 5 WinForms app?

In a .NET Framework WinForms project, there was an App.config file in the project, which was an XML file that contained a configSection that would reference a class in System.Configuration, and a section for the userSettings themselves, like so:
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561944e089">
<section name="MyAppName.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561944e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<MyAppName.Properties.Settings>
<setting name="Test" serializeAs="String">
<value>Some Value</value>
</setting>
</MyAppName.Properties.Settings>
</userSettings>
And this created a file in the build folder with the app name plus .exe.config, as in MyAppName.exe.config.
But when I create a new WinForms project using .NET:
There is no App.config in the solution. I can edit the settings using the project properties:
And I can access these values, and update them using the same Properties object and methods:
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
textBox1.Text = Properties.Settings.Default.Test;
}
private void button1_Click(object sender, EventArgs e)
{
Properties.Settings.Default.Test = textBox1.Text;
Properties.Settings.Default.Save();
}
}
}
And everything seems to work, but when I examine the bin folder, there is no file that I can see for where the values are actually stored.
Where is .NET 5 storing the saved application settings if not in a file in the same folder as the application's exe?
User settings are stored in user.config file in the following path:
%userprofile%\appdata\local\<Application name>\<Application uri hash>\<Application version>
Application settings file are not created by default (unexpectedly), however if you create them manually beside the dll/exe file of your application, the configuration system respect to it. The file name should be <Application name>.dll.config. Pay attention to the file extension which is .dll.config.
You may want to take a look at the source code of the following classes:
LocalFileSettingsProvider (The default setting provider)
ClientSettingsStore
ConfigurationManagerInternal
ClientConfigurationPaths
At the time of writing this answer Application Settings for Windows Forms still doesn't have any entry for .NET 5 and redirects to 4.x documentations.
First of all, this is a known (to .NET team) issue: https://github.com/dotnet/project-system/issues/7772.
Secondly the issue and the solution are pretty much described in your question:
(before) ..there was an App.config file in the project,..
(now) There is no App.config in the solution...
Add the missing app.config and everything will work just like it did before.

Why cannot write log to files when WPF application publishing as a single file

I use Microsoft.Extensions.Hosting and NLog.Web.AspNetCore in WPF. The application run correctly with Debug and Release mode, But when I publish the app as a single file, I found File target does not work when fileName using relative path.
NLog version: 4.6.8
Platform: .NET Core 3
NLog config
<nlog>
<targets>
<default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>
<target xsi:type="File" name="file" fileName="logs/${level}-${shortdate}.log" encoding="utf-8"
layout="${longdate}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="file" final="true"/>
</rules>
</nlog>
I use AddNLog to apply this configuration:
public App()
{
_host = new HostBuilder()
.ConfigureLogging(logBuilder =>
{
logBuilder.ClearProviders()
.SetMinimumLevel(LogLevel.Debug)
.AddNLog("NLog.config");
})
.ConfigureServices((hostContext, services) =>
{
//...
}).Build();
}
Show the MainWindow when application startup:
private void Application_Startup(object sender, StartupEventArgs e)
{
using var serviceScope = _host.Services.CreateScope();
var serviceProvider = serviceScope.ServiceProvider;
_logger = serviceProvider.GetRequiredService<ILogger<App>>();
SetupExceptionHandling();
MainWindow mainWindow = serviceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
_logger.LogInformation($"Application startup at {DateTime.Now} successfully");
}
Publishing as a single file and run it, the log of successful startup is not written to the file, But when i change fileName to an absolute path like /logs/${level}-${shortdate}.log or ${level}-${shortdate}.log, the log can be written.
I try to configure it in code:
var config = new LoggingConfiguration();
var file = new FileTarget("file")
{
FileName = "logs/${shortdate}-${level}.log",
Layout = "${longdate}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}"
};
config.AddRule(LogLevel.Info, LogLevel.Fatal, new BufferingTargetWrapper(file));
return config;
But the result is still the same.
Am I writing something wrong? thanks for your help.
NLog will automatically prefix relative fileName-path with the ${basedir}-layout. See also https://github.com/nlog/nlog/wiki/Basedir-Layout-Renderer
Sadly enough Microsoft decided not to fix AppDomain.BaseDirectory when doing Single File Publish in NetCore 3.1
https://github.com/dotnet/aspnetcore/issues/12621
https://github.com/dotnet/core-setup/issues/7491
The work-around is to explictly specify ${basedir:fixTempDir=true}:
<nlog>
<targets>
<default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>
<target xsi:type="File" name="file" fileName="${basedir:fixtempdir=true}/logs/${level}-${shortdate}.log" encoding="utf-8"
layout="${longdate}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="file" final="true"/>
</rules>
</nlog>
Hopefully Microsoft will fix the illusion with NetCore5

Execute Obfuscar Before Fody.Costura Merge Files

How can I Execute Obfuscar Before Fody.Costura Merge the Files Because Merged Files Are not obfuscated, using Fody.Costura compression or wihout it.
I've downloaded https://github.com/obfuscar/example.git project example for obfuscar, then I've installed Fody and Fody.Costura by nuget, But output example is not obfuscated if i check it with ILSpy project.
https://github.com/icsharpcode/ILSpy (ILSpy project to download compressed files and see dll code)
https://github.com/G4224T/Fody-Costura-Decompress (To decompress fody costura files).
My obfuscar configuration is
<?xml version='1.0'?>
<Obfuscator>
<Var name="InPath" value="." />
<Var name="OutPath" value=".\Obfuscator_Output" />
<Var name="HidePrivateApi" value="true" />
<Var name="KeepPublicApi" value="false" />
<Var name="KeyFile" value=".\test.snk" />
<Module file="$(InPath)\BasicExampleExe.exe" />
<!--<Module file="$(InPath)\BasicExampleLibrary.dll" />-->
</Obfuscator>
And in fody costura I've tried with
<Costura DisableCompression="true" />
and
<Costura DisableCompression="false" />
I want any option for obfuscate and merge files using this projects because are free, Thanks all
I've found a workarround of this, and was to make a new form project in solution that reference the ofuscated dlls and exe, then in this new project install fody.costura nuget package, after it you need to change some configuration and code:
obsfucar.xml
<Obfuscator>
<Var name="InPath" value="." />
<Var name="OutPath" value=".\Obfuscator_Output" />
<Var name="HidePrivateApi" value="true" />
<Var name="KeepPublicApi" value="false" />
<Var name="KeyFile" value=".\test.snk" />
<Module file="$(InPath)\BasicExampleExe.exe">
<!-- You need to ommit afuscate startup form to call it from new project with fody.costura -->
<SkipType name="BasicExampleExe.ExampleUI" />
</Module>
<Module file="$(InPath)\BasicExampleLibrary.dll" />
</Obfuscator>
Then in the Program class of the new project with fody.costura
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ObfuscatorBeforeFodyCostura
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new BasicExampleExe.ExampleUI());//Edit this part with the ofuscated form.
}
}
}
Here the solution project edited:
git clone https://juandrn#bitbucket.org/juandrn/obfuscatorbeforefodycostura.git
Thanks!

package oracle.jbo.server does not exist when running ant in jdeveloper

I have a project to mantain, this project was developed in an old PC (that sadly is not working anymore), now my boss told me to generate an ear using the ant xml that was being used in the old machine.
When I run the ant I see these errors:
Buildfile: D:\JDeveloper\COBLIN-WEB\Branches\QA\Framework-CRE\Common-ViewController\framework-ant.xml
init-framework:
compile-framework-model:
[javac] Compiling 23 source files to D:\JDeveloper\COBLIN-WEB\branches\QA\Framework-CRE\Common-Model\classes
[javac] D:\JDeveloper\COBLIN-WEB\branches\QA\Framework-CRE\Common-Model\src\bo\com\cre\framework\model\adf\entity\BaseEntityCache.java:2: package oracle.jbo.server does not exist
[javac] import oracle.jbo.server.EntityCache;
[javac] ^
[javac] D:\JDeveloper\COBLIN-WEB\branches\QA\Framework-CRE\Common-Model\src\bo\com\cre\framework\model\adf\entity\BaseEntityCache.java:4: cannot find symbol
[javac] symbol: class EntityCache
[javac] public class BaseEntityCache extends EntityCache{
[javac] ^
[javac] D:\JDeveloper\COBLIN-WEB\branches\QA\Framework-CRE\Common-Model\src\bo\com\cre\framework\model\adf\entity\BaseEntityDefImpl.java:2: package oracle.jbo.server does not exist
[javac] import oracle.jbo.server.EntityDefImpl;
[javac] ^
[javac] D:\JDeveloper\COBLIN-WEB\branches\QA\Framework-CRE\Common-Model\src\bo\com\cre\framework\model\adf\entity\BaseEntityDefImpl.java:4: cannot find symbol
[javac] symbol: class EntityDefImpl
[javac] public class BaseEntityDefImpl extends EntityDefImpl{
[javac]
....
The class mentioned in the first error is:
package bo.com.cre.framework.model.adf.entity;
import oracle.jbo.server.EntityCache;
public class BaseEntityCache extends EntityCache{
public BaseEntityCache() {
super();
}
}
The XML run with ant:
<?xml version="1.0" encoding="windows-1252" ?>
<project xmlns="antlib:org.apache.tools.ant" name="Framework" default="all" basedir=".">
<property file="framework-ant.properties"/>
<property file="${coblin.home}/${deploy.config.dir}/libs-ant.properties"/>
<import file="${coblin.home}/${deploy.config.dir}/libs-ant.xml"/>
<path id="classpath-framework">
<pathelement location="${coblin.home}/${framework.model.dir}/classes"/>
<path refid="classpath"/>
</path>
<!-- info para compilar framwork -->
<target name="all-framework" description="Build the project" depends="clean-framework,compile-framework-model,compile-framework-view,jar-framework"/>
<target name="jar-framework" description="generate common-model.jar, common-view-controller.jar">
<jar destfile="${coblin.home}/${coblin.lib.dir}/common-model.jar" basedir="${coblin.home}/${framework.model.dir}/classes">
<manifest>
<attribute name="Manifest-Version" value="1.0"/>
</manifest>
</jar>
<jar destfile="${coblin.home}/${coblin.lib.dir}/common-view-controller.jar" basedir="${coblin.home}/${framework.view.dir}/classes">
<manifest>
<attribute name="Manifest-Version" value="1.0"/>
</manifest>
</jar>
</target>
<target name="init-framework">
<tstamp/>
<mkdir dir="${coblin.home}/${framework.model.dir}/classes"/>
<mkdir dir="${coblin.home}/${framework.view.dir}/classes"/>
</target>
<target name="clean-framework" description="Clean the project Framework">
<delete includeemptydirs="true" quiet="true">
<fileset dir="${coblin.home}/${framework.model.dir}/classes" includes="**/*"/>
<fileset dir="${coblin.home}/${framework.view.dir}/classes" includes="**/*"/>
</delete>
</target>
<target name="compile-framework-model" description="Compile Java source files of Framework" depends="init-framework">
<javac destdir="${coblin.home}/${framework.model.dir}/classes" classpathref="classpath-framework" debug="${javac.debug}" nowarn="${javac.nowarn}"
deprecation="${javac.deprecation}" encoding="Cp1252" source="1.6" target="1.6">
<src path="${coblin.home}/${framework.model.dir}/src"/>
</javac>
<copy todir="${coblin.home}/${framework.model.dir}/classes">
<fileset dir="${coblin.home}/${framework.model.dir}/src">
<patternset refid="copy.patterns"/>
</fileset>
</copy>
</target>
If I compile and run the project from Jdeveloper it runs ok, but I need to generate the ear
What shoulkd I do?
You need to add the libraries needed to compile to the ANT environment. I guess they are in one of the imported files, but the once included are out of date.
Have you changed JDev version?
Anyway, you can create a new ANT build file from the project. This will generate the includes of the libraries used in the project. Then you look for the libraries in your ANT files and exchange them to the libraries in the newly generated ANT build file.
Timo

Resources