1

我的 WPF 应用程序从相对于主应用程序可执行文件的位置读取目录结构。

然后它将目录结构可视化为TreeView.

当我只运行应用程序时它工作正常,但是,当我在 XAML 设计器中预览它时,它不起作用。我发现它是从这样的不同位置加载的:

C:\Users\Adam\AppData\Local\Microsoft\VisualStudio\17.0_108779a0\Designer\Cache\1481370356x64DA\

是的,这就是System.AppDomain.CurrentDomain.BaseDirectory财产的价值。

我从assembly.Location.

我知道很久以前有人问过类似的问题,答案对以前的 Visual Studio 版本和以前的 .NET 版本有效。它们都不适用于 .NET 6 和 Visual Studio 2022。请不要将其标记为这些问题的重复。

要验证这些答案不适用于 .NET 6 - 只需创建一个新的 .NET 6 WPF 项目,在视图执行的任何代码中插入以下代码:

throw new Exception($"Path: {System.AppDomain.CurrentDomain.BaseDirectory}");

重新加载设计器,你会明白我在说什么。

我已经找到了一个非常丑陋的解决方法,但它只是hhhhhhideous!当我故意抛出并捕获异常时。我将在堆栈跟踪中获得我的代码的路径。然后我可以从那里提取我的项目目录,然后在项目文件中找到输出路径,就是这样。但是来吧!必须有更清洁的方法!

更新: 这是hhhhhhideous hack:

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");
    }

}

我测试了它并且它有效。但这只是一个残暴的可憎之物,应该被火烧死。

4

1 回答 1

1

假设你有一个这样的 DataContext 类:

public class ViewModel
{
    public string Path { get; set; }
    public ViewModel()
    {
        Path = AppDomain.CurrentDomain.BaseDirectory;
    }
}

如果您对此 Datacontext 进行绑定,例如这样:

<Grid>
    <TextBlock Text="{Binding Path}"></TextBlock>
</Grid>

实际上,在设计器和运行时之间找到了不同的路径。以下是通常适用于此类问题的解决方案:

创建一个派生自 DataContext 类的类,并设置一个测试值(仅对 Designer 上下文有效):

public class DesignViewModel : ViewModel
{
    public DesignViewModel()
    {
        Path = "Path for Designer only";
    }
}

然后,使用这个类来设置 Designer Datacontext:

d:DataContext="{d:DesignInstance Type=local:DesignViewModel, IsDesignTimeCreatable=True}"

这是一种通过强制为 Designer 设置您想要的值来解决问题的方法。

更新

如果您需要在编译时(而不是设计时)检索路径,CallerFilePathAttribute可能会很有趣。

例子:

public static string GetCompilationPath([System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "")
{
    return sourceFilePath;
}
于 2022-01-21T21:41:09.650 回答