Zasoby obrazu WPF

408

Pochodzę głównie z sieci i trochę z Windows Forms. W nowym projekcie będziemy używać WPF. Aplikacja WPF będzie potrzebowała 10-20 małych ikon i obrazów w celach ilustracyjnych. Myślę o przechowywaniu ich w zestawie jako zasobów osadzonych. Czy to dobra droga?

Jak określić w XAML, że formant obrazu powinien ładować obraz z osadzonego zasobu?

driis
źródło

Odpowiedzi:

473

Jeśli użyjesz obrazu w wielu miejscach, warto załadować dane obrazu tylko raz do pamięci, a następnie udostępnić je wszystkim Imageelementom.

Aby to zrobić, utwórz BitmapSource gdzieś zasób:

<BitmapImage x:Key="MyImageSource" UriSource="../Media/Image.png" />

Następnie w kodzie użyj czegoś takiego:

<Image Source="{StaticResource MyImageSource}" />

W moim przypadku stwierdziłem, że musiałem ustawić Image.pngplik tak, aby miał działanie kompilacji, Resourcea nie tylko Content. Powoduje to przenoszenie obrazu w skompilowanym zestawie.

Drew Noakes
źródło
6
Czy można to zrobić dynamicznie? Jeśli mam różną liczbę obrazów, które chciałbym załadować podczas uruchamiania, czy mogę utworzyć BitmapSource dla każdego obrazu i odwoływać się do nich w taki sam sposób jak powyżej?
Becky Franklin
2
@Becky - Tak, możesz, choć jeśli chcesz się do nich odwoływać w Xaml, może być konieczne użycie DynamicResourcerozszerzenia znaczników zamiast StaticResource, zakładając, że znasz klucze w czasie kompilacji. W WPF możesz tworzyć słowniki zasobów w czasie wykonywania. W rzeczywistości dzieje się tak, gdy ładujesz dokument Xaml, po prostu nie widzisz równoważnego C #.
Drew Noakes,
9
Coś, co trafiłem: jeśli dodasz zasób obrazu do słownika zasobów, nie zapomnij odwołać się do tego słownika obrazów w XAML dla swojego komponentu. Coś w stylu: <UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source = "Dictionary1.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources>
Dan Mitchell
4
Zwykle dodam Width="{Binding Source.PixelWidth, RelativeSource={RelativeSource Self}}" do tego Image, ponieważ w przeciwnym razie często widzę, że obraz z jakiegoś powodu jest groteskowo powiększany (na przykład ikony 16 x 16 rozciągnięte do czegoś, co wygląda jak 200 x 200 pikseli).
LUB Mapper
5
Odkryłem, że jeśli BitmapImage jest zadeklarowany w słowniku zasobów zestawu, do którego to działa, UriSource musi być packURI, aby to działało. W przeciwnym razie zobaczysz, że możesz zobaczyć obraz w swoim edytorze xaml w VS, ale nie ma obrazu podczas debugowania. Pakiet URIS: msdn.microsoft.com/en-au/library/aa970069(v=vs.100).aspx
programowanie nie powiodło się
174

Stwierdziłem, że najlepszą praktyką korzystania z obrazów, filmów itp. Jest:

  • Zmień swoje pliki „Kompiluj akcję” na „Treść” . Pamiętaj, aby zaznaczyć opcję Kopiuj do kompilacji katalogu .
    • Znajduje się w menu „Kliknij prawym przyciskiem myszy” w oknie Solution Explorer.
  • Źródło obrazu w następującym formacie:
    • "/ « YourAssemblyName » ; component /« YourPath »/ « YourImage.png »

Przykład

<Image Source="/WPFApplication;component/Images/Start.png" />

Korzyści:

  • Pliki nie są osadzane w zestawie.
    • Menedżer zasobów podniesie problemy z przepełnieniem pamięci przy zbyt wielu zasobach (w czasie kompilacji).
  • Można wywoływać między zespołami.
