在设计时间内,我该如何从我的WPF应用程序中获取应用程序的目录?

4
我该如何在WPF应用程序中,在设计时获取应用程序的目录?我需要在设计时访问应用程序当前目录中的资源,同时我的XAML正在设计器中显示。我无法使用这个问题中指定的解决方案,因为在设计时,System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)System.Reflection.Assembly.GetExecutingAssembly().Location都指向IDE的位置(Visual Studio... Common7或其他)。
根据我的要求进一步澄清:我想在设计时访问数据库表并显示该数据的图形。设计是在Visual Studio 2008中完成的,因此我需要一个非常具体的解决方案来解决一个非常具体的问题,即获取我的应用程序的程序集目录。

你是指可执行文件的路径吗? - Pieter888
5个回答

6

根据您的描述,似乎您的代码实际上是在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 Designer工具内展示此功能:
  1. 将此代码放入“WPF自定义控件库”或“类库”项目中
  2. 添加必要的代码以读取数据库并返回显示数据(在我的情况下,我只是将应用程序目录本身作为字符串返回)
  3. 从您正在设计的项目中引用库项目
  4. 使用自定义控件或类从XAML文件中填充您的DataContext或以其他方式提供数据给您的UI(在我的情况下,我使用x:Static绑定了DataContext)
  5. 使用“Windows Presentation Foundation Designer”编辑那个XAML文件,这可以通过双击完成,除非您已更改默认编辑器,在这种情况下,请使用“打开方式…”
当您按照这些步骤操作时,您正在查看的对象将在运行时和设计时都以相同的方式使用来自数据库的数据进行填充。
在其他情况下,这种技术同样适用,并且根据您的需求,还有其他解决方案可供选择。如果您的需求与我上面所假设的不同,请告诉我们。例如,如果您正在编写一个VS.NET插件,那么您就处于完全不同的境地。

谢谢你让我摆脱了痛苦。 - Grokodile
3
Visual Studio 2010之后不再有效。 - Perikles C. Stephanidis
1
AppDomain.CurrentDomain.GetAssemblies() 会返回多个可执行文件(.exe 文件),其中第一个通常是:%ProgramFiles%\Microsoft Visual Studio <Version>\Common7\IDE\XDesProc.exeVisual Studio 会创建一个设计时应用程序域,并影子复制已加载的程序集。因此,您的 exe 路径(从 GetAssemblies() 返回的其中一个,您可以通过其名称识别)将类似于:<DriveLetter>:\Users\<UserName>\AppData\Local\Microsoft\VisualStudio\<Version>\Designer\ShadowCache\<TempPath1>\<TempPath2>\YourExecutable.exe - Perikles C. Stephanidis
4
在 VS 2013 的设计时也不适用。 - Beachwalker
.NET 6 中有一个新属性 Assembly.Location,但它仅适用于缓存版本,而非实际可执行文件。我知道它是已加载的可执行文件,但它不是主应用程序路径,因此仍然无法访问应用程序文件。 - Harry

0

如果您正在广泛使用adorner等WPF设计师,请使用“Context”属性/类型

详情: 在设计时间中,您拥有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;

如果这不能帮到你,请告诉我。


0

我认为这是不可能的 - 你正在寻找一个可能尚未构建的程序集的位置。你的设计时代码不在你的应用程序内运行,因此必须对IDE进行一些假设。这让我感到不舒服和脆弱 - 考虑以下问题:

  • 项目是否已经构建?
  • 如果没有,那么就没有可执行文件可以获取路径,那么怎么办?
  • 如果它还没有被构建,其他文件是否存在,或者它们是构建工件?
  • 如果已经构建,它是在哪里构建的?
  • 你需要考虑其他IDE吗?

在这种情况下,你应该在设计时要求用户提供或浏览路径,通过为他们编辑对象添加属性来实现。然后,你的设计时代码可以使用属性的值来查找所需内容。


我的意思是,你需要知道在构建时它将输出装配件的位置。也许你可以告诉我们更多关于你为什么想要这样做的原因;也许问题有一个更简单的解决方案? - GraemeF
没有更简单的解决方案:我想在设计时访问数据库表并显示该数据的图形。设计是在Visual Studio 2008中完成的,因此我需要一个非常具体的解决方案来解决这个非常具体的问题。 - luvieere
数据库表是通用的,还是针对这个项目特定的?换句话说,它是 SQL Server 实例中可供其他项目使用的表,还是使用 DBMS 之外的东西(例如 XML 数据库)的本地表?如果它是通用的,您可以进行硬编码;如果它是针对项目的,我同意 Graeme 的建议,用户可以编辑属性以向您提供路径,这是迄今为止最好的建议。 - Ken White
我可以将项目移动到另一个文件夹中,因此硬编码不可行。数据库是一个Firebird数据库文件,位于项目的Debug目录中。现在我该如何获取它的路径? - luvieere
1
在项目中将数据库文件作为资源包含,这样就可以使用“pack://application”类型语法在xaml中引用它。 - kenwarner
显示剩余2条评论

0

你是否正在尝试支持设计师(例如Visual Studio Designer或Blend)?

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

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


-2

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

为了遵循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。这可以在运行时和设计时环境之间提供硬依赖关系的设计时数据支持。

这种模式更加灵活,并且可以让你在整个应用程序中提供一致的设计时数据支持。


为什么我必须满足于一个模拟解决方案?我不想在设计时看到那个表填充着垃圾数据,我希望每次瞥一眼都能看到真实数据,甚至在重新编译项目之前。我可能需要在项目的其他部分工作,并且实际上需要那些数据来做出决策,而无需重新编译。在设计时,将其与所有相关转换一起显示,比从数据库中获取更加方便。 - luvieere

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接