2009-06-28 41 views
2

我試圖編寫代碼,將從資源加載圖像,然後裁剪它。當我在XAML中完成全部或部分工作時,此代碼就可以工作。我想從全部XAML切換到全部代碼,所以我可以在不同的Uris中重複使用這個不止一個地方。在運行時創建一個CroppedBitmap - 將不會從資源加載

但是,當我嘗試在代碼中做同樣的事情時,我得到一個DirectoryNotFoundException,因爲它突然開始嘗試在磁盤上查找文件夾,而不是從資源加載圖像。

  • 如果我在XAML中加載BitmapImage,然後在XAML中創建一個CroppedBitmap,那麼一切正常。
  • 如果我在XAML中加載BitmapImage,然後編寫代碼來創建一個CroppedBitmap,那麼一切正常。
  • 如果我在代碼中加載BitmapImage,沒有從它創建一個CroppedBitmap,一切正常。
  • 但是,如果我加載代碼中的BitmapImage在代碼中創建一個CroppedBitmap,它會嘗試從文件系統而不是資源加載,並且我得到一個DirectoryNotFoundException。

代碼示例如下。我確信我在做一些愚蠢的事情,但我現在已經完成了三次(一次是在我的真實應用程序中,一次是在測試應用程序中,一次是在寫這個問題的時候),而且我得到了相同的結果結果全部三次。

對於以下所有代碼示例,我在我的項目中創建了一個Images文件夾,並在其中添加了一個名爲「elf.png」的現有圖像,並將屬性設置爲默認值(Build Action =「Resource」; Copy到輸出目錄=「不要複製」)。情況1:XAML中的BitmapImage和CroppedBitmap。

<Window x:Class="WpfApplication8.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="Window1" Height="300" Width="300"> 
    <Window.Resources> 
     <BitmapImage x:Key="fullImage" UriSource="Images/elf.png"/> 
     <CroppedBitmap x:Key="croppedImage" Source="{StaticResource fullImage}" 
         SourceRect="0 0 240 320"/> 
    </Window.Resources> 
    <Image Source="{StaticResource croppedImage}"/> 
</Window> 

這顯示了位圖的裁剪部分,如預期的那樣。


情況2:XAML中的BitmapImage;代碼隱藏的CroppedBitmap。

XAML:

<Window x:Class="WpfApplication8.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="Window1" Height="300" Width="300"> 
    <Window.Resources> 
     <BitmapImage x:Key="fullImage" UriSource="Images/elf.png"/> 
    </Window.Resources> 
    <Image Name="image"/> 
</Window> 

構造在後臺代碼:

public Window1() 
{ 
    InitializeComponent(); 
    var fullImage = (BitmapImage) FindResource("fullImage"); 
    var croppedImage = 
     new CroppedBitmap(fullImage, new Int32Rect(0, 0, 240, 320)); 
    image.Source = croppedImage; 
} 

這也示出了位圖的裁剪部,如所預期。案例3:代碼中的BitmapImage;代碼3中的BitmapImage;代碼3中的BitmapImage;代碼3中的BitmapImage;代碼3中的BitmapImage;代碼3中的BitmapImage;沒有CroppedBitmap。

public Window1() 
{ 
    InitializeComponent(); 
    var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute); 
    var fullImage = new BitmapImage(uri); 
    image.Source = fullImage; 
} 

這顯示了整個位圖。這不是我想要的,但是告訴我,我知道如何編寫C#代碼來創建合適的Uri並從資源加載BitmapImage。


案例4:代碼中的BitmapImage和CroppedBitmap。

public Window1() 
    { 
     InitializeComponent(); 
     var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute); 
     var fullImage = new BitmapImage(uri); 
     var croppedImage = 
      new CroppedBitmap(fullImage, new Int32Rect(0, 0, 240, 320)); 
     image.Source = croppedImage; 
    } 

據我所知,這只是放在一起,像以前一樣。它使用我知道的將從資源加載BitmapImage的代碼,我知道的代碼將從加載的BitmapImage中裁剪一段。但不知何故,當兩者結合在一起時,它會忘記資源在那裏,並試圖從磁盤加載。我得到以下異常:

  • XamlParseException:「無法創建‘窗口1’彙編定義的實例‘WpfApplication8,版本= 1.0.0.0,文化=中立,公鑰=空’異常被拋出由目標在標記文件'Window1.xaml'中的錯誤行1位置13.「
    • 內部異常: TargetInvocationException:「異常被調用的目標拋出。
      • 內部異常: DirectoryNotFoundException: 「找不到路徑 ':\ SVN \ WpfApplication8 \ WpfApplication8 \ BIN \調試\圖像\ elf.png C' 的一部分。」

內-內異常堆棧跟蹤顯示原始異常(在DirectoryNotFoundException)正被用於實例化CroppedBitmap線拋出。我不知道爲什麼那行會嘗試從磁盤讀取數據,或者爲什麼它不能正常工作,當我可以告訴等效的XAML工作正常時。

因爲我知道XAML使用無參數的構造函數,我也試過以下的版本,這應該是更接近什麼XAML實際上做:

public Window1() 
{ 
    InitializeComponent(); 
    var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute); 
    var fullImage = new BitmapImage(); 
    fullImage.BeginInit(); 
    fullImage.UriSource = uri; 
    fullImage.EndInit(); 
    var croppedImage = new CroppedBitmap(); 
    croppedImage.BeginInit(); 
    croppedImage.Source = fullImage; 
    croppedImage.SourceRect = new Int32Rect(0, 0, 240, 320); 
    croppedImage.EndInit(); 
    image.Source = croppedImage; 
} 

同樣的異常,此時距離croppedImage.EndInit();線。

關於如何讓全代碼版本正確加載資源並裁剪圖像的任何想法? XAML版本中發生了什麼變化?

回答

4

神奇竟然是在BitmapImage的BaseUri屬性中。 BaseUri顯然是UriSource相關的「當前目錄」。

當我的BitmapImage從XAML加載時,BaseUri被神奇地設置爲「pack:// application:,,,/WpfApplication8; component/window1.xaml」。當我修改代碼片段#4在創建CroppedBitmap之前將fullImage.BaseUri顯式設置爲相同的值時,一切正常。

爲什麼從XAML工作(從剛剛的BitmapImage - 不 - CroppedBitmap)

那麼,沒有這種神奇的基本URI值從何而來?

BaseUri是IUriContext接口的一部分。 IUriContext.BaseUri被設置在WPF程序集中的兩個地方,並且在我的各種示例之間,我設法擊中了兩個。難怪我很困惑。

  • BamlRecordReader.ElementInitialize。 BAML加載器在加載實現IUriContext的元素時自動設置BaseUri。這解釋了爲什麼我的例子#1和#2工作:他們從編譯的BAML資源加載。
  • Image.UpdateBaseUri(每當Source屬性發生變化時調用)。這將檢查Source是否實現IUriContext,如果是,則設置它的BaseUri。這解釋了爲什麼我的示例#3工作:將BitmapImage推入GUI迫使它獲得正確的搜索路徑。

當BaseUri設置爲magic pack:// URI時,它僅查找EXE資源中的圖像。如果沒有這種情況(當所有事情都是在代碼中創建並且沒有推入GUI時),它只會在磁盤上顯示。

的修復

如上所述,我可以硬編碼的基本URI。但BaseUriHelper類提供了一個更好的解決辦法:

fullImage.BaseUri = BaseUriHelper.GetBaseUri(this); 

這臺fullImage具有相同的基本URI的窗口(this)。如果這是在創建CroppedBitmap之前完成的,則一切正常。