Création d'un CroppedBitmap au moment de l'exécution - ne se charge pas à partir d'une ressource

StackOverflow https://stackoverflow.com/questions/1056021

Question

J'essaie d'écrire du code qui chargera une image à partir d'une ressource, puis le rogner. Ce code fonctionne lorsque je le fais tout ou partie en XAML. Je souhaite passer de tout en XAML à tout en code afin de pouvoir réutiliser plus d'un endroit, avec différents Uris.

Mais lorsque j'essaie de faire la même chose dans le code, j'obtiens une exception DirectoryNotFoundException car soudainement, il commence à chercher un dossier sur le disque au lieu de charger l'image à partir de la ressource.

  • Si je charge BitmapImage en XAML, puis que je crée un CroppedBitmap en XAML, tout fonctionne.
  • Si je charge BitmapImage dans XAML, puis écris du code pour créer un CroppedBitmap, tout fonctionne.
  • Si je charge BitmapImage dans le code, sans que en crée un CroppedBitmap, tout fonctionne.
  • Mais si je charge BitmapImage dans le code et que crée un CroppedBitmap dans le code, il essaie de se charger à partir du système de fichiers au lieu des ressources, et j'obtiens une exception DirectoryNotFoundException.

Les exemples de code sont ci-dessous. Je suis sûr que je fais quelque chose de stupide, mais j'ai tout parcouru trois fois (une fois dans ma vraie application, une fois dans une application de test et une fois en rédigeant cette question), et j'ai obtenu le même résultats tous les trois fois.

Pour tous les exemples de code suivants, j'ai créé un dossier Images dans mon projet et y ajouté une image existante appelée " elf.png " ;, avec les propriétés définies par défaut (Build Action = & "Ressource &"; Copier dans le répertoire de sortie = & "Ne pas copier &";).

Cas 1: BitmapImage et 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>

Ceci affiche la partie recadrée du bitmap, comme prévu.

Cas 2: BitmapImage en XAML; CroppedBitmap dans code-behind.

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>

Constructeur en code-behind:

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

Ceci affiche également la partie recadrée du bitmap, comme prévu.

Cas 3: BitmapImage dans le code; pas de CroppedBitmap.

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

Ceci montre l'intégralité du bitmap. Ce n’est pas ce que je veux, mais me dit que je sais écrire le code C # pour créer le bon type d’Uri et charger une image BitmapImage à partir d’une ressource.

Cas 4: BitmapImage et CroppedBitmap dans le code.

    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;
    }

Pour autant que je sache, ceci rassemble les mêmes pièces qu'auparavant. Il utilise du code qui, je le sais, chargera une image BitmapImage à partir d’une ressource, et le code que je sais rognera une section à partir d’une image BitmapImage chargée. Mais d'une manière ou d'une autre, lorsque les deux sont assemblés, il oublie que la ressource est présente et tente de se charger à partir du disque. Je reçois l'exception suivante:

  • XamlParseException: & "Impossible de créer l'instance de 'Window1' définie dans l'assembly 'WpfApplication8, version = 1.0.0.0, Culture = neutre, PublicKeyToken = null". Une exception a été émise par la cible d'un appel. Erreur dans le fichier de balisage 'Window1.xaml' Ligne 1 Position 13. & Quot;
    • Exception interne: TargetInvocationException: & "Une exception a été émise par la cible d'un appel. &";
      • Exception interne: DirectoryNotFoundException: & "Impossible de trouver une partie du chemin" C: \ svn \ WpfApplication8 \ WpfApplication8 \ bin \ Debug \ Images \ elf.png ". !> quot;

La trace de pile d'exception intérieure-intérieure indique que l'exception d'origine (l'exception DirectoryNotFoundException) est émise par la ligne qui instancie le CroppedBitmap. Je ne sais pas pourquoi cette ligne essaierait de lire à partir d'un disque, ou pourquoi cela ne fonctionne pas alors que le code XAML, dans la mesure de ce que je peux dire, est équivalent.

Puisque je sais que le XAML utilise les constructeurs sans paramètre, j'ai également essayé la version suivante, qui devrait être beaucoup plus proche de ce que fait le 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;
}

Même exception, cette fois à partir de la croppedImage.EndInit(); ligne.

Des idées sur la manière dont je peux obtenir la version tout code pour charger correctement la ressource et rogner l'image? Que se passe-t-il dans la version XAML qui est différente??

Était-ce utile?

La solution

La magie s'est avérée être dans la propriété BaseUri de BitmapImage. BaseUri sert apparemment de & Quot; répertoire actuel & Quot; UriSource est relatif à.

Lors du chargement de mon image BitmapImage à partir de XAML, BaseUri était paramétré par magie sur & "pack: // application: ,,", / WpfApplication8; composant / window1.xaml & ".". Lorsque j'ai modifié l'extrait de code n ° 4 pour définir explicitement fullImage.BaseUri sur cette même valeur avant de créer le CroppedBitmap, tout a fonctionné.

Pourquoi cela fonctionnait-il avec XAML (et depuis just-BitmapImage-without-CroppedBitmap)

Alors d'où vient cette valeur magique de BaseUri?

BaseUri fait partie de la IUriContext interface. IUriContext.BaseUri est défini à deux endroits dans les assemblys WPF et, parmi mes différents exemples, j'ai réussi à en toucher les deux . Pas étonnant que je sois confus.

  • BamlRecordReader.ElementInitialize. Le chargeur BAML définit automatiquement BaseUri chaque fois qu'il charge un élément qui implémente IUriContext. Cela explique pourquoi mes exemples n ° 1 et n ° 2 ont fonctionné: ils se chargeaient à partir de la ressource BAML compilée.
  • Image.UpdateBaseUri (appelée chaque fois que la propriété Source est modifiée). Ceci vérifie si la source implémente IUriContext et, si tel est le cas, définit son BaseUri. Ceci explique pourquoi mon exemple 3 a fonctionné: insérer BitmapImage dans l'interface graphique l'a forcé à obtenir le bon chemin de recherche.

Il recherche uniquement l'image dans les ressources EXE lorsque BaseUri est défini sur le pack magique: // URI. Sans cela (comme cela se produit lorsque tout est créé dans le code et non poussé dans l'interface graphique), il ne regarde que sur le disque.

Le correctif

Comme indiqué ci-dessus, je pourrais coder en dur BaseUri. Mais la BaseUriHelper classe fournit une meilleure solution:

fullImage.BaseUri = BaseUriHelper.GetBaseUri(this);

Ceci définit fullImage pour avoir la même BaseUri que la fenêtre (this). Si cela est fait avant de créer le CroppedBitmap, tout fonctionne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top