Pregunta

Estoy tratando de escribir código que cargue una imagen de un recurso y luego lo recorte. Este código funciona cuando hago todo o parte de él en XAML. Quiero cambiar de all-XAML a all-code, para poder reutilizar esto en más de un lugar, con diferentes Uris.

Pero cuando trato de hacer lo mismo en el código, obtengo una DirectoryNotFoundException, porque de repente comienza a buscar una carpeta en el disco, en lugar de cargar la imagen del recurso.

  • Si cargo la BitmapImage en XAML y luego creo un CroppedBitmap en XAML, todo funciona.
  • Si cargo el BitmapImage en XAML y luego escribo el código para crear un CroppedBitmap a partir de él, todo funciona.
  • Si cargo el BitmapImage en el código, sin crear un CroppedBitmap a partir de él, todo funciona.
  • Pero si cargo la BitmapImage en el código y creo un CroppedBitmap en el código, intenta cargar desde el sistema de archivos en lugar de los recursos, y obtengo un DirectoryNotFoundException.

Los ejemplos de código están a continuación. Estoy seguro de que estoy haciendo algo estúpido, pero ya lo he revisado tres veces (una vez en mi aplicación real, una vez en una aplicación de prueba y una vez mientras escribía esta pregunta), y obtuve lo mismo resultados las tres veces.

Para todos los ejemplos de código siguientes, he creado una carpeta de Imágenes dentro de mi proyecto, y agregué una imagen existente llamada " elf.png " ;, con las propiedades predeterminadas (Build Acción = & Quot; Recurso & Quot ;; Copiar al directorio de salida = & Quot; No copiar & Quot;).


Caso 1: tanto BitmapImage como CroppedBitmap en XAML.

<Window x:Class="WpfApplication8.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">
    <Window.Resources>
        <BitmapImage x:Key="fullImage" UriSource="Images/elf.png"/>
        <CroppedBitmap x:Key="croppedImage" Source="{StaticResource fullImage}"
                       SourceRect="0 0 240 320"/>
    </Window.Resources>
    <Image Source="{StaticResource croppedImage}"/>
</Window>

Esto muestra la parte recortada del mapa de bits, como se esperaba.


Caso 2: BitmapImage en XAML; CroppedBitmap en código subyacente.

XAML:

<Window x:Class="WpfApplication8.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">
    <Window.Resources>
        <BitmapImage x:Key="fullImage" UriSource="Images/elf.png"/>
    </Window.Resources>
    <Image Name="image"/>
</Window>

Constructor en código subyacente:

public Window1()
{
    InitializeComponent();
    var fullImage = (BitmapImage) FindResource("fullImage");
    var croppedImage =
        new CroppedBitmap(fullImage, new Int32Rect(0, 0, 240, 320));
    image.Source = croppedImage;
}

Esto también muestra la parte recortada del mapa de bits, como se esperaba.


Caso 3: BitmapImage en código; no CroppedBitmap.

public Window1()
{
    InitializeComponent();
    var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
    var fullImage = new BitmapImage(uri);
    image.Source = fullImage;
}

Esto muestra el mapa de bits completo. Esto no es lo que quiero, pero me dice que sé cómo escribir código C # para crear el tipo correcto de Uri y cargar una BitmapImage desde un recurso.


Caso 4: BitmapImage y CroppedBitmap en código.

    public Window1()
    {
        InitializeComponent();
        var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
        var fullImage = new BitmapImage(uri);
        var croppedImage =
            new CroppedBitmap(fullImage, new Int32Rect(0, 0, 240, 320));
        image.Source = croppedImage;
    }

