如何在 WPF 设计器、.NET 6.0 中获取应用程序目录?
How to get the application directory in WPF designer, .NET 6.0?
我的 WPF 应用程序从相对于主应用程序可执行文件的位置读取目录结构。
然后它将目录结构可视化为TreeView
。
当我只 运行 应用程序时它工作正常,但是,当我在 XAML 设计器中预览它时,它不起作用。我发现它是从这样的不同位置加载的:
C:\Users\Adam\AppData\Local\Microsoft\VisualStudio.0_108779a0\Designer\Cache81370356x64DA\
是的,这是 System.AppDomain.CurrentDomain.BaseDirectory
属性 的值。
我从 assembly.Location
得到了类似的位置。
我知道很久以前有人问过类似的问题,其答案对以前的 Visual Studio 版本和以前的 .NET 版本有效。 None 个适用于 .NET 6 和 Visual Studio 2022。请不要将此标记为这些问题的重复项。
要验证这些答案不适用于 .NET 6 - 只需创建一个新的 .NET 6 WPF 项目,
在视图执行的任何代码中插入以下代码:
throw new Exception($"Path: {System.AppDomain.CurrentDomain.BaseDirectory}");
重新加载设计器,你就会明白我在说什么了。
我已经找到了一个非常丑陋的解决方法,但它只是 hhhhhhideous!
当我故意抛出并捕获异常时。我将在
堆栈跟踪。然后我可以从那里提取我的项目目录,然后在项目文件中找到输出路径,就是这样。但是来吧!必须有更清洁的方法!
更新:
这是 hhhhhhideous 技巧:
using System.IO;
using System.Reflection;
using System.Xml;
static class Abominations {
/// <summary>
/// Gets the calling project's output directory in a hideous way (from debug information). Use when all else fails.
/// </summary>
/// <param name="action">Action that throws an exception.</param>
/// <returns>Calling project's output directory.</returns>
public static string GetCallingProjectOutputDirectory(Action action) {
try {
action();
}
catch (Exception exception) {
var stacktrace = exception.StackTrace!.Split(Environment.NewLine).First();
var p1 = stacktrace.IndexOf(" in ") + 4;
var p2 = stacktrace.IndexOf(":line");
var pathLength = p2 - p1;
if (p1 < 0 || p2 < 0 || pathLength < 1) throw new InvalidOperationException("No debug information");
var callingSource = stacktrace[p1..p2];
var directory = new DirectoryInfo(Path.GetDirectoryName(callingSource)!);
FileInfo? projectFile;
do {
projectFile = directory.GetFiles("*.csproj").FirstOrDefault();
if (projectFile is null) directory = directory.Parent!;
}
while (projectFile is null);
var projectXml = new XmlDocument();
projectXml.Load(projectFile.FullName);
var baseOutputPath = projectXml.GetElementsByTagName("BaseOutputPath").OfType<XmlElement>().FirstOrDefault()?.InnerText;
var outputDirectory = directory.FullName;
outputDirectory = baseOutputPath is not null
? Path.GetFullPath(Path.Combine(outputDirectory, baseOutputPath))
: Path.Combine(outputDirectory, "bin");
var buildConfiguration =
Assembly.GetCallingAssembly().GetCustomAttribute<AssemblyConfigurationAttribute>()!.Configuration;
var targetFramework =
projectXml.GetElementsByTagName("TargetFramework").OfType<XmlElement>().FirstOrDefault()!.InnerText;
outputDirectory = Path.Combine(outputDirectory, buildConfiguration, targetFramework);
return outputDirectory;
}
throw new InvalidOperationException("Provided action should throw");
}
}
我测试过它并且有效。但这只是一个残暴的可憎之物,应该被火烧死。
假设您有一个像这样的 DataContext class:
public class ViewModel
{
public string Path { get; set; }
public ViewModel()
{
Path = AppDomain.CurrentDomain.BaseDirectory;
}
}
如果您在此 Datacontext 上进行绑定,例如:
<Grid>
<TextBlock Text="{Binding Path}"></TextBlock>
</Grid>
的确,在设计器和运行时之间发现了一条不同的路径。
以下是通常适用于此类问题的解决方案:
创建一个从您的 DataContext class 派生的 class,并设置一个测试值(仅对 Designer 上下文有效):
public class DesignViewModel : ViewModel
{
public DesignViewModel()
{
Path = "Path for Designer only";
}
}
然后,使用此 class 设置设计器数据上下文:
d:DataContext="{d:DesignInstance Type=local:DesignViewModel, IsDesignTimeCreatable=True}"
这是一种通过强制为设计器设置您想要的值来解决问题的方法。
更新
如果您需要在编译时(而不是设计时)检索路径,CallerFilePathAttribute 可能会很有趣。
示例:
public static string GetCompilationPath([System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "")
{
return sourceFilePath;
}
我的 WPF 应用程序从相对于主应用程序可执行文件的位置读取目录结构。
然后它将目录结构可视化为TreeView
。
当我只 运行 应用程序时它工作正常,但是,当我在 XAML 设计器中预览它时,它不起作用。我发现它是从这样的不同位置加载的:
C:\Users\Adam\AppData\Local\Microsoft\VisualStudio.0_108779a0\Designer\Cache81370356x64DA\
是的,这是 System.AppDomain.CurrentDomain.BaseDirectory
属性 的值。
我从 assembly.Location
得到了类似的位置。
我知道很久以前有人问过类似的问题,其答案对以前的 Visual Studio 版本和以前的 .NET 版本有效。 None 个适用于 .NET 6 和 Visual Studio 2022。请不要将此标记为这些问题的重复项。
要验证这些答案不适用于 .NET 6 - 只需创建一个新的 .NET 6 WPF 项目, 在视图执行的任何代码中插入以下代码:
throw new Exception($"Path: {System.AppDomain.CurrentDomain.BaseDirectory}");
重新加载设计器,你就会明白我在说什么了。
我已经找到了一个非常丑陋的解决方法,但它只是 hhhhhhideous! 当我故意抛出并捕获异常时。我将在 堆栈跟踪。然后我可以从那里提取我的项目目录,然后在项目文件中找到输出路径,就是这样。但是来吧!必须有更清洁的方法!
更新: 这是 hhhhhhideous 技巧:
using System.IO;
using System.Reflection;
using System.Xml;
static class Abominations {
/// <summary>
/// Gets the calling project's output directory in a hideous way (from debug information). Use when all else fails.
/// </summary>
/// <param name="action">Action that throws an exception.</param>
/// <returns>Calling project's output directory.</returns>
public static string GetCallingProjectOutputDirectory(Action action) {
try {
action();
}
catch (Exception exception) {
var stacktrace = exception.StackTrace!.Split(Environment.NewLine).First();
var p1 = stacktrace.IndexOf(" in ") + 4;
var p2 = stacktrace.IndexOf(":line");
var pathLength = p2 - p1;
if (p1 < 0 || p2 < 0 || pathLength < 1) throw new InvalidOperationException("No debug information");
var callingSource = stacktrace[p1..p2];
var directory = new DirectoryInfo(Path.GetDirectoryName(callingSource)!);
FileInfo? projectFile;
do {
projectFile = directory.GetFiles("*.csproj").FirstOrDefault();
if (projectFile is null) directory = directory.Parent!;
}
while (projectFile is null);
var projectXml = new XmlDocument();
projectXml.Load(projectFile.FullName);
var baseOutputPath = projectXml.GetElementsByTagName("BaseOutputPath").OfType<XmlElement>().FirstOrDefault()?.InnerText;
var outputDirectory = directory.FullName;
outputDirectory = baseOutputPath is not null
? Path.GetFullPath(Path.Combine(outputDirectory, baseOutputPath))
: Path.Combine(outputDirectory, "bin");
var buildConfiguration =
Assembly.GetCallingAssembly().GetCustomAttribute<AssemblyConfigurationAttribute>()!.Configuration;
var targetFramework =
projectXml.GetElementsByTagName("TargetFramework").OfType<XmlElement>().FirstOrDefault()!.InnerText;
outputDirectory = Path.Combine(outputDirectory, buildConfiguration, targetFramework);
return outputDirectory;
}
throw new InvalidOperationException("Provided action should throw");
}
}
我测试过它并且有效。但这只是一个残暴的可憎之物,应该被火烧死。
假设您有一个像这样的 DataContext class:
public class ViewModel
{
public string Path { get; set; }
public ViewModel()
{
Path = AppDomain.CurrentDomain.BaseDirectory;
}
}
如果您在此 Datacontext 上进行绑定,例如:
<Grid>
<TextBlock Text="{Binding Path}"></TextBlock>
</Grid>
的确,在设计器和运行时之间发现了一条不同的路径。 以下是通常适用于此类问题的解决方案:
创建一个从您的 DataContext class 派生的 class,并设置一个测试值(仅对 Designer 上下文有效):
public class DesignViewModel : ViewModel
{
public DesignViewModel()
{
Path = "Path for Designer only";
}
}
然后,使用此 class 设置设计器数据上下文:
d:DataContext="{d:DesignInstance Type=local:DesignViewModel, IsDesignTimeCreatable=True}"
这是一种通过强制为设计器设置您想要的值来解决问题的方法。
更新
如果您需要在编译时(而不是设计时)检索路径,CallerFilePathAttribute 可能会很有趣。
示例:
public static string GetCompilationPath([System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "")
{
return sourceFilePath;
}