пятница, 1 июня 2012 г.

WPF - Установка Image.Source через код

Предположим вот такую разметку:
<Window x:Class="WPFApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Name="mainWindow"
        Title="WPFApp" Height="550" Width="1045">
    <Grid Name="rootWindow">
        <Image Height="55" Width="55" HorizontalAlignment="Left" Name="img" Stretch="Fill" VerticalAlignment="Top"  />
    </Grid>
</Window>
   
Как мы в этом случае установили бы атрибут Source для указания на нужную для отображения картинку? Нет ничего проще, чем сделать это непосредственно в самой разметке, где мы задаем атрибут Source и указываем путь к изображению.
<Image Source="Images/PIcon.jpg" Height="55" Width="55" HorizontalAlignment="Left" Name="img" Stretch="Fill" VerticalAlignment="Top"  />
В данном случае картинка располагается в папке Images, которая в свою очередь находится в корне всего проекта. Все это конечно прекрасно но что если мы хотим установить путь к картинке из кода? Первым, пришедшим мне в голову решением, был следующий фрагмент кода
img.Source = "Images/PIcon";
Упс! Такая запись в корне неверна. И какая же удовлетворит WPF?

Начнем по-порядку:

- во-первых, нужно убедится, что свойство картинки "Действие при построении" ("Build Action") установлено на "Resource". Для этого нужно вызвать "Окно свойств" в VS (Вид -> Окно свойств).

- во-вторых, типом Source'а является объект абстрактного класса ImageSource, а не обычная строка. Возможный вопрос: "Так, а почему же в разметке мы указали путь строкой и все было нормально?". В WPF есть такое понятие как конвертеры типов (Type converters). Они позволяют в разметке XAML, задавать значения атрибутов элементов, в виде строк. При запуске, приложение сделает всю остальную работу по преобразованию этих строк в конкретные объекты (значения).

- в-третьих, на вопрос типа "Так какой же объект мы должны подставить?" ответом будет объект типа BitmapImage (он как раз реализует абстрактный класс ImageSource).

- в-четвертых, один из конструкторов BitmapImage (их всего два) в качестве параметра принимает объект типа Uri и перечисление UriKind. Но Uri не обычный, а так называемый Pack URIs. В общем случае он составляется так:

/AssemblyShortName;component/Path где,

/AssemblyShortName - имя нашей сборки
;component - определяет то, что сборка к которой идет обращение, ссылается из локальной сборки
/Path - имя файла ресурса

Итак, вся нужная информация у нас на руках, время завершить начатое. Финальное решение будет иметь следующий вид:
var uriImageSource = new Uri(@"/WPFApp;component/Images/PIcon.png", UriKind.RelativeOrAbsolute);
img.Source = new BitmapImage(uriImageSource);
Вот теперь все, изображение соизволит явиться.

Здесь есть еще одна загвоздка. Предположим, что мы изменили расположение картинки в проекте например на "Sprites/PIcon.png". Соответственно мы так же меняем путь в конструкторе Uri:
var uriImageSource = new Uri(@"/WPFApp;component/Sprites/PIcon.png", UriKind.RelativeOrAbsolute);
Если сразу после этого скомпилировать проект ("Запуск без отладки" или Ctrl+F5), то картинка может и вовсе не появится. Почему? Не знаю баг это или нет, но решение оказалось тривиальным. Нужно просто "Перестроить решение" (правой кнопкой по проекту -> Перестроить решение) и скомпилировать вновь.

Комментариев нет:

Отправить комментарий