Ustawienie źródła obrazu WPF w kodzie


325

Próbuję ustawić źródło obrazu WPF w kodzie. Obraz jest osadzony jako zasób w projekcie. Przeglądając przykłady, wymyśliłem poniższy kod. Z jakiegoś powodu to nie działa - obraz się nie wyświetla.

Podczas debugowania widzę, że strumień zawiera dane obrazu. Więc co jest nie tak?

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;

Ikona jest zdefiniowana mniej więcej tak: <Image x:Name="_icon" Width="16" Height="16" />


Jeśli obraz znajduje się na dysku lokalnym, <Image Source="some_fully_qualified_path">w XAML nigdy nie zawodzi.
Laurie Stearn,

@LaurieDowiedz się, że nie znamy ścieżki i potrzebujemy kodu, aby ją ustalić. Jako ktoś nowy w programowaniu GUI Windows, muszę przyznać, że WinForms wydaje się bardziej atrakcyjny niż ta bzdura XAML.
Alex Jansen

Odpowiedzi:


416

Po tym samym problemie co ty i po przeczytaniu odkryłem rozwiązanie - spakuj identyfikatory URI .

Zrobiłem następujące w kodzie:

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;

Lub krócej, używając innego konstruktora BitmapImage:

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

Identyfikator URI jest podzielony na części:

  • Autorytet: application:///
  • Ścieżka: nazwa pliku zasobów, który jest kompilowany w przywoływany zespół. Ścieżka musi być zgodna z następującym formatem:AssemblyShortName[;Version][;PublicKey];component/Path

    • AssemblyShortName: krótka nazwa przywoływanego zestawu.
    • ; Wersja [opcjonalnie]: wersja przywoływanego zestawu, która zawiera plik zasobów. Jest to używane, gdy załadowane są dwa lub więcej zestawów odniesienia o tej samej krótkiej nazwie.
    • ; PublicKey [opcjonalnie]: klucz publiczny, który został użyty do podpisania wskazanego zestawu. Jest to używane, gdy załadowane są dwa lub więcej zestawów odniesienia o tej samej krótkiej nazwie.
    • ; komponent: określa, że ​​do zestawu, do którego następuje odwołanie, odwołuje się zespół lokalny.
    • / Ścieżka: nazwa pliku zasobu, w tym jego ścieżka, względem katalogu głównego folderu projektu przywoływanego zestawu.

Trzy ukośniki application:muszą zostać zastąpione przecinkami:

Uwaga: Komponentem uprawnień identyfikatora URI paczki jest osadzony identyfikator URI, który wskazuje na pakiet i musi być zgodny z RFC 2396. Ponadto znak „/” należy zastąpić znakiem „,” i zastrzeżonymi znakami, takimi jak „%” i "?" trzeba uciec. Szczegółowe informacje można znaleźć w OPC.

I oczywiście upewnij się, że ustawiłeś akcję kompilacji obrazu Resource.


Tak, to było rozwiązanie, które znalazłem po kilku próbach i błędach. Dzięki za dokładne wyjaśnienie. Odpowiedź zaakceptowana!
Torbjørn

Nie wiem jednak, dlaczego było to potrzebne i dlaczego inne odpowiedzi nie działały dla mnie ...
Torbjørn

inne odpowiedzi w tym i inne pytania też mi nie pasowały. Ten działa idealnie. Dzięki.
Thomas Stock

9
Okej, ale czy nie ma metody lub klasy, której parser XAML używa do konwersji prostego ciągu ścieżki do źródła obrazu? Czy nie moglibyśmy tego po prostu użyć?
Qwertie

1
Często widzę ten fragment skopiowany do innych postów, w których ludzie zwykle ignorują istnienie BitmapImage(Uri)konstruktora, co pomaga uniknąć potrzeby wywoływania BeginInit i EndInit. Dodałem to tutaj.
Clemens,

175
var uriSource = new Uri(@"/WpfApplication1;component/Images/Untitled.png", UriKind.Relative);
foo.Source = new BitmapImage(uriSource);

Spowoduje to załadowanie obrazu o nazwie „Untitled.png” do folderu o nazwie „Obrazy” z jego „Kompilacją działania” ustawioną na „Zasób” w zespole o nazwie „WpfApplication1”.


