【问题标题】:Relative URI not working in BitmapImage相对 URI 在 BitmapImage 中不起作用
【发布时间】:2016-11-28 07:57:13
【问题描述】:

我有一种通过绑定加载图像供以后使用的方法。

当我按如下方式设置它时,它可以工作:

public static ImageSource LoadImage()
{
    return new BitmapImage(
        new Uri("C:/User/Name/Documents/Visual Studio 2015/Projects/Project/bin/debug/subfolder/sprite.png", UriKind.Absolute));
}

但是,当我换成简单使用时:

public static ImageSource LoadImage()
{
    return new BitmapImage(
        new Uri("/subfolder/sprite.png", UriKind.Relative));
}

XAML 不显示图像。我查看了Environment.CurrentDirectory,它返回"C:/User/Name/Documents/Visual Studio 2015/Projects/Project/bin/debug/"

我用谷歌搜索了一个解决方案,但似乎什么也没看到。

【问题讨论】:

  • 你确定图片在那里?路径是否正确(您是否尝试过./sub/spritesub/sprite)?您是否尝试过将精灵放入项目中,并确保将其设置为“始终复制”或“更新时复制”?
  • 是的,我相信它就在那里,因为它在我使用绝对路径时有效,而不是在尝试相对路径时。我也尝试过前导句号,并删除前导斜杠,没有运气。
  • 我似乎无法让相对的Uri 对象正常工作。它仅在我使用绝对路径时才有效。
  • 解决方法:return new BitmapImage(new Uri(System.IO.Path.GetFullPath(@"/subfolder/sprite.png")));

标签: c# wpf xaml


【解决方案1】:

如果您希望图像位于输出文件夹中。将图像添加到项目中,在 Build Action 中选择“Content”,然后在 Copy to output directory 选项中选择“Copy Always”。

您将在输出中获得您的图像,并且您将能够使用您的相对 URI 命令加载它。

