Configuración de fuente de imagen WPF en código

325

Estoy tratando de configurar el código fuente de una imagen WPF. La imagen está incrustada como un recurso en el proyecto. Al mirar ejemplos, he encontrado el siguiente código. Por alguna razón no funciona, la imagen no aparece.

Al depurar puedo ver que la secuencia contiene los datos de la imagen. ¿Así que qué hay de malo?

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;

El icono se define más o menos así: <Image x:Name="_icon" Width="16" Height="16" />

Torbjørn
fuente
Si la imagen está en una unidad local, <Image Source="some_fully_qualified_path">en el XAML nunca falla.
Laurie Stearn
@LaurieStearn el punto es que no conocemos la ruta y necesitamos código para determinarla. Como alguien nuevo en la programación de la GUI de Windows, tengo que admitir que WinForms parece más atractivo que esta basura de XAML.
Alex Jansen

Respuestas:

416

Después de tener el mismo problema que tú y leer un poco, descubrí la solución: URI de paquete .

Hice lo siguiente en 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;

O más corto, usando otro constructor de BitmapImage:

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

El URI se divide en partes:

  • Autoridad: application:///
  • Ruta: el nombre de un archivo de recursos que se compila en un ensamblado referenciado. La ruta debe cumplir con el siguiente formato:AssemblyShortName[;Version][;PublicKey];component/Path

    • AssemblyShortName: el nombre corto para el ensamblado al que se hace referencia.
    • ; Versión [opcional]: la versión del ensamblado referenciado que contiene el archivo de recursos. Esto se usa cuando se cargan dos o más ensamblados referenciados con el mismo nombre corto.
    • ; PublicKey [opcional]: la clave pública que se utilizó para firmar el ensamblado al que se hace referencia. Esto se usa cuando se cargan dos o más ensamblados referenciados con el mismo nombre corto.
    • ; componente: especifica que se hace referencia al ensamblado al que se hace referencia desde el ensamblaje local.
    • / Ruta: el nombre del archivo de recursos, incluida su ruta, en relación con la raíz de la carpeta de proyecto del ensamblado al que se hace referencia.

Los tres cortes después application:deben ser reemplazados por comas:

Nota: El componente de autoridad de un URI de paquete es un URI incorporado que apunta a un paquete y debe cumplir con RFC 2396. Además, el carácter "/" debe reemplazarse con el carácter "," y los caracteres reservados como "%" y "?" debe ser escapado Vea el OPC para más detalles.

Y, por supuesto, asegúrese de establecer la acción de compilación en su imagen Resource.

Jared Harley
fuente
Sí, esta fue la solución que encontré después de algunas pruebas y errores. Gracias por la explicación detallada. Respuesta aceptada!
Torbjørn el
No sé por qué esto era necesario, sin embargo, y por qué las otras respuestas no funcionó para mí ...
Torbjørn
las otras respuestas en esta y otras preguntas tampoco funcionaron para mí. Este funciona perfectamente. Gracias.
Thomas Stock
99
De acuerdo, pero ¿no hay algún método o clase que el analizador XAML use para convertir la cadena de ruta simple en un ImageSource? ¿No podríamos usar eso?
Qwertie
1
A menudo veo este fragmento copiado en otras publicaciones, donde las personas generalmente ignoran la existencia del BitmapImage(Uri)constructor, lo que ayuda a evitar la necesidad de llamar a BeginInit y EndInit. Lo he agregado aquí.
Clemens
175
var uriSource = new Uri(@"/WpfApplication1;component/Images/Untitled.png", UriKind.Relative);
foo.Source = new BitmapImage(uriSource);

Esto cargará una imagen llamada "Untitled.png" en una carpeta llamada "Imágenes" con su "Acción de compilación" establecida en "Recurso" en un ensamblaje llamado "WpfApplication1".

Simón
fuente
16
Gracias por eso. Un problema que me hizo tropezar como novato en wpf, la imagen debe estar marcada como recurso para que esto funcione.
si618
44
Esta solución me dio una excepción, así que probé la solución de Jared Harley y funcionó ...
Sangram Nandkhile
2
lo más importante es "UriKind.RelativeOrAbsolute" - otros ejemplos siempre arrojaron excepciones
Sven
99
var uriSource = new Uri("Images/Untitled.png", UriKind.Relative);sería suficiente
Hamzeh Soboh
... y mucho más fácil. Gracias Hamzeh
pStan
74

Esto es un poco menos de código y se puede hacer en una sola línea.

string packUri = "pack://application:,,,/AssemblyName;component/Images/icon.png";
_image.Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;
Alex B
fuente
8
Esta es definitivamente la mejor respuesta, utilizando la implementación propia de
.NET
¿Qué pasa si necesitamos establecer el alto y el ancho de la imagen, es decir, icon.png? Porque al usar _image.height y _image.width establece la ventana de imagen, no la imagen real, es decir, icon.png.
secretgenes
41

Muy fácil:

Para configurar dinámicamente la imagen de un elemento de menú, solo haga lo siguiente:

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

... mientras que "icon.ico" puede ubicarse en todas partes (actualmente está ubicado en el directorio 'Recursos') y debe vincularse como Recurso ...

