C# 不使用MemoryStream将WPF字节数组复制到ImageSource
我有一个应用程序,可以动态创建WPF图像控件来显示SQL Server数据库表中的图像。显示控件的面板经常被更换。我使用MemoryStream允许控件访问从数据库加载的字节数组。我无法关闭这些流,只要显示图像控件并且仍然显示图像。我担心的是,这些流将一直保持打开状态,直到垃圾收集器开始处理图像控件,并希望关闭这些流。最后,我可能会确保在更换面板之前关闭流或处理图像控件,但我想知道让这些流保持打开是否有问题,以及是否有解决方法 这是我正在使用的代码C# 不使用MemoryStream将WPF字节数组复制到ImageSource,c#,sql-server,wpf,image,C#,Sql Server,Wpf,Image,我有一个应用程序,可以动态创建WPF图像控件来显示SQL Server数据库表中的图像。显示控件的面板经常被更换。我使用MemoryStream允许控件访问从数据库加载的字节数组。我无法关闭这些流,只要显示图像控件并且仍然显示图像。我担心的是,这些流将一直保持打开状态,直到垃圾收集器开始处理图像控件,并希望关闭这些流。最后,我可能会确保在更换面板之前关闭流或处理图像控件,但我想知道让这些流保持打开是否有问题,以及是否有解决方法 这是我正在使用的代码 public class ImageToSql
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;
}
}
下面是如何调用此代码。“\u fileImage”和“\u sqlImage”是我的测试窗口上的图像控件。有一次,我在“LoadImageFromFile”中使用一个流从磁盘读取文件,并且认为我能够在图像控件中显示图像的同时关闭该流。所以,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);
一旦使用内存流,您可以使用以下使用模式来处置它:
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;
}
好的,我可能已经在SqlConnection上使用了一个“using”来自动关闭它了。旧习难改。谢谢。所以,诀窍是我需要设置缓存选项,然后冻结图像。我必须做一些研究来了解原因。现在,我更愿意关闭流,然后让它们保持打开状态,以便稍后由我的代码或GC关闭。from msdn:如果希望关闭用于创建BitmapImage的流,请将CacheOption设置为BitmapCacheOption.OnLoad。默认的OnDemand缓存选项保留对流的访问,直到需要映像并且垃圾回收器处理清理。此外:bitmapImage在冻结时是可冻结的,它将变为不可变,并且具有性能优势(无需更改通知),并且可以跨线程访问。它似乎还可以防止某些内存泄漏。