Динамическая загрузка изображений в WPF
У меня странная проблема с 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 =...". Если эта строка прокомментирована, программа запускается без исключений, даже если изображение отсутствует. Загрузка изображения только в том случае, если смысл имеет смысл, но тогда изображение должно быть загружено, когда я добавляю элемент управления Image в StackPanel.
Любые ides?
Изменить: Кстати, если вы добавляете изображение в качестве ресурса, строка 'int q =..' не нужна.
Ответы
Ответ 1
Это потому, что Творение было отложено.
Если вы хотите, чтобы изображение было загружено немедленно, вы можете просто добавить этот код в фазу init.
src.CacheOption = BitmapCacheOption.OnLoad;
вот так:
src.BeginInit();
src.UriSource = new Uri("picture.jpg", UriKind.Relative);
src.CacheOption = BitmapCacheOption.OnLoad;
src.EndInit();
Ответ 2
В коде для загрузки ресурса в исполняющей сборке, где мое изображение "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");
Ответ 3
Это странное поведение, и хотя я не могу сказать, почему это происходит, я могу порекомендовать некоторые варианты.
Во-первых, наблюдение. Если вы включите изображение как содержимое в VS и скопируете его в выходной каталог, ваш код работает. Если изображение помечено как "Нет" в VS и вы копируете его, оно не работает.
Решение 1: FileStream
Объект 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: MemoryStream
Это в основном решение 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);
Теперь вы можете изменить файл в системе, если это то, что вам нужно.
Ответ 4
Вы можете попробовать подключить обработчики к различным событиям BitmapImage:
Они могут рассказать вам немного о том, что происходит, в отношении изображения.
Ответ 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);
Просто как это!