Un bothe
fuente
2
Más corto = mejor. Sin embargo, tuve que configurarlo como @ "/ folder / image.png", pero luego funcionó bien. ¡Gracias!
gjvdkamp
3
Cuando asigno la fuente de imágenes, solo aparece en blanco :(
HaloMediaz
La ruta de @HaloMediaz puede estar equivocada ... por ejemplo, tiene recursos pero puede escribirla Recurso
Rouzbeh Zarandi
Esto funcionó para mí, y mucho más directo que los URI absolutos en las otras respuestas.
Psiloc
16

También puede reducir esto a una línea. Este es el código que utilicé para configurar el icono de mi ventana principal. Se supone que el archivo .ico está marcado como Contenido y se está copiando en el directorio de salida.

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

La forma más simple:

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

Has probado:

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
fuente
13

Esta es mi manera:

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
fuente
8

Aquí hay un ejemplo que establece la ruta de la imagen dinámicamente (imagen ubicada en algún lugar del disco en lugar de compilar 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;
}

Reflexiones sobre los iconos ...

En primer lugar, pensaría que la propiedad Icon solo puede contener una imagen. ¡Pero en realidad puede contener cualquier cosa! Descubrí esto por accidente cuando intenté establecer mediante programación la Imagepropiedad directamente en una cadena con la ruta a una imagen. El resultado fue que no mostraba la imagen, ¡sino el texto real de la ruta!

Esto lleva a una alternativa para no tener que crear una imagen para el icono, sino usar texto con una fuente de símbolo en su lugar para mostrar un simple "icono". El siguiente ejemplo utiliza la fuente Wingdings que contiene un símbolo "disquete". Este símbolo es realmente el personaje <, que tiene un significado especial en XAML, por lo que debemos usar la versión codificada &lt;. ¡Esto funciona como un sueño! A continuación se muestra un símbolo de disquete como un ícono en el elemento del menú:

<MenuItem Name="mnuFileSave" Header="Save" Command="ApplicationCommands.Save">
  <MenuItem.Icon>
    <Label VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Wingdings">&lt;</Label>
  </MenuItem.Icon>
</MenuItem>
asombro
fuente
Pregunta: " La imagen está incrustada como un recurso en el proyecto ", respuesta: " Aquí hay un ejemplo [para una imagen] ubicado en algún lugar del disco en lugar de compilar como recurso ".
minutos
7

Si su imagen está almacenada en un ResourceDictionary, puede hacerlo con solo una línea de código:

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

También hay una forma más simple. Si la imagen se carga como un recurso en XAML, y el código en cuestión es el código subyacente para ese contenido XAML:

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

Pon el marco en un VisualBrush:

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

brush.Visual = frame;

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

Ponga el VisualBrush en 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;

Ahora ponga GeometryDrawing en una imagen de dibujo:

new DrawingImage(drawing);

Coloque esto en su fuente de la imagen, y ¡voilà!

Sin embargo, podrías hacerlo mucho más fácil:

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

Y en código:

BitmapImage image = new BitmapImage { UriSource="/yourassembly;component/YourImage.PNG" };
Arcturus
fuente
Jajaja ¿Por qué hacerlo fácil cuando puede dificultarlo? :) Intentaré su solución simple antes de aceptar ...
Torbjørn
En realidad, esto no me ayudó en absoluto. Tal vez soy estúpido :( No tengo tiempo para mirar más de cerca en este momento (proyecto favorito). Quisiera más respuestas para cuando vuelva a hacerlo :)
Torbjørn
3

También hay una forma más simple. Si la imagen se carga como un recurso en el XAML, y el código en cuestión es el código detrás de ese XAML:

Aquí está el diccionario de recursos para un archivo XAML: la única línea que le interesa es ImageBrush con la clave "PosterBrush"; el resto del código es solo para mostrar el contexto

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

        </ResourceDictionary>
    </UserControl.Resources>

Ahora, en el código detrás, puedes hacer esto

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

Cómo cargar una imagen incrustada en iconos e imágenes de recursos (versión corregida de Arcturus):

Suponga que desea agregar un botón con una imagen. Que deberias hacer

  1. Agregue a los iconos de la carpeta del proyecto y coloque la imagen ClickMe.png aquí
  2. En las propiedades de 'ClickMe.png', establezca 'BuildAction' en 'Resource'
  3. Supongamos que el nombre del ensamblado compilado es 'Company.ProductAssembly.dll'.
  4. Ahora es el momento de cargar nuestra imagen en 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>

Hecho.

Siarhei Kuchuk
fuente
2

Soy nuevo en WPF, pero no en .NET.

Pasé cinco horas intentando agregar un archivo PNG a un "Proyecto de biblioteca de control personalizado de WPF" en .NET 3.5 (Visual Studio 2010) y configurándolo como fondo de un control heredado de imagen.

Nada relacionado con los URI funcionó. No puedo imaginar por qué no hay un método para obtener un URI de un archivo de recursos, a través de IntelliSense, tal vez como:

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

Probé muchos URI y jugué con ResourceManager y los métodos GetManifest de Assembly, pero todos hubo excepciones o valores NULL.

Aquí pongo el código que funcionó para mí:

// 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
fuente
3
Creo que el problema es que a WPF no le gusta el enfoque tradicional de poner recursos en archivos resx. En su lugar, se supone que debe agregar la imagen directamente a su proyecto y establecer su propiedad Build Action en Resource. No sé cuál es la diferencia (en términos de formato de archivo físico) entre los dos enfoques.
Qwertie
1
Asegúrese de que la acción de compilación sea contenido
Jerry Nixon
1

Si ya tiene una transmisión y conoce el formato, puede usar algo como esto:

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

fuente
1

Te perdiste un poco.

Para obtener un recurso incrustado de cualquier ensamblaje, debe mencionar el nombre del ensamblado con su nombre de archivo como he mencionado aquí:

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
fuente
BeginInit () no parece existir en WPF.
Myosotis
Está disponible, consulte el siguiente enlace msdn.microsoft.com/en-us/library/…
maulik kansara