【讨论】:

    【解决方案2】:

    您应该从程序集资源文件中加载图像。

    确保图像文件位于名为 subfolder 的 Visual Studio 项目文件夹中,并将其构建操作设置为 Resource。然后通过Resource File Pack URI加载它:

    public static ImageSource LoadImage()
    {
        return new BitmapImage(new Uri("pack://application:,,,/subfolder/sprite.png"));
    }
    

    【讨论】:

      【解决方案3】:

      虽然我看到了你的评论,但我可以肯定地告诉你,你需要删除前导斜杠。

      这个语法没有问题:

      return new BitmapImage(new Uri("subfolder/sprite.png", UriKind.Relative));
      

      我不会完全相信Environment.CurrentDirectory,因为我不知道 BitmapImage 是如何处理相对 uri 的。

      您可以尝试手动运行可执行文件,确保“子文件夹”在同一文件夹中。

      当然,另一种选择是获取完整路径:

      var fullPath = Path.GetFullPath("subfolder/sprite.png");
      return new BitmapImage(new Uri(fullPath, UriKind.Absolute));
      

      【讨论】:

        【解决方案4】:

        查看具有不同路径定义的示例:

            public class ImageUriViewModel
            {
                public Uri AbsoluteUri { get; }
                public Uri RelativeUri { get; }
                public Uri AbsoluteFromRelativeUri { get; }
                public Uri AssemblyRelativeUri { get; }
                public ImageSource AbsoluteImage { get; }
                public ImageSource RelativeImage { get; }
                public ImageSource AbsoluteFromRelativeImage { get; }
                public ImageSource AssemblyRelativeImage { get; }
        
                public ImageUriViewModel()
                {
                    // Full path on my computer.
                    string AbsolutePath = "C:/Users/EldHa/Мои проекты/CyberForurm/CyberForurmWpf/Febr20y/bin/Debug/Images/sprite.png";
        
                    // Relative path on my computer.
                    // Note: there is no leading slash!
                    // The leading slash is interpreted as the path from the Root folder on the current drive.
                    string RelativePath = "Images/sprite.png";
        
        
                    AbsoluteUri = new Uri(AbsolutePath, UriKind.Absolute);
                    RelativeUri = new Uri(RelativePath, UriKind.Relative);
                    AbsoluteFromRelativeUri = new Uri(System.IO.Path.GetFullPath(RelativePath));
                    AssemblyRelativeUri = new Uri(System.IO.Path.GetFullPath(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), RelativePath)));
        
                    AbsoluteImage = GetImage(AbsoluteUri);
                    RelativeImage = GetImage(RelativeUri);
                    AbsoluteFromRelativeImage = GetImage(AbsoluteFromRelativeUri);
                    AssemblyRelativeImage = GetImage(AssemblyRelativeUri);
                }
        
                private static ImageSource GetImage(Uri uri)
                {
                    try
                    {
                        return new BitmapImage(uri);
                    }
                    catch (Exception)
                    {
                        return NotFile;
                    }
                }
        
                private static readonly ImageSource NotFile;
        
                private static readonly double pixelsPerDip = VisualTreeHelper.GetDpi(new UIElement()).PixelsPerDip;
        
                static ImageUriViewModel()
                {
                    FormattedText text = new FormattedText("Not\r\nFile",
                                            CultureInfo.CurrentCulture,
                                            FlowDirection.LeftToRight,
                                            new Typeface("Tahoma"),
                                            12,
                                            Brushes.Black,
                                            pixelsPerDip);
                    Geometry textGeometry = text.BuildGeometry(new Point());
        
                    GeometryDrawing drawing = new GeometryDrawing(null, new Pen(Brushes.Red, 1), textGeometry);
                    NotFile = new DrawingImage(drawing);
                    NotFile.Freeze();
                }
            }
        
            <Window.DataContext>
                <local:ImageUriViewModel/>
            </Window.DataContext>
            <UniformGrid Background="AliceBlue" Columns="1">
                <TextBlock Text="{Binding AbsoluteUri}" />
                <Image Source="{Binding AbsoluteImage}"/>
                <TextBlock Text="{Binding RelativeUri}" />
                <Image Source="{Binding RelativeImage}"/>
                <TextBlock Text="{Binding AbsoluteFromRelativeUri}" />
                <Image Source="{Binding AbsoluteFromRelativeImage}"/>
                <TextBlock Text="{Binding AssemblyRelativeUri}" />
                <Image Source="{Binding AssemblyRelativeImage}" />
            </UniformGrid>
        </Window>
        
        1. 在 XAML 设计器工作室中:
        • AbsoluteUri 和 RelativeUri 工作正常;
        • AbsoluteFromRelativeUri 值相对于文件夹 Windows/System32;
        • AssemblyRelativeUri 的值是相对于带有 cash Studio 的文件夹的。
        1. 当从带有 exe 文件的文件夹启动执行时(包括在 Studio 中运行) - 一切正常。

        2. 当从另一个文件夹启动执行时(例如,桌面上的快捷方式):

        • AbsoluteUri、RelativeUri 和 AssemblyRelativeUri 工作正常;
        • AbsoluteFromRelativeUri 定义相对于当前文件夹的路径。

        【讨论】:

          【解决方案5】:

          您必须从相对 URI 中保留前导 /,这会使路径成为根目录,从而忽略任何基本 URI 的完整目录结构:

          var baseUri = new Uri("C:/User/Name/Documents/Visual Studio 2015/Projects/Project/bin/debug/");
          var relative1 = new Uri("/subfolder/sprite.png", UriKind.Relative);
          var relative2 = new Uri("subfolder/sprite.png", UriKind.Relative);
          
          // file:///C:/subfolder/sprite.png
          Console.WriteLine(new Uri(baseUri, relative1));
          
          // file:///C:/User/Name/Documents/Visual Studio 2015/Projects/Project/bin/debug/subfolder/sprite.png
          Console.WriteLine(new Uri(baseUri, relative2));
          

          Live example


          更新:

          至于BitmapImage 本身:和往常一样,官方的 WPF 帮助确实有一些改进的空间。

          • BaseUri 没有提到它的默认值实际上是pack://application:,,,/ProjectName;component/WindowName.xaml 而不是当前工作目录。
          • 虽然UriSource 有一些示例(混合路径是否已植根),但他们忘记提及这仅在源是嵌入式资源时才有效。他们真的可以放置一个指向this page 的链接。

          如果您不想将图像打包为资源,那么您有两种选择:

          解决方案 1:改用 StreamSource 属性

          当然,StreamSource 属性的文档也很稀缺,只提到在加载图像后应该将CacheOption 设置为OnLoad 以关闭流。但是简单地设置StreamSource 是行不通的,所以完整的解决方案:

          public static ImageSource LoadImage(string relativePath)
          {
              var bmp = new BitmapImage();
              bmp.BeginInit();
              bmp.CacheOption = BitmapCacheOption.OnLoad;
              bmp.StreamSource = File.OpenRead(relativePath);
              bmp.EndInit();
          
              return bmp;
          }
          

          解决方案2:设置BaseUri

          如果您不介意在BaseUri 中覆盖默认的pack://... 值,正确的解决方案如下:

          // as described above relativePath should not be rooted so use
          // just "subfolder/sprite.png" without the leading /
          public static ImageSource LoadImage(string relativePath)
          {
              return new BitmapImage(new Uri(relativePath, UriKind.Relative))
              {
                  BaseUri = new Uri(AppContext.BaseDirectory)
              };
          }
          

          【讨论】:

          • 为什么baseUri地址一定要传,为什么和CurrentDirectory不相关?
          • 我使用特定的baseUri 仅用于演示目的(实际上是 OP 的CurrentDirectory),以明确根路径在与任何内容组合时如何覆盖目录信息。无论如何,大多数 API 都将相对路径与 CurrentDirectory 结合起来。所以原题的实际答案是第一句话,代码只是为了演示/解释。
          • 看OP的评论,return new BitmapImage(new Uri("subfolder/sprite.png", UriKind.Relative));这个不行,你可以自己测试一下。
          • 有时在生产环境中使用相对路径非常有用。让我们关注一个问题:是否可以将相对地址传递给BitmapImage?到目前为止的答案是否定的。你可以用这个来测试:github.com/dovid-lt/relativePathUriWithBitmap
          • @dovid:你是对的,我是过失。事实证明,当在 WPF 窗口中使用时,BaseUri 的默认值是 pack://...,即引用程序集和容器窗口名称的 URI。它在控制台应用程序中对我有用,因为如果没有任何上下文,BaseUrinull。我更新了答案。如果它现在回答您的问题,请告诉我。
          【解决方案6】:

          如果您没有在项目中的任何地方使用它并且它被标记为“资源”,它不会在最终构建中包含您的图像。点击您的图片,然后在“构建操作”字段中选择“内容”。

          【讨论】:

            猜你喜欢
            • 2011-02-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-15
            • 2019-12-27
            • 1970-01-01
            • 1970-01-01
            • 2011-05-04
            相关资源
            最近更新 更多