在 WPF 中动态加载图像
Posted
技术标签:
【中文标题】在 WPF 中动态加载图像【英文标题】:Dynamic loading of images in WPF 【发布时间】:2010-10-08 20:19:29 【问题描述】:我有一个 WPF 的奇怪问题,我在运行时从磁盘加载图像并将它们添加到 StackView 容器中。但是,图像没有显示。经过一番调试,我找到了诀窍,但它真的没有任何意义。我制作了一个小型演示应用程序来识别问题:
新建一个WPF项目,粘贴代码如下:
xaml:
<Window x:Class="wpfBug.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<StackPanel Name="sp">
</StackPanel>
</Window>
xaml.cs,粘贴到默认使用下面:
namespace wpfBug
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
public Window1()
InitializeComponent();
private void Window_Loaded(object sender, RoutedEventArgs e)
Image i = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.UriSource = new Uri("picture.jpg", UriKind.Relative);
src.EndInit();
i.Source = src;
i.Stretch = Stretch.Uniform;
//int q = src.PixelHeight; // Image loads here
sp.Children.Add(i);
将图片复制到 bin/Debug 文件夹并命名为 'picture.jpg'
该程序不显示任何内容,除非注释行未注释。
谁能解释我做错了什么,或者为什么会发生这种情况?如果删除图像并运行程序,它会在“int q= ...”行生成异常。如果该行被注释,即使没有图像,程序也会毫无例外地运行。仅在必要时才加载图像,但是当我将图像控件添加到 StackPanel 时应该加载图像。
有什么办法吗?
编辑:顺便说一句,如果您将图像添加为资源,则不需要 'int q = ..' 行。
【问题讨论】:
【参考方案1】:这是一种奇怪的行为,虽然我无法说明为什么会发生这种情况,但我可以推荐一些选项。
首先,观察。如果您将图像作为内容包含在 VS 中并将其复制到输出目录,则您的代码可以正常工作。如果图像在 VS 中标记为“无”并且您将其复制过来,则它不起作用。
解决方案 1:文件流
BitmapImage 对象接受 UriSource 或 StreamSource 作为参数。让我们改用 StreamSource。
FileStream stream = new FileStream("picture.png", FileMode.Open, FileAccess.Read);
Image i = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.StreamSource = stream;
src.EndInit();
i.Source = src;
i.Stretch = Stretch.Uniform;
panel.Children.Add(i);
问题:流保持打开状态。如果在此方法结束时关闭它,图像将不会显示。这意味着文件在系统上保持写锁定状态。
解决方案 2:内存流
这基本上是解决方案 1,但您将文件读入内存流并将该内存流作为参数传递。
MemoryStream ms = new MemoryStream();
FileStream stream = new FileStream("picture.png", FileMode.Open, FileAccess.Read);
ms.SetLength(stream.Length);
stream.Read(ms.GetBuffer(), 0, (int)stream.Length);
ms.Flush();
stream.Close();
Image i = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.StreamSource = ms;
src.EndInit();
i.Source = src;
i.Stretch = Stretch.Uniform;
panel.Children.Add(i);
现在您可以修改系统上的文件,如果您需要的话。
【讨论】:
【参考方案2】:您可以尝试将处理程序附加到 BitmapImage 的各种事件:
DecodeFailed
DownloadFailed
DownloadCompleted
就图像而言,他们可能会告诉您一些关于正在发生的事情。
【讨论】:
我试过了,没有触发任何事件。我对所有测试都使用相同的图像,所以图像btw没有任何问题;)【参考方案3】:这是因为创造被推迟了。 如果你想立即加载图片,你可以简单地将这段代码添加到初始化阶段。
src.CacheOption = BitmapCacheOption.OnLoad;
像这样:
src.BeginInit();
src.UriSource = new Uri("picture.jpg", UriKind.Relative);
src.CacheOption = BitmapCacheOption.OnLoad;
src.EndInit();
【讨论】:
我对此进行了测试,它可以工作。然而,这证明了框架中存在一个错误,因为如果将 CacheOption 设置为 OnDemand 图像将永远不会加载,即使它应该在屏幕上显示之前加载也是如此。感谢您的提示,我没有注意到该属性。 +1 ;) 有什么办法+100吗?你救了我的工作。 正确的目标...为我工作:) 修复了 DevExpress 并在 TreeListView 中显示图像 :) 为那个棘手的事情浪费了半天时间。【参考方案4】:在执行程序集中加载资源的代码中,我的图像“Freq.png”位于“图标”文件夹中并定义为“资源”。
this.Icon = new BitmapImage(new Uri(@"pack://application:,,,/"
+ Assembly.GetExecutingAssembly().GetName().Name
+ ";component/"
+ "Icons/Freq.png", UriKind.Absolute));
如果有人愿意,我也做了一个函数...
/// <summary>
/// Load a resource WPF-BitmapImage (png, bmp, ...) from embedded resource defined as 'Resource' not as 'Embedded resource'.
/// </summary>
/// <param name="pathInApplication">Path without starting slash</param>
/// <param name="assembly">Usually 'Assembly.GetExecutingAssembly()'. If not mentionned, I will use the calling assembly</param>
/// <returns></returns>
public static BitmapImage LoadBitmapFromResource(string pathInApplication, Assembly assembly = null)
if (assembly == null)
assembly = Assembly.GetCallingAssembly();
if (pathInApplication[0] == '/')
pathInApplication = pathInApplication.Substring(1);
return new BitmapImage(new Uri(@"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute));
用法:
this.Icon = ResourceHelper.LoadBitmapFromResource("Icons/Freq.png");
【讨论】:
【参考方案5】:这是从 URI 加载图像的扩展方法:
public static BitmapImage GetBitmapImage(
this Uri imageAbsolutePath,
BitmapCacheOption bitmapCacheOption = BitmapCacheOption.Default)
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = bitmapCacheOption;
image.UriSource = imageAbsolutePath;
image.EndInit();
return image;
使用示例:
Uri _imageUri = new Uri(imageAbsolutePath);
ImageXamlElement.Source = _imageUri.GetBitmapImage(BitmapCacheOption.OnLoad);
就这么简单!
【讨论】:
以上是关于在 WPF 中动态加载图像的主要内容,如果未能解决你的问题,请参考以下文章
C# wpf datagrid 动态加载数据后改变单元格颜色bug
Xceed WPF Toolkit - BusyIndicator - 在 C# 中创建动态加载消息,数据模板中的数据绑定