Настройка источника изображения WPF в коде

325

Я пытаюсь установить источник изображения WPF в коде. Изображение встраивается как ресурс в проект. Просматривая примеры, я пришел к следующему коду. По какой-то причине это не работает - изображение не отображается.

По отладке я вижу, что поток содержит данные изображения. Так что не так?

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream("SomeImage.png");
PngBitmapDecoder iconDecoder = new PngBitmapDecoder(iconStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
ImageSource iconSource = iconDecoder.Frames[0];
_icon.Source = iconSource;

Значок определяется примерно так: <Image x:Name="_icon" Width="16" Height="16" />

Torbjørn
источник
Если образ находится на локальном диске, <Image Source="some_fully_qualified_path">в XAML никогда не происходит сбой.
Лори Стерн
@LaurieStearn весь смысл в том, что мы не знаем путь и нам нужен код для его определения. Как новичок в программировании Windows GUI, я должен признать, что WinForms кажется более привлекательным, чем это дерьмо XAML.
Алекс Янсен

Ответы:

416

После того, как у меня возникла та же проблема, что и у вас, и я немного почитал, я нашел решение - Pack URI .

Я сделал следующее в коде:

Image finalImage = new Image();
finalImage.Width = 80;
...
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri("pack://application:,,,/AssemblyName;component/Resources/logo.png");
logo.EndInit();
...
finalImage.Source = logo;

Или короче, используя другой конструктор BitmapImage:

finalImage.Source = new BitmapImage(
    new Uri("pack://application:,,,/AssemblyName;component/Resources/logo.png"));

URI разбит на части:

  • Власть: application:///
  • Путь: имя файла ресурса, который скомпилирован в ссылочную сборку. Путь должен соответствовать следующему формату:AssemblyShortName[;Version][;PublicKey];component/Path

    • AssemblyShortName: короткое имя для указанной сборки.
    • ; Version [необязательный]: версия ссылочной сборки, которая содержит файл ресурсов. Это используется, когда загружены две или более ссылочных сборки с одинаковым коротким именем.
    • ; PublicKey [необязательно]: открытый ключ, который использовался для подписи ссылочной сборки. Это используется, когда загружены две или более ссылочных сборки с одинаковым коротким именем.
    • ; component: указывает, что ссылка на сборку указана из локальной сборки.
    • / Path: имя файла ресурса, включая его путь, относительно корня папки проекта указанной сборки.

Три слеша после application:должны быть заменены запятыми:

Примечание. Компонент полномочий URI пакета является встроенным URI, который указывает на пакет и должен соответствовать RFC 2396. Кроме того, символ «/» должен быть заменен символом «,» и зарезервированными символами, такими как «%». и "?" должен быть сбежал. Смотрите OPC для деталей.

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

Джаред Харли
источник
Да, это было решение, которое я нашел после проб и ошибок. Спасибо за подробное объяснение. Ответ принят!
Торбьерн
Я не знаю, почему это было необходимо, и почему другие ответы не сработали для меня ...
Торбьерн,
другие ответы на этот и другие вопросы тоже не сработали. Этот работает отлично. Спасибо.
Томас Сток
9
Хорошо, но разве нет какого-то метода или класса, который синтаксический анализатор XAML использует для преобразования строки простого пути в ImageSource? Не могли бы мы просто использовать это?
Qwertie
1
Я часто вижу этот фрагмент, скопированный в другие посты, где люди обычно игнорируют существование BitmapImage(Uri)конструктора, что помогает избежать необходимости вызывать BeginInit и EndInit. Я добавил это здесь.
Клеменс
175
var uriSource = new Uri(@"/WpfApplication1;component/Images/Untitled.png", UriKind.Relative);
foo.Source = new BitmapImage(uriSource);

Это загрузит изображение с именем «Untitled.png» в папку с именем «Images», для которой «Build Action» установлено в «Resource», в сборке под названием «WpfApplication1».

Саймон
источник
16
Спасибо за это. Одна проблема, которая выдвинула меня как новичка в wpf, изображение должно быть помечено как ресурс, чтобы это работало.
si618
4
Это решение дало мне исключение, поэтому я попробовал решение Джареда Харли, и оно сработало ...
Sangram Nandkhile
2
самая важная вещь - "UriKind.RelativeOrAbsolute" - другие примеры всегда вызывали исключения
Свен
9
var uriSource = new Uri("Images/Untitled.png", UriKind.Relative);было бы достаточно
Hamzeh Soboh
... и намного проще. Спасибо
Hamzeh
74

Это немного меньше кода и может быть сделано в одной строке.

string packUri = "pack://application:,,,/AssemblyName;component/Images/icon.png";
_image.Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;
Алекс Б
источник
8
Это, безусловно, лучший ответ, используя собственную реализацию
.NET
Что если нам нужно установить высоту и ширину изображения, то есть icon.png? Потому что с помощью _image.height и _image.width это устанавливает окно изображения не фактическое изображение, то есть icon.png.
Secretgenes
41

Очень просто:

Чтобы установить изображение элемента меню динамически, сделайте только следующее:

MyMenuItem.ImageSource = 
    new BitmapImage(new Uri("Resource/icon.ico",UriKind.Relative));

... тогда как "icon.ico" может быть расположен везде (в настоящее время он находится в каталоге 'Resources') и должен быть связан как Resource ...

Боте
источник
2
Короче = лучше. Я должен был установить его как @ "/ folder / image.png", но тогда он работал нормально. Спасибо!
gjvdkamp
3
Когда я назначаю источник изображений, он просто отображается пустым :(
HaloMediaz
@HaloMediaz путь может быть неправильным .... например, у вас есть ресурсы, но вы можете написать его Ресурс
Rouzbeh Zarandi
Это сработало для меня, и гораздо проще, чем абсолютные URI в других ответах.
Psiloc
16

Вы также можете уменьшить это до одной строки. Это код, который я использовал, чтобы установить значок для моего главного окна. Предполагается, что файл .ico помечен как содержимое и копируется в выходной каталог.

 this.Icon = new BitmapImage(new Uri("Icon.ico", UriKind.Relative));
Пейсон Уэлч
источник
16

Самый простой способ:

var uriSource = new Uri("image path here");
image1.Source = new BitmapImage(uriSource);
Hasan
источник
15

Ты пробовала:

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream("SomeImage.png");
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = iconStream;
bitmap.EndInit();
_icon.Source = bitmap;
Эндрю Мире
источник
13

Это мой путь:

internal static class ResourceAccessor
{
    public static Uri Get(string resourcePath)
    {
        var uri = string.Format(
            "pack://application:,,,/{0};component/{1}"
            , Assembly.GetExecutingAssembly().GetName().Name
            , resourcePath
        );

        return new Uri(uri);
    }
}

Использование:

new BitmapImage(ResourceAccessor.Get("Images/1.png"))
IlPADlI
источник
8

Вот пример, который устанавливает путь к изображению динамически (изображение, находящееся где-то на диске, а не в качестве ресурса):

if (File.Exists(imagePath))
{
    // Create image element to set as icon on the menu element
    Image icon = new Image();
    BitmapImage bmImage = new BitmapImage();
    bmImage.BeginInit();
    bmImage.UriSource = new Uri(imagePath, UriKind.Absolute);
    bmImage.EndInit();
    icon.Source = bmImage;
    icon.MaxWidth = 25;
    item.Icon = icon;
}

Размышления о иконах ...

Сначала подумайте, что вы думаете, что свойство Icon может содержать только изображение. Но на самом деле он может содержать что угодно! Я обнаружил это случайно, когда программно попытался установить Imageсвойство непосредственно в строку с путем к изображению. В результате получилось не изображение, а фактический текст пути!

Это приводит к альтернативе: не нужно создавать изображение для значка, а вместо этого использовать текст с символьным шрифтом для отображения простого «значка». В следующем примере используется шрифт Wingdings, который содержит символ «floppydisk». Этот символ действительно является символом <, который имеет особое значение в XAML, поэтому мы должны использовать &lt;вместо него закодированную версию . Это работает как мечта! Ниже показан символ дискеты в виде значка в пункте меню:

<MenuItem Name="mnuFileSave" Header="Save" Command="ApplicationCommands.Save">
  <MenuItem.Icon>
    <Label VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Wingdings">&lt;</Label>
  </MenuItem.Icon>
</MenuItem>
трепет
источник
Вопрос: « Изображение внедрено как ресурс в проект », ответ: « Вот пример [для изображения], расположенный где-то на диске, а не как ресурс как сборка ».
мин
7

Если ваше изображение хранится в ResourceDictionary, вы можете сделать это только с одной строкой кода:

MyImage.Source = MyImage.FindResource("MyImageKeyDictionary") as ImageSource;
Hollyroody
источник
6

Там также более простой способ. Если изображение загружено как ресурс в XAML, а рассматриваемый код является выделенным кодом для этого содержимого XAML:

Uri iconUri = new Uri("pack://application:,,,/ImageNAme.ico", UriKind.RelativeOrAbsolute);
NotifyIcon.Icon = BitmapFrame.Create(iconUri);
Bharat Thanki
источник
4

Поместите рамку в VisualBrush:

VisualBrush brush = new VisualBrush { TileMode = TileMode.None };

brush.Visual = frame;

brush.AlignmentX = AlignmentX.Center;
brush.AlignmentY = AlignmentY.Center;
brush.Stretch = Stretch.Uniform;

Поместите VisualBrush в GeometryDrawing

GeometryDrawing drawing = new GeometryDrawing();

drawing.Brush = brush;

// Brush this in 1, 1 ratio
RectangleGeometry rect = new RectangleGeometry { Rect = new Rect(0, 0, 1, 1) };
drawing.Geometry = rect;

Теперь поместите GeometryDrawing в DrawingImage:

new DrawingImage(drawing);

Поместите это в ваш источник изображения, и вуаля!

Вы могли бы сделать это намного проще, хотя:

<Image>
    <Image.Source>
        <BitmapImage UriSource="/yourassembly;component/YourImage.PNG"></BitmapImage>
    </Image.Source>
</Image>

И в коде:

BitmapImage image = new BitmapImage { UriSource="/yourassembly;component/YourImage.PNG" };
Арктур
источник
РЖУНИМАГУ! Зачем делать это проще, когда ты первый можешь сделать это трудным :) Я попробую ваше простое решение, прежде чем согласиться ...
Torbjørn
На самом деле это мне совсем не помогло. Может быть, я идиот :( У меня нет времени сейчас присматриваться (проект питомца). Хотелось бы получить больше ответов, когда я вернусь к этому :)
Торбьерн
3

Там также более простой способ. Если изображение загружено как ресурс в XAML, а рассматриваемый код является кодовым указателем для этого XAML:

Вот словарь ресурсов для файла XAML - единственная строка, которая вас волнует, - это ImageBrush с ключом «PosterBrush» - остальная часть кода предназначена только для отображения контекста

<UserControl.Resources>
        <ResourceDictionary>
            <ImageBrush x:Key="PosterBrush" ImageSource="..\Resources\Images\EmptyPoster.jpg" Stretch="UniformToFill"/>

        </ResourceDictionary>
    </UserControl.Resources>

Теперь, в коде позади, вы можете просто сделать это

ImageBrush posterBrush = (ImageBrush)Resources["PosterBrush"];
Марк Маллин
источник
2

Как загрузить изображение из встроенных в иконки ресурсов и изображений (исправленная версия Arcturus):

Предположим, вы хотите добавить кнопку с изображением. Что вы должны сделать?

  1. Добавьте в папку иконки проекта и поместите изображение ClickMe.png здесь
  2. В свойствах ClickMe.png установите для параметра «BuildAction» значение «Ресурс».
  3. Предположим, что имя вашей скомпилированной сборки - «Company.ProductAssembly.dll».
  4. Теперь пришло время загрузить наше изображение в XAML

    <Button Width="200" Height="70">
      <Button.Content>
        <StackPanel>
          <Image Width="20" Height="20">
            <Image.Source>
              <BitmapImage UriSource="/Company.ProductAssembly;component/Icons/ClickMe.png"></BitmapImage>
              </Image.Source>
          </Image>
          <TextBlock HorizontalAlignment="Center">Click me!</TextBlock>
        </StackPanel>
      </Button.Content>
    </Button>

Готово.

Сергей Кучук
источник
2

Я новичок в WPF, но не в .NET.

Я потратил пять часов, пытаясь добавить файл PNG в «Проект библиотеки пользовательских элементов управления WPF» в .NET 3.5 (Visual Studio 2010) и установить его в качестве фона унаследованного элемента управления.

Ничего относительного с URI не сработало. Я не могу представить, почему нет способа получить URI из файла ресурсов через IntelliSense, может быть, как:

Properties.Resources.ResourceManager.GetURI("my_image");

Я перепробовал множество URI и поиграл с ResourceManager и методами GetManifest, но все они были исключениями или значениями NULL.

Здесь я добавлю код, который работал для меня:

// Convert the image in resources to a Stream
Stream ms = new MemoryStream()
Properties.Resources.MyImage.Save(ms, ImageFormat.Png);

// Create a BitmapImage with the stream.
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = ms;
bitmap.EndInit();

// Set as source
Source = bitmap;
JoanComasFdz
источник
3
Я думаю, проблема в том, что WPF не «нравится» традиционный подход к размещению ресурсов в файлах resx. Вместо этого вы должны добавить изображение непосредственно в ваш проект и установить для его свойства Build Action значение Resource. Я не знаю, в чем разница (в терминах физического формата файла) между двумя подходами.
Qwertie
1
Убедитесь в том, что действие по сборке выполнено
Джерри Никсон,
1

Если у вас уже есть поток и вы знаете формат, вы можете использовать что-то вроде этого:

static ImageSource PngStreamToImageSource (Stream pngStream) {
    var decoder = new PngBitmapDecoder(pngStream,
        BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    return decoder.Frames[0];
}

источник
1

Вы просто немного пропустили.

Чтобы получить встроенный ресурс из любой сборки, вы должны указать имя сборки вместе с именем файла, как я упоминал здесь:

Assembly asm = Assembly.GetExecutingAssembly();
Stream iconStream = asm.GetManifestResourceStream(asm.GetName().Name + "." + "Desert.jpg");
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = iconStream;
bitmap.EndInit();
image1.Source = bitmap;
Маулик Кансара
источник
Кажется, что BeginInit () не существует в WPF.
Myosotis
Это доступно, проверьте ниже ссылку msdn.microsoft.com/en-us/library/…
maulik kansara