การตั้งค่าแหล่งรูปภาพ 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" />


หากภาพอยู่ในไดรฟ์<Image Source="some_fully_qualified_path">ในระบบใน XAML จะไม่ล้มเหลว
Laurie Stearn

@ LaurieStearn จุดรวมคือเราไม่ทราบเส้นทางและต้องการรหัสเพื่อตรวจสอบ ในฐานะที่เป็นคนใหม่สำหรับการเขียนโปรแกรม Windows GUI ฉันต้องยอมรับว่า WinForms ดูน่าสนใจกว่าอึ XAML นี้
Alex Jansen

คำตอบ:


416

หลังจากที่มีปัญหาเช่นเดียวกับที่คุณทำและอ่านบางผมค้นพบทางออก - แพ็คยูริ

ฉันทำในรหัสต่อไปนี้:

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:ต้องเปลี่ยนด้วยเครื่องหมายจุลภาค:

หมายเหตุ: องค์ประกอบสิทธิ์ของ pack URI เป็น URI แบบฝังที่ชี้ไปที่แพ็คเกจและต้องสอดคล้องกับ RFC 2396 นอกจากนี้อักขระ "/" จะต้องถูกแทนที่ด้วยอักขระ "," และอักขระที่สงวนเช่น "%" และ "?" จะต้องหลบหนี ดู OPC สำหรับรายละเอียด

Resourceและแน่นอนให้แน่ใจว่าคุณตั้งค่าการสร้างการกระทำบนภาพของคุณ


ใช่นี่เป็นวิธีการแก้ปัญหาที่ฉันพบว่าตัวเองหลังจากลองผิดลองถูก ขอบคุณสำหรับคำอธิบายอย่างละเอียด ตอบรับแล้ว!
Torbjørn

ผมไม่ทราบว่าทำไมถึงเป็นสิ่งที่จำเป็นแม้ว่าและทำไมคำตอบอื่น ๆ ไม่ได้ทำงานสำหรับฉัน ...
Torbjørn

คำตอบอื่น ๆ ในคำถามนี้และคำถามอื่นไม่ได้ผลสำหรับฉันเช่นกัน อันนี้ทำงานได้อย่างสมบูรณ์ ขอบคุณ
โทมัสต็อก

9
ตกลง แต่ไม่มีวิธีการหรือคลาสที่ XAML parser ใช้ในการแปลงสตริงพา ธ แบบง่ายเป็น ImageSource หรือไม่ เราใช้มันไม่ได้เหรอ?
Qwertie

1
ฉันมักจะเห็นตัวอย่างนี้คัดลอกไปยังโพสต์อื่น ๆ ที่ผู้คนมักจะไม่สนใจการมีอยู่ของตัวBitmapImage(Uri)สร้างซึ่งช่วยหลีกเลี่ยงความต้องการโทร BeginInit และ EndInit ฉันเพิ่มที่นี่
Clemens

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
ขอบคุณสำหรับสิ่งนั้น ปัญหาหนึ่งที่ทำให้ฉันสะดุดในฐานะ noob ถึง wpf ภาพต้องถูกทำเครื่องหมายเป็นทรัพยากรเพื่อให้ทำงานได้
si618

4
วิธีนี้ให้ข้อยกเว้นแก่ฉันดังนั้นฉันจึงลองวิธีแก้ปัญหาของ Jared Harley และใช้งานได้ ...
Sangram Nandkhile

2
สิ่งที่สำคัญที่สุดคือ "UriKind.RelativeOrAbsolute" - ตัวอย่างอื่น ๆ มักจะโยนข้อยกเว้น
สเวน

9
var uriSource = new Uri("Images/Untitled.png", UriKind.Relative);จะเพียงพอ
Hamzeh Soboh

... และง่ายขึ้นมาก ขอบคุณ Hamzeh
pStan

74

นี่เป็นรหัสน้อยกว่าเล็กน้อยและสามารถทำได้ในบรรทัดเดียว

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

8
นี่เป็นคำตอบที่ดีที่สุดโดยใช้การติดตั้ง. NET ของตัวเอง
joshcomley

ถ้าเราต้องการตั้งค่าความสูงและความกว้างของรูปภาพเช่น icon.png เนื่องจากการใช้ _image.height และ _image.width ทำให้หน้าต่างรูปภาพไม่ใช่ภาพจริงเช่น icon.png
secretgenes

41

ง่ายมาก:

ในการตั้งค่ารูปภาพของรายการเมนูแบบไดนามิกให้ทำดังต่อไปนี้:

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

... ในขณะที่ "icon.ico" สามารถอยู่ได้ทุกที่ (ปัจจุบันอยู่ในไดเรกทอรี 'Resources') และต้องเชื่อมโยงเป็นทรัพยากร ...