Por lo que puedo decir, esto solo reúne las mismas piezas que antes. Utiliza código que sé que cargará una imagen de mapa de bits de un recurso, y código que sé que recortará una sección de una imagen de mapa de bits cargada. Pero de alguna manera, cuando los dos se juntan, se olvida que el recurso está allí e intenta cargarse desde el disco. Recibo la siguiente excepción:

  • XamlParseException: " No se puede crear una instancia de 'Window1' definida en el ensamblado 'WpfApplication8, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null'. El destino de una invocación ha lanzado una excepción. Error en el archivo de marcado 'Window1.xaml' Línea 1 Posición 13. & Quot;
    • Excepción interna: TargetInvocationException: " El destino de una invocación ha lanzado una excepción. "
      • Excepción interna: DirectoryNotFoundException: " No se pudo encontrar una parte de la ruta 'C: \ svn \ WpfApplication8 \ WpfApplication8 \ bin \ Debug \ Images \ elf.png'. < !> quot;

El seguimiento de la pila de excepción interna-interna muestra que la excepción original (DirectoryNotFoundException) está siendo lanzada por la línea que crea una instancia de CroppedBitmap. No sé por qué esa línea intentaría leer desde el disco, o por qué no funciona cuando el XAML tan lejos como puedo decir funciona bien.

Como sé que XAML está utilizando los constructores sin parámetros, también probé la siguiente versión, que debería estar mucho más cerca de lo que realmente hace XAML:

public Window1()
{
    InitializeComponent();
    var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
    var fullImage = new BitmapImage();
    fullImage.BeginInit();
    fullImage.UriSource = uri;
    fullImage.EndInit();
    var croppedImage = new CroppedBitmap();
    croppedImage.BeginInit();
    croppedImage.Source = fullImage;
    croppedImage.SourceRect = new Int32Rect(0, 0, 240, 320);
    croppedImage.EndInit();
    image.Source = croppedImage;
}

Misma excepción, esta vez desde la línea croppedImage.EndInit();.

¿Alguna idea sobre cómo puedo obtener la versión de código completo para cargar correctamente el recurso y recortar la imagen? ¿Qué sucede en la versión XAML que es diferente??

¿Fue útil?

Solución

La magia resultó estar en la propiedad BaseUri de BitmapImage. BaseUri aparentemente sirve como & Quot; directorio actual & Quot; que UriSource es relativo.

Cuando mi BitmapImage se cargaba desde XAML, BaseUri se configuraba mágicamente en " pack: // application: ,,, / WpfApplication8; component / window1.xaml " ;. Cuando modifiqué el fragmento de código # 4 para establecer explícitamente fullImage.BaseUri en el mismo valor antes de crear el CroppedBitmap, todo funcionó.

Por qué funcionó desde XAML (y desde just-BitmapImage-without-CroppedBitmap)

Entonces, ¿de dónde vino este valor mágico de BaseUri?

BaseUri es parte del IUriContext interfaz. IUriContext.BaseUri se establece en dos lugares en los ensamblajes WPF, y entre mis diversos ejemplos, logré presionar ambos . No es de extrañar que estuviera confundido.

  • BamlRecordReader.ElementInitialize. El cargador BAML establece automáticamente BaseUri cada vez que carga un elemento que implementa IUriContext. Esto explica por qué mis ejemplos # 1 y # 2 funcionaron: se estaban cargando desde el recurso BAML compilado.
  • Image.UpdateBaseUri (se llama cada vez que se cambia la propiedad Source). Esto verifica si la Fuente implementa IUriContext, y si es así, establece su BaseUri. Esto explica por qué mi ejemplo # 3 funcionó: empujar la BitmapImage en la GUI lo obligó a obtener la ruta de búsqueda correcta.

Solo busca la imagen en los recursos EXE cuando BaseUri está configurado en el paquete mágico: // URI. Sin eso (como sucede cuando todo se crea en código y no se inserta en la GUI), solo se ve en el disco.

La solución

Como se indicó anteriormente, podría codificar BaseUri. Pero la clase BaseUriHelper proporciona una mejor solución:

fullImage.BaseUri = BaseUriHelper.GetBaseUri(this);

Esto configura fullImage para que tenga la misma BaseUri que la ventana (this). Si esto se hace antes de crear el CroppedBitmap, todo funciona.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top