Создание CroppedBitmap во время выполнения - не загружается из ресурса - PullRequest
3 голосов
/ 29 июня 2009

Я пытаюсь написать код, который будет загружать изображение из ресурса, а затем обрезать его. Этот код работает, когда я делаю все или часть этого в XAML. Я хочу переключиться с all-XAML на all-code, чтобы я мог использовать его более чем в одном месте с разными Uris.

Но когда я пытаюсь сделать то же самое в коде, я получаю исключение DirectoryNotFoundException, потому что внезапно он начинает пытаться искать папку на диске вместо загрузки изображения из ресурса.

  • Если я загружаю BitmapImage в XAML, а затем создаю CroppedBitmap в XAML, все работает.
  • Если я загружаю BitmapImage в XAML, а затем пишу код для создания из него CroppedBitmap, все работает.
  • Если я загружаю BitmapImage в коде, без создания из него CroppedBitmap, все работает.
  • Но если я загружаю BitmapImage в коде и создаю CroppedBitmap в коде, он пытается загрузить из файловой системы вместо ресурсов, и я получаю исключение DirectoryNotFoundException.

Примеры кода приведены ниже. Я уверен, что делаю что-то глупое, но я все это трижды пробежал (один раз в моем реальном приложении, один раз в тестовом приложении и один раз во время написания этого вопроса), и я получил то же самое результаты все три раза.

Для всех следующих примеров кода я создал папку Images внутри своего проекта и добавил туда существующее изображение с именем "elf.png", свойства которого установлены в значения по умолчанию (Build Action = "Resource"; Копировать в Выходной каталог = "Не копировать").


Случай 1: BitmapImage и CroppedBitmap в 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>

Показывает обрезанную часть растрового изображения, как и ожидалось.


Случай 2: BitmapImage в XAML; CroppedBitmap в коде позади.

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>

Конструктор в коде:

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

Здесь также отображается обрезанная часть растрового изображения, как и ожидалось.


Случай 3: BitmapImage в коде; нет CroppedBitmap.

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

Это показывает все растровое изображение. Это не то, что я хочу, но действительно говорит мне, что я знаю, как написать код C #, чтобы создать правильный тип Uri и загрузить BitmapImage из ресурса.


Случай 4: BitmapImage и CroppedBitmap в коде.

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

Насколько я могу судить, это просто собирает те же самые части, что и раньше. Он использует код, который, как я знаю, будет загружать BitmapImage из ресурса, а код, который я знаю, будет обрезать раздел из загруженного BitmapImage. Но каким-то образом, когда они объединены, он забывает о наличии ресурса и пытается загрузить его с диска. Я получаю следующее исключение:

  • XamlParseException: "Невозможно создать экземпляр 'Window1', определенный в сборке 'WpfApplication8, Version = 1.0.0.0, Culture = нейтральный, PublicKeyToken = null'. Исключение было выдано целью вызова. Ошибка в файле разметки ' Window1.xaml 'Line 1 Position 13 ".
    • Внутреннее исключение: TargetInvocationException: «Исключение было сгенерировано целью вызова».
      • Внутреннее исключение: DirectoryNotFoundException: "Не удалось найти часть пути" C: \ svn \ WpfApplication8 \ WpfApplication8 \ bin \ Debug \ Images \ elf.png '. "

Трассировка стека внутренних внутренних исключений показывает, что исходное исключение (DirectoryNotFoundException) генерируется строкой, которая создает экземпляр CroppedBitmap. Я не знаю, почему эта строка будет пытаться читать с диска или почему она не работает, если XAML, насколько я могу сказать, работает нормально.

Поскольку я знаю, что XAML использует конструкторы без параметров, я также попробовал следующую версию, которая должна быть намного ближе к тому, что на самом деле делает 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;
}

То же исключение, на этот раз из строки croppedImage.EndInit();.

Любые идеи о том, как я могу получить полностью кодовую версию, чтобы правильно загрузить ресурс и обрезать изображение? Что происходит в версии XAML, которая отличается?

1 Ответ

4 голосов
/ 30 июня 2009

Магия оказалась в свойстве BaseUri BitmapImage. BaseUri, очевидно, служит «текущим каталогом», к которому относится UriSource.

Когда мой BitmapImage загружался из XAML, BaseUri волшебным образом устанавливался в «pack: // application: ,,, / WpfApplication8; component / window1.xaml». Когда я изменил фрагмент кода # 4, чтобы явно задать для fullImage.BaseUri то же значение перед созданием CroppedBitmap, все работало.

Почему это работает из XAML (и из просто-BitmapImage-без-CroppedBitmap)

Так откуда взялась эта магическая величина BaseUri?

BaseUri является частью интерфейса IUriContext . IUriContext.BaseUri установлен в двух местах в сборках WPF, и между моими различными примерами мне удалось поразить оба из них. Неудивительно, что я запутался.

  • BamlRecordReader.ElementInitialize. Загрузчик BAML автоматически устанавливает BaseUri каждый раз, когда загружает элемент, реализующий IUriContext. Это объясняет, почему мои примеры № 1 и № 2 работали: они загружались из скомпилированного ресурса BAML.
  • Image.UpdateBaseUri (вызывается всякий раз, когда изменяется свойство Source). Он проверяет, реализует ли Source IUriContext, и, если да, устанавливает его BaseUri. Это объясняет, почему мой пример № 3 сработал: загрузка BitmapImage в графический интерфейс заставила его найти правильный путь поиска.

Он ищет изображение в ресурсах EXE только в том случае, если для BaseUri задан магический пакет: // URI. Без этого (как это происходит, когда все создается в коде, а не помещается в графический интерфейс), он выглядит только на диске.

Исправление

Как отмечалось выше, я мог бы жестко закодировать BaseUri. Но класс BaseUriHelper обеспечивает лучшее решение:

fullImage.BaseUri = BaseUriHelper.GetBaseUri(this);

Устанавливает для fullImage тот же BaseUri, что и у окна (this). Если это сделано до создания CroppedBitmap, все работает.

...