Recursos de imagem WPF

408

Eu venho de uma experiência principalmente na Web e um pouco no Windows Forms. Para um novo projeto, usaremos o WPF. O aplicativo WPF precisará de 10 a 20 pequenos ícones e imagens para fins ilustrativos. Estou pensando em armazená-los no assembly como recursos incorporados. Esse é o caminho certo a seguir?

Como especificar no XAML que um controle de imagem deve carregar a imagem de um recurso incorporado?

driis
fonte

Respostas:

473

Se você usar a imagem em vários locais, vale a pena carregar os dados da imagem apenas uma vez na memória e compartilhá-los entre todos os Imageelementos.

Para fazer isso, crie a BitmapSource as a resource em algum lugar:

<BitmapImage x:Key="MyImageSource" UriSource="../Media/Image.png" />

Em seguida, no seu código, use algo como:

<Image Source="{StaticResource MyImageSource}" />

No meu caso, descobri que precisava definir o Image.pngarquivo para ter uma ação de compilação, Resourcee não apenas Content. Isso faz com que a imagem seja carregada dentro do seu assembly compilado.

Drew Noakes
fonte
6
Seria possível fazer isso dinamicamente? Se eu tiver um número diferente de imagens que gostaria de carregar na inicialização, poderia criar um BitmapSource por imagem e consultá-las da mesma maneira que acima?
Becky Franklin
2
@ Becky - Sim, você poderia, mas se você quiser se referir a eles no Xaml, poderá precisar usar a DynamicResourceextensão de marcação em vez de StaticResource, assumindo que conheceria as chaves no momento da compilação. No WPF, você pode criar dicionários de recursos em tempo de execução. De fato, é o que acontece quando você carrega um documento Xaml, mas não vê o C # equivalente.
de Drew Noakes
9
Algo que acertei: se você adicionar seu recurso de imagem a um dicionário de recursos, não se esqueça de se referir ao dicionário de imagens no XAML do seu componente. Algo como: <UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source = "Dictionary1.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources>
Dan Mitchell
4
Eu costumo acrescentar Width="{Binding Source.PixelWidth, RelativeSource={RelativeSource Self}}" ao Image, pois, caso contrário, muitas vezes vejo imagens aumentando de forma grotesca por algum motivo (como ícones de 16x16 esticados para algo que se parece com 200x200 pixels).
OU Mapper
5
Descobri que, se o BitmapImage for declarado no recurso de um assembly referenciado, o UriSource precisará ser um packURI para que isso funcione. Caso contrário, você descobrirá que pode ver a imagem no seu editor xaml no VS, mas nenhuma imagem durante a depuração. Pacote URIS: msdn.microsoft.com/en-au/library/aa970069(v=vs.100).aspx
falha na programação de
174

Eu descobri que a melhor prática de usar imagens, vídeos etc. é:

  • Altere seus arquivos "Ação de criação" para "Conteúdo" . Certifique-se de verificar Copiar para criar o diretório .
    • Encontrado no menu "Clique com o botão direito" na janela do Solution Explorer.
  • Origem da imagem no seguinte formato:
    • "/ « YourAssemblyName » ; component /« YourPath »/ « YourImage.png » "

Exemplo

<Image Source="/WPFApplication;component/Images/Start.png" />

Benefícios:

  • Os arquivos não são incorporados ao assembly.
    • O Gerenciador de Recursos levantará alguns problemas de estouro de memória com muitos recursos (em tempo de construção).
  • Pode ser chamado entre montagens.
Nuno Rodrigues
fonte
36
Essa mesma abordagem funciona se você incorporar o recurso na montagem, mas precisar definir a "Ação de compilação" como "Recurso".
Ashley Davis
5
Funciona, obrigado. Uma observação para outras pessoas: "componente" é obrigatório "como está", "Imagens" é um caminho relativo de png no projeto. Ou seja, a imagem colocada na raiz será "<Image Source =" / WPFApplication; component / Start.png "/>"
Badiboy 28/10/11
3
Um exemplo de como fazer isso em c # seria bom. (Isso não é um URI válido para que ele não pode ser usado quando a construção de um BitmapImage.)
Vaccano
5
Então, como você faz isso se o arquivo estiver definido como Embedded Resource? Isso não parece funcionar. E não quero incluir a imagem no meu projeto duas vezes. (Eu já estou usando-o como um recurso incorporado.)
BrainSlugs83
2
Onde e como o "componente" entra no caminho. Isso faz parte de alguma especificação?
Triynko
46

Algumas pessoas estão perguntando sobre fazer isso no código e não obtendo uma resposta.

Depois de passar muitas horas pesquisando, encontrei um método muito simples, não encontrei exemplo e, portanto, compartilho o meu aqui, que funciona com imagens. (o meu era um .gif)

Resumo:

Ele retorna um BitmapFrame que os "destinos" do ImageSource parecem gostar.

Usar:

doGetImageSourceFromResource ("[YourAssemblyNameHere]", "[YourResourceNameHere]");

