2012-05-23 56 views
0

我不得不修復一些使用Memory Stream將Base64字符串轉換爲圖像的非常舊的代碼中的錯誤。基本上,它循環遍歷一系列存儲爲base64字符串的圖像,並將它們轉換爲圖像,然後使用ActiveReports繪製它們。System.Drawing.Image.FromStream的這種用法可以指向相同的內存嗎?

這個錯誤是,一旦它加載一個圖像,所有下面的圖像將是第一個圖像的副本。

我發現正在做字符串轉換爲圖像的代碼,並立即注意到它沒有處理內存流。如果我在使用塊中包裝內存流,我會得到一個GDI異常。我猜這是因爲圖像還沒有真正從內存中讀取,但我想聽聽有沒有人猜測。提前致謝!

 byte[] oGraphic = null; 
     try 
     { 
      oGraphic = Convert.FromBase64String(psGraphic); 

      DataDynamics.ActiveReports.Picture oImg = new Picture(); 
      oImg.Top = this.Legend.Top + this.fTopFirst; 
      oImg.Visible = true; 
      oImg.Name = sLabelName; 
      oImg.PictureAlignment = PictureAlignment.Center; 

      oImg.Image = null; 

      if (oGraphic != null) 
      { 
       var oStream = new MemoryStream(oGraphic); 
       oImg.Image = System.Drawing.Image.FromStream(oStream); 
       oImg.Height = Convert.ToSingle(oImg.Image.Height)/(oImg.Image.VerticalResolution); 
       oImg.Width = Convert.ToSingle(oImg.Image.Width)/(oImg.Image.HorizontalResolution); 
       oImg.SizeMode = SizeModes.Zoom; 
       this.fGraphicHeight = oImg.Height; 
       this.fGraphicWidth = oImg.Width; 
       if (this.fConstantGraphic > this.fGraphicWidth) 
        oImg.Left = this.Legend.Left + this.fLeftFirst + 
           ((this.fConstantGraphic - this.fGraphicWidth)/2); 
       else 
        oImg.Left = this.Legend.Left + this.fLeftFirst; 

      } 
      else 
      { 
       this.fGraphicHeight = 0f; 
       this.fGraphicWidth = 0f; 
      } 

      this.GHMap.Controls.Add(oImg); 
     } 
     catch (Exception oE) 
     { 
      ..... 
     } 

enter image description here

回答

0

結果是我們創建圖像的方式存在問題。添加下面的代碼可以解決問題。

oImg.Image = System.Drawing.Image.FromStream(oStream); 

    TO THIS 

    oImg.Image = ImageFromBase64String(psGraphic); 

    private Image ImageFromBase64String(string sBase64String) 
     { 
      using (var sStream = new MemoryStream(Convert.FromBase64String(sBase64String))) 
      using (var iSourceImage = Image.FromStream(sStream)) 
      { 
       return new Bitmap(iSourceImage); 
      } 
     } 
1

我所能想象的唯一的事情是,如果存在的代碼從你這裏有什麼缺少一行:

if (oGraphic == null) // missing line 
    oGraphic = Convert.FromBase64String(psGraphic); 

沒有原因的byte []到在try {}塊之外聲明。該數組被包裝到一個MemoryStream中,然後被包裝到一個Image中。該圖像附加到添加到圖片收藏中的全新圖片。

它是什麼我們沒有看到?

+0

我添加了oGraphic變量的聲明,幾乎所有的東西都在那裏。 – NullReference

+0

@NullReference oGraphic後綴會發生什麼?我現在正在抓秸稈。 – Tergiver

+0

oGraphic是一個局部變量,在它用於這裏後,新的MemoryStream(oGraphic);它不再被使用。 – NullReference

1

這裏是另一種猜測(我會獨自離開第一猜測後人):

我不熟悉的主動報告,但它看起來像你設置的圖片對象的頂部和PictureAlignment性質到相同的值並添加多個圖片。他們可能都在那裏,但一個在另一個之上嗎?所以結果是一張照片?

+0