16
Dziękuję za to. Jeden problem, który potknął mnie jako noob do wpf, obraz musi być oznaczony jako zasób, aby to zadziałało.
si618

4
To rozwiązanie dało mi wyjątek, więc wypróbowałem rozwiązanie Jareda Harleya i zadziałało ...
Sangram Nandkhile

2
najważniejsze jest „UriKind.RelativeOrAbsolute” - inne przykłady zawsze rzucały wyjątki
Sven

9
var uriSource = new Uri("Images/Untitled.png", UriKind.Relative);byłoby wystarczające
Hamzeh Soboh

... i dużo łatwiejsze. Dzięki
Hamzeh

74

Jest to nieco mniej kodu i można to zrobić w jednym wierszu.

string packUri = "pack://application:,,,/AssemblyName;component/Images/icon.png";
_image.Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;

8
To zdecydowanie najlepsza odpowiedź, wykorzystująca własną implementację
platformy .NET

co jeśli musimy ustawić wysokość i szerokość obrazu tj. icon.png? Ponieważ za pomocą _image.height i _image.width ustawia okno obrazu, a nie rzeczywisty obraz, np. Icon.png.
secretgenes

41

Bardzo łatwe:

Aby dynamicznie ustawić obraz elementu menu, wykonaj tylko następujące czynności:

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

... podczas gdy „icon.ico” może znajdować się wszędzie (obecnie znajduje się w katalogu „Zasoby”) i musi być powiązany jako Zasób…


2
Krótszy = lepszy. Musiałem jednak ustawić go jako @ "/ folder / image.png", ale potem działało dobrze. Dzięki!
gjvdkamp

3
Kiedy przypisuję źródło obrazów, wyświetla się tylko puste :(
HaloMediaz

@HaloMediaz ścieżka może być niepoprawna .... np. Masz Zasoby, ale możesz je napisać Zasób
Rouzbeh Zarandi

To zadziałało dla mnie i było znacznie prostsze niż bezwzględne identyfikatory URI w innych odpowiedziach.
Psiloc,

16

Możesz również zmniejszyć to do jednej linii. To jest kod, którego użyłem do ustawienia ikony w moim głównym oknie. Zakłada, że ​​plik .ico jest oznaczony jako Treść i jest kopiowany do katalogu wyjściowego.

 this.Icon = new BitmapImage(new Uri("Icon.ico", UriKind.Relative));

16

Najprostszy sposób:

var uriSource = new Uri("image path here");
image1.Source = new BitmapImage(uriSource);

15

Czy próbowałeś:

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

To jest mój sposób:

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

Stosowanie:

new BitmapImage(ResourceAccessor.Get("Images/1.png"))

8

Oto przykład, który dynamicznie ustawia ścieżkę obrazu (obraz znajduje się gdzieś na dysku zamiast budować jako zasób):

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

Refleksje na temat ikon ...

Po pierwsze, można by pomyśleć, że właściwość Icon może zawierać tylko obraz. Ale może zawierać wszystko! Odkryłem to przez przypadek, gdy programowo próbowałem ustawićImage właściwość bezpośrednio na ciąg znaków ze ścieżką do obrazu. Rezultat był taki, że nie pokazywał obrazu, ale rzeczywisty tekst ścieżki!

Prowadzi to do alternatywy, aby nie musieć tworzyć obrazu dla ikony, ale zamiast tego użyj tekstu z czcionką symbolu, aby wyświetlić prostą „ikonę”. W poniższym przykładzie użyto czcionki Wingdings, która zawiera symbol „dyskietki”. Ten symbol jest tak naprawdę postacią <, która ma specjalne znaczenie w XAML, więc &lt;zamiast tego musimy użyć zakodowanej wersji . To działa jak sen! Poniżej pokazano symbol dyskietki jako ikonę w pozycji menu:

<MenuItem Name="mnuFileSave" Header="Save" Command="ApplicationCommands.Save">
  <MenuItem.Icon>
    <Label VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Wingdings">&lt;</Label>
  </MenuItem.Icon>
</MenuItem>

Pytanie: „ Obraz jest osadzony jako zasób w projekcie ”, odpowiedz: „ Oto przykład [dla obrazu] umieszczony gdzieś na dysku, a nie budowany jako zasób ”.
min.

7

Jeśli twój obraz jest przechowywany w ResourceDictionary, możesz to zrobić za pomocą tylko jednego wiersza kodu:

MyImage.Source = MyImage.FindResource("MyImageKeyDictionary") as ImageSource;

6

Jest też prostszy sposób. Jeśli obraz jest ładowany jako zasób do XAML, a kod, o którym mowa, jest kodem dla tej zawartości XAML:

Uri iconUri = new Uri("pack://application:,,,/ImageNAme.ico", UriKind.RelativeOrAbsolute);
NotifyIcon.Icon = BitmapFrame.Create(iconUri);

4

Umieść ramkę w VisualBrush:

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

brush.Visual = frame;

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

Umieść VisualBrush w 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;

Teraz umieść GeometryDrawing w obrazie rysunku:

new DrawingImage(drawing);

Umieść to w swoim źródle obrazu i voilà!

Możesz to zrobić o wiele łatwiej:

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

I w kodzie:

BitmapImage image = new BitmapImage { UriSource="/yourassembly;component/YourImage.PNG" };

LOL! Po co ułatwiać, skoro po raz pierwszy może to utrudnić :) Wypróbuję proste rozwiązanie, zanim zaakceptuję ...
Torbjørn