Método:

static internal ImageSource doGetImageSourceFromResource(string psAssemblyName, string psResourceName)
{
    Uri oUri = new Uri("pack://application:,,,/" +psAssemblyName +";component/" +psResourceName, UriKind.RelativeOrAbsolute);
    return BitmapFrame.Create(oUri);
}

Aprendizados:

Pelas minhas experiências, a sequência do pacote não é o problema, verifique seus fluxos e, especialmente, se a leitura pela primeira vez definir o ponteiro para o final do arquivo e você precisar redefinir para zero antes de ler novamente.

Espero que isso poupe as muitas horas que eu gostaria que esta peça tivesse para mim!

Craig
fonte
44

No código para carregar um recurso no assembly em execução em que minha imagem Freq.pngestava na pasta Iconse definida como Resource:

this.Icon = new BitmapImage(new Uri(@"pack://application:,,,/" 
    + Assembly.GetExecutingAssembly().GetName().Name 
    + ";component/" 
    + "Icons/Freq.png", UriKind.Absolute)); 

Eu também fiz uma função:

/// <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 (suposição de que você coloque a função em uma classe ResourceHelper):

this.Icon = ResourceHelper.LoadBitmapFromResource("Icons/Freq.png");

Nota : consulte URIs do pacote MSDN no WPF :
pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml

Eric Ouellet
fonte
novo Uri lança URI inválido: porta inválida especificada.
Steve
Você tem o uri ofensivo?
Eric Ouellet
1
mesmo uri que o seu, exceto que o meu estava sendo executado dentro de um winform hospedado em WPF. E o esquema "pack" ainda não estava registrado quando liguei para o novo Uri.
Steve Steve
1
Opa ... provavelmente foi corrigido para ganhar o WPF hospedado. Eu sinto Muito. Não tentarei consertá-lo porque acho que não é um uso muito comum. Boa sorte!
Eric Ouellet
No meu caso, usar new Uri(@"pack://application:,,,/" + pathInApplication)também fez o truque.
Adam Calvet Bohl
40

Sim, é o caminho certo.

Você pode usar a imagem no arquivo de recurso usando o caminho:

<Image Source="..\Media\Image.png" />

Você deve definir a ação de compilação do arquivo de imagem como "Recurso".

ema
fonte
1
Obrigado por isso. Existe uma maneira de fazer algo semelhante com um ImageSource, essencialmente carregando a imagem uma vez em um dicionário de recursos. Receio que essa abordagem carregue os dados da imagem várias vezes na memória.
Drew Noakes
2
Isso será uma bagunça quando você precisar refatorar seu código. Você precisará alterar manualmente todas as referências de imagem se o documento xaml alterar o espaço para nome. O método descrito por Drew Noakes é muito mais suave e sustentável.
Kasper Holdum
14

Descrição completa de como usar recursos: Arquivos de Recursos, Conteúdo e Dados do Aplicativo WPF

E como referenciá-los, leia "Pack URIs in WPF".

Em resumo, existem meios para referenciar recursos de assemblies referenciados / referenciados.

techfan
fonte
O link parece estar ativo e bom (embora ele diga "Esta documentação está arquivada e não está sendo mantida" ).
Peter Mortensen
5
  1. Visual Studio 2010 Professional SP1.
  2. Perfil do Cliente .NET Framework 4.
  3. Imagem PNG adicionada como recurso nas propriedades do projeto.
  4. Novo arquivo na pasta Recursos criado automaticamente.
  5. Crie ação definida como recurso.

Isso funcionou para mim:

<BitmapImage x:Key="MyImageSource" UriSource="Resources/Image.png" />
JoanComasFdz
fonte
3

Se você estiver usando o Blend , para torná-lo mais fácil e sem problemas para obter o caminho correto para o atributo Origem , basta arrastar e soltar a imagem do painel Projeto no designer.

user42467
fonte
3

Sim, é o caminho certo. Você pode usar imagens no arquivo Recurso usando um caminho:

<StackPanel Orientation="Horizontal">
    <CheckBox  Content="{Binding Nname}" IsChecked="{Binding IsChecked}"/>
    <Image Source="E:\SWorking\SharePointSecurityApps\SharePointSecurityApps\SharePointSecurityApps.WPF\Images\sitepermission.png"/>
    <TextBlock Text="{Binding Path=Title}"></TextBlock>
</StackPanel>
Sanjay Ranavaya
fonte
-5

O seguinte funcionou e as imagens a serem configuradas são recursos nas propriedades:

    var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(MyProject.Properties.Resources.myImage.GetHbitmap(),
                                      IntPtr.Zero,
                                      Int32Rect.Empty,
                                      BitmapSizeOptions.FromEmptyOptions());
    MyButton.Background = new ImageBrush(bitmapSource);
img_username.Source = bitmapSource;
Raghulan Gowthaman
fonte
5
Sem ofensa, mas isso cheira a más práticas.
ShloEmi