【问题标题】:How do I get the application's directory from my WPF application, at design time?如何在设计时从我的 WPF 应用程序中获取应用程序的目录?
【发布时间】:2009-11-27 13:42:26
【问题描述】:

我如何在设计时从我的 WPF 应用程序中获取应用程序的目录? 我需要在设计时访问我的应用程序当前目录中的资源,而我的 XAML 正在设计器中显示.我无法使用this question 中指定的解决方案,因为在设计时System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)System.Reflection.Assembly.GetExecutingAssembly().Location 都指向IDE 的位置(Visual Studio...Common7 或其他东西)。

应要求进一步阐明我的目标:我想在设计时访问数据库表并显示该数据的图形。设计是在 Visual Studio 2008 中完成的,所以我需要一个非常具体的解决方案来解决一个非常具体的问题,那就是为我的应用程序获取程序集目录。

【问题讨论】:

  • 你是指可执行文件的路径吗?

标签: c# .net wpf working-directory


【解决方案1】:

从您的描述看来,您的代码实际上是在 Visual Studio 的 WPF 设计器中运行的,例如,它是用于设计的自定义控件库的一部分。

在这种情况下,Assembly.GetEntryAssembly() 返回 null,但以下代码获取应用程序目录的路径:

  string applicationDirectory = (
    from assembly in AppDomain.CurrentDomain.GetAssemblies()
    where assembly.CodeBase.EndsWith(".exe")
    select System.IO.Path.GetDirectoryName(assembly.CodeBase.Replace("file:///", ""))
    ).FirstOrDefault();

以下步骤可用于在 VS.NET 2008 的 WPF 设计器工具中演示此工作:

  1. 将此代码放在“WPF 自定义控件库”或“类库”项目中
  2. 添加读取数据库所需的任何代码并返回数据以供显示(在我的例子中,我只是将应用程序目录本身作为字符串返回)
  3. 从您正在设计的项目中引用库项目
  4. 使用 XAML 文件中的自定义控件或类来填充您的 DataContext 或以其他方式向您的 UI 提供数据(在我的例子中,我使用 x:Static 绑定了 DataContext)
  5. 使用“Windows Presentation Foundation Designer”编辑该 XAML 文件,只需双击即可完成,除非您更改了默认编辑器,在这种情况下使用“打开方式...”

当您按照这些步骤操作时,您正在查看的对象将在运行时和设计时以相同的方式填充数据库中的数据。

在其他情况下,同样的技术也同样有效,根据您的需要,还有其他可用的解决方案。如果您的需求与我上面假设的不同,请告诉我们。例如,如果您正在编写一个 VS.NET 插件,那么您就处于一个完全不同的球赛中。

【讨论】:

  • 感谢您将我从痛苦中解救出来。
  • 在 Visual Studio 2010 之后不再有效。
  • - 从AppDomain.CurrentDomain.GetAssemblies() 返回多个可执行文件(.exe 文件),第一个通常是:%ProgramFiles%\Microsoft Visual Studio <Version>\Common7\IDE\XDesProc.exe - Visual Studio 创建设计时应用程序域和 shadow复制 加载的程序集。因此,您的 exe 的路径(从 GetAssemblies() 返回的路径之一,您可以从其名称中识别出来)将类似于:<DriveLetter>:\Users\<UserName>\AppData\Local\Microsoft\VisualStudio\<Version>\Designer\ShadowCache\<TempPath1>\<TempPath2>\YourExecutable.exe
  • 对于 VS 2013 中的设计时也无效
  • .NET 6 有一个新属性,它是 Assembly.Location,但它是为缓存版本设置的,而不是实际的可执行文件。我知道它是加载的可执行文件,但它不是主应用程序路径,所以仍然无法访问应用程序文件。
【解决方案2】:

您是否正在尝试支持设计师(例如视觉工作室设计师或 Blend)?

如果是这样,那么有各种不同的方法来解决这个问题。您通常不想依赖可执行文件的相对路径,因为它可以托管在各种不同的设计工具(VS、Expression Blend 等)中。

也许您可以更全面地解释您要解决的问题,以便我们提供更好的答案?