Właściwie to wcale mi nie pomogło. Może jestem głupi :( Nie mam teraz czasu, aby przyjrzeć się bliżej (projekt zwierzaka). Chciałbym uzyskać więcej odpowiedzi, kiedy wrócę do niego :)
Torbjørn

3

Jest też prostszy sposób. Jeśli obraz jest ładowany jako zasób do XAML, a kod, o którym mowa, to kluczowy kod dla tej XAML:

Oto słownik zasobów dla pliku XAML - jedyną linią, na której Ci zależy, jest ImageBrush z kluczem „PosterBrush” - reszta kodu służy tylko do pokazania kontekstu

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

        </ResourceDictionary>
    </UserControl.Resources>

Teraz w kodzie z tyłu możesz to zrobić

ImageBrush posterBrush = (ImageBrush)Resources["PosterBrush"];

2

Jak załadować obraz z osadzonych w ikonach zasobów i obrazach (poprawiona wersja Arcturus):

Załóżmy, że chcesz dodać przycisk z obrazem. Co powinieneś zrobić?

  1. Dodaj do ikon folderów projektu i umieść obraz ClickMe.png tutaj
  2. We właściwościach „ClickMe.png” ustaw „BuildAction” na „Resource”
  3. Załóżmy, że nazwa skompilowanego zestawu to „Company.ProductAssembly.dll”.
  4. Czas załadować nasz obraz do 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>

Gotowy.


2

Jestem nowy w WPF, ale nie jestem w .NET.

Pięć godzin spędziłem na dodawaniu pliku PNG do „Projektu biblioteki niestandardowej kontroli WPF” w .NET 3.5 (Visual Studio 2010) i ustawianiu go jako tła kontrolki dziedziczonej przez obraz.

Nic względnego z identyfikatorami URI nie działało. Nie mogę sobie wyobrazić, dlaczego nie ma metody uzyskania identyfikatora URI z pliku zasobów za pośrednictwem IntelliSense, może jako:

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

Wypróbowałem wiele identyfikatorów URI i grałem z ResourceManager i metodami GetManifest zespołu, ale wszystkie były wyjątkami lub wartościami NULL.

Tutaj piszę kod, który działał dla mnie:

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

3
Myślę, że problem polega na tym, że WPF nie „lubi” tradycyjnego podejścia do umieszczania zasobów w plikach resx. Zamiast tego należy dodać obraz bezpośrednio do projektu i ustawić jego właściwość Kompilacja działania na Zasób. Nie wiem, jaka jest różnica (w formacie fizycznego pliku) między tymi dwoma podejściami.
Qwertie

1
Upewnij się, że akcja kompilacji jest satysfakcjonująca
Jerry Nixon,

1

Jeśli masz już strumień i znasz format, możesz użyć czegoś takiego:

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

1

Trochę za tobą tęskniłeś.

Aby uzyskać osadzony zasób z dowolnego zestawu, musisz wspomnieć nazwę zestawu z nazwą pliku, jak już wspomniałem tutaj:

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;

Wydaje się, że BeginInit () nie istnieje w WPF.
Myosotis,

Jest dostępny, sprawdź poniżej link msdn.microsoft.com/en-us/library/…
maulik kansara
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.