2015-12-25 17 views
0

我有一個應用程序可以即時創建WPF圖像控件以顯示來自SQL Server數據庫表的圖像。顯示控件的面板經常被更換。我使用MemoryStreams來允許控件訪問從數據庫加載的字節數組。只要顯示圖像控件並且仍然顯示圖像,我就無法關閉這些流。我擔心的是,這些流將保持開放狀態,直到垃圾收集器開始處理圖像控件並希望關閉流。我可能會最終確定在更換面板之前關閉了流或丟棄了圖像控件,但是想知道是否存在將這些流打開以及是否存在解決方法的問題。WPF字節數組映射到ImageSource而不使用MemoryStream

下面是我使用的代碼。

public class ImageToSql 
{ 
    private static OpenFileDialog _openFileDialog = new OpenFileDialog(); 

    static ImageToSql() 
    { 
     _openFileDialog = new OpenFileDialog(); 
     _openFileDialog.AddExtension = true; 
     _openFileDialog.CheckFileExists = true; 
     _openFileDialog.DefaultExt = ".jpg"; 
     _openFileDialog.Filter = "JPEG files (*.jpg)|*.jpg|All files (*.*)|*.*"; 
     _openFileDialog.FilterIndex = 1; 
     _openFileDialog.Multiselect = false; 
    } 

    public static BitmapImage LoadImageFromFile(string caption) 
    { 
     BitmapImage bi = new BitmapImage(); 

     _openFileDialog.Title = caption; 
     if (_openFileDialog.ShowDialog() == true) 
     { 
      bi.BeginInit(); 
      bi.StreamSource = File.OpenRead(_openFileDialog.FileName); 
      bi.EndInit(); 
     } 

     return bi; 
    } 

    public static int StoreImage(string connectionString, string sql, string imageParameterName, BitmapImage bitmapImage) 
    { 
     SqlConnection conn = new SqlConnection(connectionString); 
     try 
     { 
      conn.Open(); 
      sql += ";SELECT @@IDENTITY"; 
      using (SqlCommand cmd = new SqlCommand(sql, conn)) 
      { 
       byte[] data = new byte[bitmapImage.StreamSource.Length]; 
       bitmapImage.StreamSource.Seek(0, SeekOrigin.Begin); 
       bitmapImage.StreamSource.Read(data, 0, data.Length); 

       cmd.Parameters.Add(imageParameterName, System.Data.SqlDbType.VarBinary, -1).Value = data; 
       object obj = cmd.ExecuteScalar(); 
       return Int32.Parse(obj.ToString()); 
      } 
     } 
     finally 
     { 
      conn.Close();     
     } 
    } 

    public static BitmapImage RetrieveImage(string connectionString, string sql, string imageIDParameterName, int imageID) 
    { 
     BitmapImage bi = new BitmapImage(); 
     SqlConnection conn = new SqlConnection(connectionString); 
     try 
     { 
      conn.Open(); 
      using (SqlCommand cmd = new SqlCommand(sql, conn)) 
      { 
       cmd.Parameters.Add(imageIDParameterName, System.Data.SqlDbType.Int).Value = imageID; 
       byte[] data = (byte[])cmd.ExecuteScalar(); 

       MemoryStream ms = new MemoryStream(data); 
       ms.Seek(0, SeekOrigin.Begin); 
       bi.BeginInit(); 
       bi.StreamSource = ms; 
       bi.EndInit(); 
      } 
     } 
     finally 
     { 
      conn.Close(); 
     } 

     return bi; 
    } 
} 

下面是如何調用這段代碼。 「_fileImage」和「_sqlImage」是我測試窗口上的圖像控件。有一次,我使用流在「LoadImageFromFile」中從磁盤讀取文件,並認爲我能夠關閉該流,同時仍然在Image控件中顯示圖像。所以,MemoryStream似乎很特別。

 BitmapImage bi = ImageToSql.LoadImageFromFile("Select Image to Save"); 
     _fileImage.Source = bi; 

     int imageID = ImageToSql.StoreImage(connString, 
      "INSERT INTO PV_Image (Description, Image) VALUES ('Some Image', @pImage)", 
      "@pImage", bi); 

     _sqlImage.Source = ImageToSql.RetrieveImage(connString, 
      "SELECT Image FROM PV_Image WHERE ImageID = @pImageID", 
      "@pImageID", imageID); 
+0

好吧,我可能已經使用了一個「使用」上的SqlConnection自動關閉對我來說。舊習難改。 – 2old4this

回答

0

可以處理存儲流一旦使用使用模式中使用:

using (Stream stream = new MemoryStream(data)) 
{ 
    BitmapImage image = new BitmapImage(): 
    stream.Position = 0; 
    image.BeginInit(); 
    image.CacheOption = BitmapCacheOption.OnLoad; 
    image.StreamSource = stream; 
    image.EndInit(); 
    image.Freeze(); 
    return image; 
} 
+0

謝謝。所以,訣竅是我需要設置緩存選項,然後凍結圖像。我將不得不做一些研究來理解爲什麼。現在,我更加舒適地關閉這些流,然後將它們打開,希望稍後可以通過我的代碼或GC關閉它們。 – 2old4this

+0

from msdn:如果您希望關閉用於創建BitmapImage的流,請將CacheOption設置爲BitmapCacheOption.OnLoad。默認的OnDemand緩存選項保留對流的訪問權限,直到需要映像爲止,並由垃圾收集器處理清理。 – Wouter

+0

另外:bitmapImage在凍結時是可凍結的,它變得不可變,它具有性能優勢(不需要更改通知),並且它變爲可交叉線程訪問。它似乎也可以防止某些內存泄漏。 – Wouter