【讨论】:

    【解决方案3】:

    我认为这是不可能的 - 您正在询问可能尚未构建的程序集的位置。您的设计时代码不会在您的应用程序中运行,并且必须对 IDE 做出一些假设。这对我来说是错误和脆弱的 - 考虑以下问题:

    • 项目建成了吗?
    • 如果没有,就没有可执行文件来获取路径,那怎么办?
    • 如果尚未构建,其他文件是否会存在,或者它们是否构建了人工制品?
    • 如果已经建好了,建在哪里?
    • 您是否需要考虑其他 IDE?

    在这种情况下,您可能应该在设计时要求用户提供或浏览路径,方法是在您的对象上添加一个属性以供他们编辑。然后,您的设计时代码可以使用该属性的值来查找所需的内容。

    【讨论】:

    • “关于 IDE 的假设”?有什么假设?它是 Visual Studio,如果 build conf 是 Release,我想要我项目的 Release 文件夹的路径,否则我想要我的 Debug 文件夹。
    • 我的意思是你需要知道在构建时它将在哪里输出程序集。也许您可以告诉我们更多您想要这样做的原因;或许有更简单的解决方案?
    • 没有更简单的解决方案:我想在设计时访问数据库表并显示该数据的图形。设计是在 Visual Studio 2008 中完成的,所以我需要一个非常具体的解决方案来解决非常具体的问题。
    • 鉴于额外的信息,我添加了更多细节,但这听起来对我来说不是正确的方法,所以我的回答基本上仍然是“不要”。 /耸肩
    • 将数据库文件作为资源包含在项目中是否有意义?这将允许您使用 pack://application 类型语法在您的 xaml 中引用它
    【解决方案4】:

    如果您正在使用装饰器等广泛从事 WPF 设计器工作,请使用“上下文”属性/类型

    详情:- 在设计时你有 modelItem 的实例(我假设它,你知道的)如果没有,那么你可以在 Activate 方法的 Override 实现中实例化它

    //在DesignAdorner类中

    public class DesignAdorner : PrimarySelectionAdornerProvider
    {
          protected override void Activate(ModelItem item)
            {
                    modelItem = item;
            }
    }
    

    现在您可以使用以下单行代码访问当前应用程序路径

    string aplicationPathDir = System.IO.Directory.GetParent(modelItem.Context.ToString()).FullName;
    

    如果对您没有帮助,请告诉我。

    【讨论】:

      【解决方案5】:

      好的,鉴于这里的进一步澄清是我会做的。

      与 GraemeF 提出的问题保持一致,做你想做的事是脆弱的,充其量也容易崩溃。

      因此,一般做法是将设计时数据支持视为与运行时数据支持完全不同的方法。很简单,您在设计时环境和该数据库之间创建的耦合是一个坏主意。

      为了简单地为可视化提供设计时数据,我更喜欢使用一个遵循通用接口的模拟类作为运行时类。这为我提供了一种显示数据的方法,我可以确保这些数据是正确的类型并且符合与我的运行时对象相同的合同。然而,这是一个完全不同的类,用于设计时支持(通常用于单元测试)。

      例如。如果我有一个运行时类需要显示个人详细信息,例如名字、姓氏和电子邮件:

      public class Person()
      {
          public String FirstName { get; set;}
          public String LastName {get; set;}
          public Email EmailAddress {get; set;}
      }
      

      我在运行时从数据库中填充此对象,但还需要提供设计时可视化,我将引入一个 IPerson 接口,该接口定义要遵守的合同,即强制属性 getter 存在:

      public interface IPerson()
      {
          String FirstName { get; }
          String LastName { get; }
          Email EmailAddress { get; }
      }
      

      然后我会更新我的运行时 Person 类来实现接口:

      public class Person() : IPerson
      {
      public String FirstName { get; set;}
      public String LastName {get; set;}
      public Email EmailAddress {get; set;}
      }
      

      然后我会创建一个模拟类来实现相同的接口并为设计时使用提供合理的值

      public MockPerson() : IPerson
      {
      public String FirstName { get { return "John"; } }
      public String LastName { get { return "Smith"; } } 
      public Email EmailAddress { get { return new Email("John@smith.com"); } }
      }
      

      然后我将实现一种机制,在设计时提供 MockPerson 对象,在运行时提供真实的 Person 对象。类似于thisthis。这提供了设计时数据支持,而没有运行时和设计时环境之间的硬依赖。

      这种模式更加灵活,允许您在整个应用程序中提供一致的设计时数据支持。

      【讨论】:

      • 为什么我必须接受模拟解决方案?我不想在设计时看到那个表充满了垃圾,我希望每次我关注它时都能看到真实数据,甚至在我重新编译我的项目之前。我可能碰巧在项目的其他部分工作,并且实际上需要这些数据来在那里做出决定而无需重新编译。在设计时按我想要的方式显示它,而不是从数据库中收集它。
      猜你喜欢
      • 2018-11-15
      • 2019-01-01
      • 2011-07-28
      • 1970-01-01
      • 1970-01-01
      • 2015-01-23
      • 1970-01-01
      • 2012-03-02
      • 2016-10-15
      相关资源
      最近更新 更多