Nuno Rodrigues
źródło
36
To samo podejście działa, jeśli osadzisz zasób w zestawie, ale musisz ustawić opcję „Kompilacja działania” na „Zasób”.
Ashley Davis
5
Działa dzięki. Jedna uwaga dla innych: „komponent” jest wymagany „tak jak jest”, „Obrazy” to względna ścieżka png w projekcie. Tj. Obraz umieszczony w katalogu głównym to „<Image Source =" / WPFApplication; component / Start.png "/>"
Badiboy
3
Przykład jak to zrobić w C # byłby miły. (To nie jest prawidłowy identyfikator URI, więc nie można go użyć podczas konstruowania
obrazu bitmapowego
5
Jak to zrobić, jeśli plik jest ustawiony na Zasób osadzony? To nie działa. Nie chcę dwa razy dołączać obrazu do mojego projektu. (Już używam go jako zasobu osadzonego.)
BrainSlugs83
2
Gdzie i jak „komponent” wchodzi na ścieżkę. Czy to część jakiejś specyfikacji?
Triynko,
46

Niektóre osoby pytają o zrobienie tego w kodzie i nie uzyskanie odpowiedzi.

Po wielu godzinach poszukiwań znalazłem bardzo prostą metodę, nie znalazłem żadnego przykładu, dlatego dzielę się tutaj moim, który działa z obrazami. (mój był .gif)

Podsumowanie:

Zwraca BitmapFrame, które wydają się lubić „miejsca docelowe” ImageSource.

Posługiwać się:

doGetImageSourceFromResource ("[YourAssemblyNameHere]", "[YourResourceNameHere]");

Metoda:

static internal ImageSource doGetImageSourceFromResource(string psAssemblyName, string psResourceName)
{
    Uri oUri = new Uri("pack://application:,,,/" +psAssemblyName +";component/" +psResourceName, UriKind.RelativeOrAbsolute);
    return BitmapFrame.Create(oUri);
}

Wnioski:

Z moich doświadczeń wynika, że ​​łańcuch paczki nie jest problemem, sprawdź swoje strumienie, a zwłaszcza jeśli czytając go po raz pierwszy ustawiłeś wskaźnik na końcu pliku i musisz ponownie ustawić go na zero przed ponownym odczytaniem.

Mam nadzieję, że dzięki temu zaoszczędzisz tyle godzin, ile chciałbym, aby ten kawałek miał dla mnie!

Craig
źródło
44

W kodzie, aby załadować zasób do wykonującego zestawu, w którym mój obraz Freq.pngbył w folderze Iconsi zdefiniowany jako Resource:

this.Icon = new BitmapImage(new Uri(@"pack://application:,,,/" 
    + Assembly.GetExecutingAssembly().GetName().Name 
    + ";component/" 
    + "Icons/Freq.png", UriKind.Absolute)); 

Wykonałem również funkcję:

/// <summary>
/// Load a resource WPF-BitmapImage (png, bmp, ...) from embedded resource defined as 'Resource' not as 'Embedded resource'.
/// </summary>
/// <param name="pathInApplication">Path without starting slash</param>
/// <param name="assembly">Usually 'Assembly.GetExecutingAssembly()'. If not mentionned, I will use the calling assembly</param>
/// <returns></returns>
public static BitmapImage LoadBitmapFromResource(string pathInApplication, Assembly assembly = null)
{
    if (assembly == null)
    {
        assembly = Assembly.GetCallingAssembly();
    }

    if (pathInApplication[0] == '/')
    {
        pathInApplication = pathInApplication.Substring(1);
    }
    return new BitmapImage(new Uri(@"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute)); 
}

Użycie (założenie, że umieściłeś funkcję w klasie ResourceHelper):

this.Icon = ResourceHelper.LoadBitmapFromResource("Icons/Freq.png");

Uwaga : patrz identyfikatory URI MSDN Pack w WPF :
pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml

Eric Ouellet
źródło
nowy identyfikator Uri zgłasza Nieprawidłowy identyfikator URI: Podano nieprawidłowy port.
Steve,
Czy masz uri obrażające?
Eric Ouellet,
1
takie same uri jak twoje, z wyjątkiem tego, że moja działała w WPF hostowanym w winformie. A schemat „paczki” nie został jeszcze zarejestrowany, kiedy zadzwoniłem do nowego Uri.
Steve,
1
Ups ... Prawdopodobnie wycofano go, aby utworzyć WPF na serwerze WinForm. Przepraszam. Nie będę próbował tego naprawić, ponieważ uważam, że nie jest to bardzo częste użycie. Powodzenia!
Eric Ouellet,
W moim przypadku użycie new Uri(@"pack://application:,,,/" + pathInApplication)również załatwiło sprawę .
Adam Calvet Bohl
40

Tak, to właściwa droga.

Możesz użyć obrazu w pliku zasobów, używając tylko ścieżki:

<Image Source="..\Media\Image.png" />

Musisz ustawić akcję kompilacji pliku obrazu na „Zasób”.

ema
źródło
1
Dzięki za to. Czy istnieje sposób zrobienia czegoś podobnego z ImageSource, zasadniczo ładując obraz raz do słownika zasobów. Obawiam się, że takie podejście ładuje dane obrazu wiele razy w pamięci.
Drew Noakes,
2
To będzie bałagan, gdy będziesz musiał zmienić kod. Będziesz musiał ręcznie zmienić wszystkie odwołania do obrazów, jeśli twój dokument xaml zmieni przestrzeń nazw. Metoda opisana przez Drew Noakesa jest o wiele płynniejsza i łatwiejsza w utrzymaniu.
Kasper Holdum
14

Pełny opis sposobu korzystania z zasobów: zasoby aplikacji WPF, zawartość i pliki danych

I jak się do nich odwoływać, przeczytaj „Pakowanie identyfikatorów URI w WPF”.

Krótko mówiąc, istnieją nawet środki do odwoływania się do zasobów z referencyjnych / referencyjnych zestawów.

techfan
źródło
Łącze wydaje się być aktywne i ma się dobrze (choć napisano: „Ta dokumentacja jest zarchiwizowana i nie jest utrzymywana” ).
Peter Mortensen
5
  1. Visual Studio 2010 Professional SP1.
  2. Profil klienta .NET Framework 4.
  3. Obraz PNG dodany jako zasób we właściwościach projektu.
  4. Nowy plik w folderze zasobów automatycznie utworzony.
  5. Zbuduj zestaw działań do zasobu.

To działało dla mnie:

<BitmapImage x:Key="MyImageSource" UriSource="Resources/Image.png" />
JoanComasFdz
źródło
3

Jeśli używasz Blend , aby było to wyjątkowo łatwe i nie miał problemów z uzyskaniem poprawnej ścieżki dla atrybutu Source , po prostu przeciągnij i upuść obraz z panelu Projekt na projektanta.

użytkownik42467
źródło
3

Tak, to właściwa droga. Możesz użyć obrazów w pliku zasobów, używając ścieżki:

<StackPanel Orientation="Horizontal">
    <CheckBox  Content="{Binding Nname}" IsChecked="{Binding IsChecked}"/>
    <Image Source="E:\SWorking\SharePointSecurityApps\SharePointSecurityApps\SharePointSecurityApps.WPF\Images\sitepermission.png"/>
    <TextBlock Text="{Binding Path=Title}"></TextBlock>
</StackPanel>
Sanjay Ranavaya
źródło
-5

Poniższe działało, a obrazy do ustawienia są zasobami we właściwościach:

    var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(MyProject.Properties.Resources.myImage.GetHbitmap(),
                                      IntPtr.Zero,
                                      Int32Rect.Empty,
                                      BitmapSizeOptions.FromEmptyOptions());
    MyButton.Background = new ImageBrush(bitmapSource);
img_username.Source = bitmapSource;
Raghulan Gowthaman
źródło
5
Bez obrazy, ale pachnie to złą praktyką.
ShloEmi,