Domanda

Sto provando a scrivere il codice che caricherà un'immagine da una risorsa, quindi la ritaglierà. Questo codice funziona quando faccio tutto o parte di esso in XAML. Voglio passare da tutto-XAML a tutto-codice, quindi posso riutilizzarlo in più di un posto, con diversi Uris.

Ma quando provo a fare la stessa cosa nel codice, ottengo DirectoryNotFoundException, perché improvvisamente inizia a cercare una cartella sul disco, invece di caricare l'immagine dalla risorsa.

  • Se carico BitmapImage in XAML e quindi creo un CroppedBitmap in XAML, tutto funziona.
  • Se carico BitmapImage in XAML e quindi scrivo il codice per creare un CroppedBitmap da esso, tutto funziona.
  • Se carico BitmapImage nel codice, senza creando un CroppedBitmap da esso, tutto funziona.
  • Ma se carico BitmapImage nel codice e creo un CroppedBitmap nel codice, prova a caricarlo dal filesystem anziché dalle risorse, e ottengo DirectoryNotFoundException.

Esempi di codice sono di seguito. Sono sicuro che sto facendo qualcosa di stupido, ma ho esaminato il tutto tre volte ora (una volta nella mia vera app, una volta in un'app di test e una volta mentre stavo scrivendo questa domanda), e ho ottenuto lo stesso risultati tutte e tre le volte.

Per tutti i seguenti esempi di codice, ho creato una cartella Immagini all'interno del mio progetto e ho aggiunto un'immagine esistente chiamata " elf.png " ;, con proprietà impostate sui valori predefiniti (Build Azione = & Quot; Risorsa & Quot ;; Copia nella directory di output = & Quot; Non copiare & Quot;).


Caso 1: sia BitmapImage che CroppedBitmap in 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>

Questo mostra la porzione ritagliata della bitmap, come previsto.


Caso 2: BitmapImage in XAML; CroppedBitmap in 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>

Costruttore in code-behind:

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

Questo mostra anche la porzione ritagliata della bitmap, come previsto.


Caso 3: BitmapImage nel codice; no CroppedBitmap.

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

Questo mostra l'intera bitmap. Questo non è quello che voglio, ma mi dice che so come scrivere il codice C # per creare il giusto tipo di Uri e caricare un'immagine Bitmap da una risorsa.


Caso 4: BitmapImage e CroppedBitmap nel codice.

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

Per quanto ne so, questo mette insieme gli stessi pezzi di prima. Utilizza il codice che so caricherà una BitmapImage da una risorsa e il codice che conosco ritaglierà una sezione da una BitmapImage caricata. Ma in qualche modo, quando i due vengono messi insieme, si dimentica che la risorsa è lì e tenta di caricare dal disco. Ottengo la seguente eccezione:

  • XamlParseException: " Impossibile creare l'istanza di 'Window1' definita nell'assembly 'WpfApplication8, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null'. L'eccezione è stata generata dal target di una chiamata. Errore nel file di markup 'Window1.xaml' Riga 1 Posizione 13. & Quot;
    • Eccezione interna: TargetInvocationException: " L'eccezione è stata generata dalla destinazione di una chiamata. "
      • Eccezione interna: DirectoryNotFoundException: " Impossibile trovare una parte del percorso 'C: \ svn \ WpfApplication8 \ WpfApplication8 \ bin \ Debug \ Images \ elf.png'. < !> quot;

La traccia dello stack dell'eccezione interno-interno mostra che l'eccezione originale (DirectoryNotFoundException) viene generata dalla linea che crea un'istanza di CroppedBitmap. Non so perché quella riga provi a leggere dal disco, o perché non funziona quando XAML, per quanto posso dire, funziona bene.

Dato che so che XAML sta usando i costruttori senza parametri, ho anche provato la seguente versione, che dovrebbe essere molto più vicina a ciò che fa effettivamente 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;
}

Stessa eccezione, questa volta dalla croppedImage.EndInit(); riga.

Qualche idea su come posso ottenere la versione di tutto il codice per caricare correttamente la risorsa e ritagliare l'immagine? Cosa succede nella versione XAML che è diversa?

È stato utile?

Soluzione

La magia si è rivelata nella proprietà BaseUri di BitmapImage. Apparentemente BaseUri funge da & Quot; directory corrente & Quot; a cui UriSource è relativo.

Quando la mia BitmapImage veniva caricata da XAML, BaseUri veniva magicamente impostato su " pack: // application: ,,, / WpfApplication8; component / window1.xaml " ;. Quando ho modificato lo snippet di codice n. 4 per impostare esplicitamente fullImage.BaseUri sullo stesso valore prima di creare CroppedBitmap, tutto ha funzionato.

Perché ha funzionato da XAML (e da just-BitmapImage-without-CroppedBitmap)

Quindi da dove proviene questo magico valore BaseUri?

BaseUri fa parte di IUriContext interfaccia. IUriContext.BaseUri si trova in due punti negli assiemi WPF e, tra i miei vari esempi, sono riuscito a colpire entrambi . Nessuna meraviglia che ero confuso.

  • BamlRecordReader.ElementInitialize. Il caricatore BAML imposta automaticamente BaseUri ogni volta che carica un elemento che implementa IUriContext. Questo spiega perché i miei esempi n. 1 e n. 2 funzionavano: si stavano caricando dalla risorsa BAML compilata.
  • Image.UpdateBaseUri (chiamato ogni volta che viene modificata la proprietà Source). Questo verifica se l'origine implementa IUriContext e, in tal caso, imposta BaseUri. Questo spiega perché il mio esempio n. 3 ha funzionato: l'inserimento di BitmapImage nella GUI l'ha costretto a ottenere il giusto percorso di ricerca.

Cerca l'immagine nelle risorse EXE solo quando BaseUri è impostato sul pacchetto magico: // URI. Senza questo (come accade quando tutto viene creato nel codice e non inserito nella GUI), guarda solo sul disco.

La correzione

Come notato sopra, ho potuto codificare Hard BaseUri. Ma la classe BaseUriHelper fornisce una soluzione migliore:

fullImage.BaseUri = BaseUriHelper.GetBaseUri(this);

Questo imposta fullImage per avere lo stesso BaseUri della finestra (this). Se questo viene fatto prima di creare CroppedBitmap, tutto funziona.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top