它看起來像oImg.Top正在增加,並通過代碼後,我已經證實.Top值不同 – NullReference

1

競猜#3(這些人會得到一個對號,我只知道它!)

一切看起來OK中提供的代碼,所以這個問題是在其他地方(雖然它仍然是完全有可能我錯了,沒關係)。

您確定每次執行此代碼時psGraphic都不同嗎?

1

問題的原因在於Picture控件是單個部分上的單個控件實例。所以你只是一遍又一遍地重寫這個單一控件上的圖像。

如果你想在這個報告中看到的唯一東西是圖像,那麼最好的辦法是使用ActiveReports的Unbound模式,並將每個圖像視爲另一個「記錄」。請參閱this walkthrough for an example of using unbound mode(請參閱DataInitialize和FetchData事件以瞭解有關內容)。

使用未綁定模式時,ActiveReports將逐個呈現圖像,將圖像像新記錄一樣對待每個圖像。代碼將如下所示(對不起,我目前沒有ActiveReports,所以我無法檢查此代碼,但這應該非常接近。讓我知道如果你遇到任何問題,我會在早上清理):

在ActiveReports中ReportStart事件:

DataDynamics.ActiveReports.Picture oImg = new Picture(); 
oImg.Top = 0; 
oImg.Visible = true; 
oImg.Name = sLabelName; 
oImg.PictureAlignment = PictureAlignment.Center; 
// setting DataField "binds" the Picture control to get it's data from the MyImageField field which we'll initialize and bind in the events below 
oImg.DataField = "MyImageField"; 
this.Sections["Detail"].Controls.Add(oImg); 

在ActiveReports中DataInitialize事件:

this.Fields.Add("MyImageField"); 

在ActiveReports中FetchData事件:

var imageBytes = Convert.FromBase64String(_imageStrings.Current); // I'm not sure where the base64 image strings come from, some I'm assuming you can put them in an enumerator field in the report like "_imageStrings" 
var imageStream = new MemoryStream(imageBytes); 
var image = Image.FromStream(imageStream); 
Fields["MyImageField"].Value = image; 

// This tells ActiveReports if there are more records, and if it should raise the FetchData event again (allowing you to add another image). 
eArgs.EOF = !_imageStrings.MoveNext(); 

如果您需要調整每個圖像的圖像控件大小,請使用該部分的Format事件。你可以使用類似以下內容:

在Detail_Format事件:

var pictureControl = this.Sections["Detail"].Controls["MyImageControl"] as DataDynamics.ActiveReports.Picture; 
pictureControl.Width = Convert.ToSingle(pictureControl.Image.Width)/(pictureControl.Image.VerticalResolution); 
pictureControl.Width = Convert.ToSingle(pictureControl.Image.Width)/(pictureControl.Image.HorizontalResolution); 

最後的ActiveReports也只是自動綁定到一個IEnumerable一套POCO對象(或IList的,我忘了)。因此,您可以簡單地擁有一個「MyImage」類,並具有像「MyImage」這樣的屬性,ActiveReports將讀取並綁定它(您不必在DataInitialize和FetchData中編寫任何代碼)。我想你也可能只是將MemoryStream放在那裏作爲綁定,ActiveReports也會讀取它,但我對此並不積極。

BTW:處置MemoryStream時出現GDI異常的原因是因爲GDI試圖在圖像數據的單個MemoryStream中尋找而不是複製它。因此,您需要爲每個System.Drawing.Image實例提供一個新的流(不要擔心MemoryStream會在釋放所有內容時自行清理)。

+0

哇,非常感謝詳細的答案。我會盡快嘗試並回復你。再次感謝! – NullReference

+0

所以我的後續問題是有沒有辦法在現有的方法內創建一個新的圖片控件實例? – NullReference

+0

@NullReference當然,您可以根據需要創建任意數量的實例,只需確保在報告開始運行之前(在ActiveReport_ReportStart事件中或之前)執行它,並確保將其添加到第一個塊中完成的部分上面的代碼在我的答案。通常,使用單個Picture控件的未綁定或數據綁定方法會更高效(可能更容易)。 –

相關問題