2
สั้นกว่า ฉันต้องตั้งเป็น @ "/ folder / image.png" แต่ถึงอย่างนั้นมันก็ใช้ได้ดี ขอบคุณ!
gjvdkamp

3
เมื่อฉันกำหนดอิมเมจจากแหล่งมันก็แค่แสดงว่างเปล่า :(
HaloMediaz

เส้นทาง @HaloMediaz อาจผิด .... เช่นคุณมีทรัพยากร แต่คุณสามารถเขียนได้ทรัพยากร
Rouzbeh Zarandi

สิ่งนี้ใช้ได้สำหรับฉันและตรงไปตรงมามากกว่า URIs ที่แน่นอนในคำตอบอื่น ๆ
Psiloc

16

คุณสามารถลดสิ่งนี้เป็นหนึ่งบรรทัด นี่คือรหัสที่ฉันใช้ในการตั้งค่าไอคอนสำหรับหน้าต่างหลักของฉัน จะถือว่าไฟล์. ico ถูกทำเครื่องหมายเป็นเนื้อหาและจะถูกคัดลอกไปยังไดเรกทอรีที่ส่งออก

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


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"))

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

ภาพสะท้อนบนไอคอน ...

ความคิดแรกคุณจะคิดว่าคุณสมบัติไอคอนสามารถมีภาพได้ แต่มันสามารถมีอะไรจริง ๆ ! ฉันค้นพบสิ่งนี้โดยบังเอิญเมื่อฉันพยายามตั้งค่าImageคุณสมบัติเป็นสตริงโดยตรงโดยทางโปรแกรม ผลก็คือมันไม่ได้แสดงภาพ แต่ข้อความที่แท้จริงของเส้นทาง!

สิ่งนี้นำไปสู่ทางเลือกอื่นที่ไม่จำเป็นต้องสร้างภาพสำหรับไอคอน แต่ใช้ข้อความที่มีแบบอักษรสัญลักษณ์แทนเพื่อแสดงไอคอน "แบบง่าย" ตัวอย่างต่อไปนี้ใช้แบบอักษร Wingdingsซึ่งมีสัญลักษณ์ "floppydisk" สัญลักษณ์นี้เป็นตัวละคร<ที่มีความหมายพิเศษใน XAML ดังนั้นเราต้องใช้เวอร์ชั่นที่เข้ารหัส&lt;แทน งานนี้เหมือนฝัน! ต่อไปนี้แสดงสัญลักษณ์ floppydisk เป็นไอคอนบนรายการเมนู:

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

6

นอกจากนี้ยังมีวิธีที่ง่ายกว่า หากอิมเมจถูกโหลดเป็นทรัพยากรใน XAML และรหัสที่เป็นปัญหาคือโค้ดสำหรับเนื้อหา XAML นั้น:

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

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

วางสิ่งนี้ลงบนแหล่งที่มาของภาพและvoilà

คุณสามารถทำได้ง่ายกว่ามากแม้ว่า:

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

และในรหัส:

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

ฮ่า ๆ! ทำไมทำให้มันง่ายเมื่อคุณสามารถทำให้มันยาก :) ผมจะพยายามแก้ปัญหาที่ง่ายของคุณก่อนที่จะยอมรับว่า ...
Torbjørn

จริงๆแล้วนี่ไม่ได้ช่วยอะไรฉันเลย บางทีฉันอาจโง่ :( ไม่มีเวลามองอย่างใกล้ชิดมากขึ้นในตอนนี้ (โครงการสัตว์เลี้ยง) ต้องการคำตอบเพิ่มเติมเมื่อฉันกลับไปที่ :) :)
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 Custom Library Library Project" ใน. NET 3.5 (Visual Studio 2010) และตั้งเป็นพื้นหลังของตัวควบคุมที่สืบทอดภาพ

ไม่มีอะไรเกี่ยวข้องกับ URIs ที่ทำงาน ฉันไม่สามารถจินตนาการได้ว่าทำไมไม่มีวิธีการรับ URI จากไฟล์ทรัพยากรผ่าน IntelliSense อาจเป็น:

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

ฉันได้ลอง URIs มากมายและเล่นกับ ResourceManager และวิธี GetManifest ของ Assembly แต่ทั้งหมดมีข้อยกเว้นหรือค่า 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;

3
ฉันคิดว่าปัญหาคือ WPF ไม่ "ชอบ" วิธีดั้งเดิมของการวางทรัพยากรลงในไฟล์ resx แต่คุณควรเพิ่มภาพลงในโครงการของคุณโดยตรงและตั้งค่าคุณสมบัติ Build Action เป็นทรัพยากร ฉันไม่รู้ว่าความแตกต่างคืออะไร (ในรูปแบบไฟล์ฟิสิคัลไฟล์) ระหว่างสองวิธี
Qwertie

1
ตรวจสอบให้แน่ใจว่าแอคชั่น Build เป็นเนื้อหา
Jerry Nixon

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.