carregamento dinâmico de imagens no WPF
Pergunta
Eu tenho um problema estranho com o WPF, eu estava carregando imagens do disco em tempo de execução e adicioná-los a um contêiner StackView. No entanto, as imagens não foram exibidos. Depois de alguma depuração eu encontrei o truque, mas isso realmente não faz qualquer sentido. Eu fiz um aplicativo de demonstração pequeno para identificar o problema:
Crie um novo projeto WPF, e cole o código da seguinte forma:
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, cole abaixo usings padrão:
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);
}
}
}
Copiar uma imagem para a pasta bin / Debug e chamá-lo 'picture.jpg'
Este programa não exibe nada, a não ser que a linha comentada é comentada.
Alguém pode explicar o que estou fazendo de errado, ou por que isso acontece? Se você remover a imagem e executar o programa gera uma exceção no 'q int = ...' linha. Se essa linha é comentado o programa é executado sem exceções, mesmo se nenhuma imagem está presente. Carregando uma imagem somente se nessesary faz sentido, mas, em seguida, a imagem deve ser carregada quando eu adicionar o controle de imagem para o StackPanel.
Qualquer ides?
Edit:. A propósito, se você adicionar a imagem como um recurso, o 'q int = ..' linha não é necessário
Solução
É porque a criação foi adiada. Se você deseja que a imagem a ser carregado imediatamente, você pode simplesmente adicionar este código para a fase de inicialização.
src.CacheOption = BitmapCacheOption.OnLoad;
como este:
src.BeginInit();
src.UriSource = new Uri("picture.jpg", UriKind.Relative);
src.CacheOption = BitmapCacheOption.OnLoad;
src.EndInit();
Outras dicas
No código de recurso de carga na execução de montagem, onde a minha imagem 'Freq.png' estava na pasta "Ícones" e definido como "Recursos".
this.Icon = new BitmapImage(new Uri(@"pack://application:,,,/"
+ Assembly.GetExecutingAssembly().GetName().Name
+ ";component/"
+ "Icons/Freq.png", UriKind.Absolute));
Também fiz uma função se alguém gostaria que ...
/// <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));
}
Uso:
this.Icon = ResourceHelper.LoadBitmapFromResource("Icons/Freq.png");
Este é um comportamento estranho e embora eu sou incapaz de dizer por que isso está ocorrendo, eu posso recomendar algumas opções.
Em primeiro lugar, uma observação. Se você incluir a imagem como conteúdo em VS e copiá-lo para o diretório de saída, o código funciona. Se a imagem é marcado como Nenhum na VS e você copiá-lo, ele não funciona.
Solução 1: FileStream
O objeto BitmapImage aceita um UriSource ou StreamSource como parâmetro. Vamos uso StreamSource vez.
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);
O problema: estadias fluxo aberto. Se você fechá-lo no final deste método, a imagem não vai aparecer. Isto significa que a estadias de arquivo escrever bloqueado no sistema.
Solução 2: MemoryStream
Este é basicamente solução 1, mas você ler o arquivo em um fluxo de memória e passar esse fluxo de memória como o argumento.
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);
Agora você é capaz de modificar o arquivo no sistema, se isso é algo que você necessita.
Você poderia tentar anexar manipuladores para vários eventos de BitmapImage:
Eles podem dizer-lhe um pouco sobre o que está acontecendo, tanto quanto a imagem está em causa.
Aqui está o método de extensão para carregar uma imagem de 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;
}
Amostra de uso:
Uri _imageUri = new Uri(imageAbsolutePath);
ImageXamlElement.Source = _imageUri.GetBitmapImage(BitmapCacheOption.OnLoad);
Simples assim!