Definindo a fonte da imagem WPF no código

325

Estou tentando definir a fonte de uma imagem WPF no código. A imagem é incorporada como um recurso no projeto. Olhando para os exemplos, criei o código abaixo. Por alguma razão, não funciona - a imagem não aparece.

Ao depurar, posso ver que o fluxo contém os dados da imagem. Então, oque há de errado?

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream("SomeImage.png");
PngBitmapDecoder iconDecoder = new PngBitmapDecoder(iconStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
ImageSource iconSource = iconDecoder.Frames[0];
_icon.Source = iconSource;

O ícone é definido mais ou menos assim: <Image x:Name="_icon" Width="16" Height="16" />

Torbjørn
fonte
Se a imagem estiver em uma unidade local, <Image Source="some_fully_qualified_path">o XAML nunca falha.
Laurie Stearn
@LaurieStearn o ponto principal é que não sabemos o caminho e precisamos de código para determiná-lo. Como alguém novo na programação da GUI do Windows, tenho que admitir que o WinForms parece mais atraente do que essa porcaria de XAML.
Alex Jansen

Respostas:

416

Depois de ter o mesmo problema que você e fazer algumas leituras, descobri a solução - URIs do pacote .

Eu fiz o seguinte no código:

Image finalImage = new Image();
finalImage.Width = 80;
...
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri("pack://application:,,,/AssemblyName;component/Resources/logo.png");
logo.EndInit();
...
finalImage.Source = logo;

Ou mais curto, usando outro construtor BitmapImage:

finalImage.Source = new BitmapImage(
    new Uri("pack://application:,,,/AssemblyName;component/Resources/logo.png"));

O URI é dividido em partes:

  • Autoridade: application:///
  • Caminho: o nome de um arquivo de recurso que é compilado em um assembly referenciado. O caminho deve estar em conformidade com o seguinte formato:AssemblyShortName[;Version][;PublicKey];component/Path

    • AssemblyShortName: o nome abreviado do assembly referenciado.
    • ; Versão [opcional]: a versão do assembly referenciado que contém o arquivo de recursos. Isso é usado quando dois ou mais assemblies referenciados com o mesmo nome abreviado são carregados.
    • ; PublicKey [opcional]: a chave pública usada para assinar o assembly referenciado. Isso é usado quando dois ou mais assemblies referenciados com o mesmo nome abreviado são carregados.
    • ; component: especifica que o assembly a que se refere é referenciado a partir do assembly local.
    • / Path: o nome do arquivo de recurso, incluindo seu caminho, relativo à raiz da pasta do projeto do assembly referenciado.

As três barras depois application:devem ser substituídas por vírgulas:

Nota: O componente de autoridade de um URI de pacote é um URI incorporado que aponta para um pacote e deve estar em conformidade com a RFC 2396. Além disso, o caractere "/" deve ser substituído pelo caractere "," e por caracteres reservados, como "%" e "?" deve ser escapado. Veja o OPC para detalhes.

E, é claro, certifique-se de definir a ação de compilação na sua imagem como Resource.

Jared Harley
fonte
Sim, essa foi a solução que me encontrei após algumas tentativas e erros. Obrigado pela explicação completa. Resposta aceita!
Torbjørn
Eu não sei por que isso era necessário, porém, e por que as outras respostas não funcionou para mim ...
Torbjørn
as outras respostas nesta e em outras perguntas também não funcionaram para mim. Este funciona perfeitamente. Obrigado.
Thomas Stock
9
Ok, mas não existe algum método ou classe que o analisador XAML usa para converter a cadeia de caminho simples em um ImageSource? Não poderíamos simplesmente usar isso?
Qwertie
1
Costumo ver esse trecho copiado para outras postagens, onde as pessoas geralmente ignoram a existência do BitmapImage(Uri)construtor, o que ajuda a evitar a necessidade de chamar BeginInit e EndInit. Eu adicionei aqui.
Clemens
175
var uriSource = new Uri(@"/WpfApplication1;component/Images/Untitled.png", UriKind.Relative);
foo.Source = new BitmapImage(uriSource);

Isso carregará uma imagem chamada "Untitled.png" em uma pasta chamada "Imagens" com sua "Ação de compilação" definida como "Recurso" em um assembly chamado "WpfApplication1".

Simon
fonte
16
Obrigado por isso. Um problema que me tropeçou como um noob para wpf, a imagem deve ser marcada como recurso para que isso funcione.
si618
4
Esta solução Deu-me uma exceção, então eu tentei solução de Jared Harley e funcionou ...
Sangram Nandkhile
2
a coisa mais importante é "UriKind.RelativeOrAbsolute" - outros exemplos sempre jogou exceções
Sven
9
var uriSource = new Uri("Images/Untitled.png", UriKind.Relative);seria suficiente
Hamzeh Soboh
... e muito mais fácil. Obrigado Hamzeh
pStan
74

Isso é um pouco menos de código e pode ser feito em uma única linha.

string packUri = "pack://application:,,,/AssemblyName;component/Images/icon.png";
_image.Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;
Alex B
fonte
8
Esta é definitivamente a melhor resposta, utilizando .NETs própria implementação
joshcomley
e se precisarmos definir a altura e a largura da imagem, ie icon.png? Porque, usando _image.height e _image.width, ele define a janela da imagem e não a imagem real, ou seja, icon.png.
secretgenes
41

Muito fácil:

Para definir a imagem de um item de menu dinamicamente, faça o seguinte:

MyMenuItem.ImageSource = 
    new BitmapImage(new Uri("Resource/icon.ico",UriKind.Relative));

... enquanto "icon.ico" pode estar localizado em qualquer lugar (atualmente está localizado no diretório 'Recursos') e deve estar vinculado como Recurso ...

A Bothe
fonte
2
Menor = melhor. Eu tive que defini-lo como @ "/ folder / image.png", mas depois funcionou bem. Obrigado!
Gjvdkamp 22/02
3
Quando atribuo a fonte de imagem, ela está aparecendo em branco :(
HaloMediaz 3/16/16
Caminho @HaloMediaz pode estar errado .... por exemplo, você tem recursos, mas você pode escrevê-lo Resource
Rouzbeh Zarandi
Isso funcionou para mim e muito mais direto do que os URIs absolutos nas outras respostas.
Psiloc 6/11
16

Você também pode reduzir isso para uma linha. Este é o código que usei para definir o ícone na minha janela principal. Ele assume que o arquivo .ico está marcado como Conteúdo e está sendo copiado para o diretório de saída.

 this.Icon = new BitmapImage(new Uri("Icon.ico", UriKind.Relative));
Payson Welch
fonte
16

Maneira mais simples:

var uriSource = new Uri("image path here");
image1.Source = new BitmapImage(uriSource);
Hasan
fonte
15

Você tentou:

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream("SomeImage.png");
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = iconStream;
bitmap.EndInit();
_icon.Source = bitmap;
Andrew Myhre
fonte
13

Esta é a minha maneira:

internal static class ResourceAccessor
{
    public static Uri Get(string resourcePath)
    {
        var uri = string.Format(
            "pack://application:,,,/{0};component/{1}"
            , Assembly.GetExecutingAssembly().GetName().Name
            , resourcePath
        );

        return new Uri(uri);
    }
}

Uso:

new BitmapImage(ResourceAccessor.Get("Images/1.png"))
IlPADlI
fonte
8

Aqui está um exemplo que define o caminho da imagem dinamicamente (imagem localizada em algum lugar do disco, em vez de criar como recurso):

if (File.Exists(imagePath))
{
    // Create image element to set as icon on the menu element
    Image icon = new Image();
    BitmapImage bmImage = new BitmapImage();
    bmImage.BeginInit();
    bmImage.UriSource = new Uri(imagePath, UriKind.Absolute);
    bmImage.EndInit();
    icon.Source = bmImage;
    icon.MaxWidth = 25;
    item.Icon = icon;
}

Reflexões sobre ícones ...

Primeiro, você pensaria que a propriedade Icon pode conter apenas uma imagem. Mas na verdade pode conter qualquer coisa! Descobri isso por acidente quando tentei programaticamente definir oImage propriedade diretamente para uma seqüência de caracteres com o caminho para uma imagem. O resultado foi que ele não mostrou a imagem, mas o texto real do caminho!

Isso leva a uma alternativa para não precisar criar uma imagem para o ícone, mas use o texto com uma fonte de símbolo para exibir um "ícone" simples. O exemplo a seguir usa a fonte Wingdings que contém um símbolo "disquete". Este símbolo é realmente o personagem <, que tem um significado especial em XAML, portanto, temos que usar a versão codificada &lt;. Isso funciona como um sonho! A seguir, é mostrado um símbolo de disquete como um ícone no item de menu:

<MenuItem Name="mnuFileSave" Header="Save" Command="ApplicationCommands.Save">
  <MenuItem.Icon>
    <Label VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Wingdings">&lt;</Label>
  </MenuItem.Icon>
</MenuItem>
incrédulo
fonte
Pergunta: " A imagem é incorporada como um recurso no projeto ", resposta: " Aqui está um exemplo [de uma imagem] localizado em algum lugar do disco, em vez de criar como recurso ".
mins
7

Se sua imagem estiver armazenada em um ResourceDictionary, você poderá fazê-lo com apenas uma linha de código:

MyImage.Source = MyImage.FindResource("MyImageKeyDictionary") as ImageSource;
Hollyroody
fonte
6

Também há uma maneira mais simples. Se a imagem for carregada como um recurso no XAML e o código em questão for o code-behind desse conteúdo XAML:

Uri iconUri = new Uri("pack://application:,,,/ImageNAme.ico", UriKind.RelativeOrAbsolute);
NotifyIcon.Icon = BitmapFrame.Create(iconUri);
Bharat Thanki
fonte
4

Coloque o quadro em um VisualBrush:

VisualBrush brush = new VisualBrush { TileMode = TileMode.None };

brush.Visual = frame;

brush.AlignmentX = AlignmentX.Center;
brush.AlignmentY = AlignmentY.Center;
brush.Stretch = Stretch.Uniform;

Coloque o VisualBrush em GeometryDrawing

GeometryDrawing drawing = new GeometryDrawing();

drawing.Brush = brush;

// Brush this in 1, 1 ratio
RectangleGeometry rect = new RectangleGeometry { Rect = new Rect(0, 0, 1, 1) };
drawing.Geometry = rect;

Agora coloque o GeometryDrawing em um DrawingImage:

new DrawingImage(drawing);

Coloque isso na sua fonte da imagem e pronto!

Você poderia fazer isso muito mais fácil:

<Image>
    <Image.Source>
        <BitmapImage UriSource="/yourassembly;component/YourImage.PNG"></BitmapImage>
    </Image.Source>
</Image>

E no código:

BitmapImage image = new BitmapImage { UriSource="/yourassembly;component/YourImage.PNG" };
Arcturus
fonte
RI MUITO! Por que se torna mais fácil quando você pode tornar difícil :) Vou tentar a sua solução simples antes de eu aceitar embora ...
Torbjørn
Na verdade, isso não me ajudou em nada. Talvez eu sou estúpido :( não tem tempo para olhar mais de perto agora (projeto de estimação) gostaria de mais respostas para quando eu voltar a ele :).
Torbjørn
3

Também há uma maneira mais simples. Se a imagem é carregada como um recurso no XAML e o código em questão é o código por trás desse XAML:

Aqui está o dicionário de recursos para um arquivo XAML - a única linha com a qual você se importa é o ImageBrush com a chave "PosterBrush" - o restante do código é apenas para mostrar o contexto

<UserControl.Resources>
        <ResourceDictionary>
            <ImageBrush x:Key="PosterBrush" ImageSource="..\Resources\Images\EmptyPoster.jpg" Stretch="UniformToFill"/>

        </ResourceDictionary>
    </UserControl.Resources>

Agora, no código por trás, você pode fazer isso

ImageBrush posterBrush = (ImageBrush)Resources["PosterBrush"];
Mark Mullin
fonte
2

Como carregar uma imagem incorporada em ícones e imagens de recursos (versão corrigida do Arcturus):

Suponha que você queira adicionar um botão com uma imagem. O que você deveria fazer?

  1. Adicione aos ícones das pastas do projeto e coloque a imagem ClickMe.png aqui
  2. Nas propriedades de 'ClickMe.png', defina 'BuildAction' como 'Resource'
  3. Suponha que o nome do seu assembly compilado seja 'Company.ProductAssembly.dll'.
  4. Agora é hora de carregar nossa imagem em XAML

    <Button Width="200" Height="70">
      <Button.Content>
        <StackPanel>
          <Image Width="20" Height="20">
            <Image.Source>
              <BitmapImage UriSource="/Company.ProductAssembly;component/Icons/ClickMe.png"></BitmapImage>
              </Image.Source>
          </Image>
          <TextBlock HorizontalAlignment="Center">Click me!</TextBlock>
        </StackPanel>
      </Button.Content>
    </Button>

Feito.

Siarhei Kuchuk
fonte
2

Eu sou novo no WPF, mas não no .NET.

Passei cinco horas tentando adicionar um arquivo PNG a um "Projeto de Biblioteca de Controle Personalizado WPF" no .NET 3.5 (Visual Studio 2010) e definindo-o como plano de fundo de um controle herdado de imagem.

Nada em relação aos URIs funcionou. Não consigo imaginar por que não existe um método para obter um URI de um arquivo de recurso, por meio do IntelliSense, talvez como:

Properties.Resources.ResourceManager.GetURI("my_image");

Eu tentei muitos URIs e joguei com o ResourceManager e os métodos GetManifest do Assembly, mas todos foram exceções ou valores NULL.

Aqui eu potei o código que funcionou para mim:

// Convert the image in resources to a Stream
Stream ms = new MemoryStream()
Properties.Resources.MyImage.Save(ms, ImageFormat.Png);

// Create a BitmapImage with the stream.
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = ms;
bitmap.EndInit();

// Set as source
Source = bitmap;
JoanComasFdz
fonte
3
Eu acho que o problema é que o WPF não "gosta" da abordagem tradicional de colocar recursos em arquivos resx. Em vez disso, você deve adicionar a imagem diretamente ao seu projeto e definir sua propriedade Build Action como Resource. Não sei qual é a diferença (no termo formato de arquivo físico) entre as duas abordagens.
Qwertie
1
Verifique se a ação de compilação é conteúdo
Jerry Nixon
1

Se você já possui um fluxo e conhece o formato, pode usar algo como isto:

static ImageSource PngStreamToImageSource (Stream pngStream) {
    var decoder = new PngBitmapDecoder(pngStream,
        BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    return decoder.Frames[0];
}

fonte
1

Você perdeu um pouco.

Para obter um recurso incorporado de qualquer assembly, você deve mencionar o nome do assembly com o nome do arquivo, conforme mencionado aqui:

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream(asm.GetName().Name + "." + "Desert.jpg");
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = iconStream;
bitmap.EndInit();
image1.Source = bitmap;
maulik kansara
fonte
BeginInit () parece não existir no WPF.
Myosotis
É verificação disponível no link abaixo msdn.microsoft.com/en-us/library/...
Maulik Kansara