近日项目,需要用到动态的GIF做背景。于是从PictureBox继承了一个控件,希望以最少的代码解决动态背景的问题。
起先一切都顺利,用PictureBox的Image属性很快就能够搞定一切,能够支持的图片格式也基本满足要求,遂以为一切大功告成。工作完成,我需要将背景图片等信息保存,然后在另外一个程序中打开、运行(一个设计时效果,一个运行时效果)。然而,再次打开时,程序报错:GDI+中出现一般性错误(Generic error in GDI+),报错的代码是base.Paint(e),也就是执行PictureBox基类的Paint函数时出错。
保存图片的代码是这样的:
MemoryStream ms = new MemoryStream();
this.Image.Save(ms, Image.RawFormat);
byte[] serializedObject = ms.ToArray();
ms.Close();
XmlElement value = node.OwnerDocument.CreateElement("Image");
String strImage = Convert.ToBase64String(serializedObject);
value.AppendChild(node.OwnerDocument.CreateTextNode(strImage));
bkImage.AppendChild(value);
打开是其逆过程:
XmlElement value = xmlImage.SelectSingleNode("Image") as XmlElement;
if (value == null)
return;
byte[] dsBytes;
dsBytes = Convert.FromBase64String(value.InnerText);
if (dsBytes.Length > 0)
{
MemoryStream ms = new MemoryStream(dsBytes);
this.Image = Image.FromStream(ms);
ms.Close();
}
自认为这样的代码不会有错的,除非反持续化过程中图片的格式发生了改变。可是跟踪下去,发现持续化前后图片类型的GUID是相同的,由此肯定图片格式没有出错。
那问题在哪里?
VS2005的异常报告里面显示,是在Image.SelectActiveFrame()这个函数里面出错了,查MSDN,没有任何有用的信息。于是Google,一搜才知道,原来不少人都遇到了这个问题。于是兴奋,以为会有很好的解决方法。可惜失望了,遇到这个问题的人不少,可有解决方法的却寥寥无几。似乎我找到的唯一的一个解决办法就是使用Load()函数代替动态创建图片,这显然不能满足我的需求(我需要将图片也保存到XML字段中)。
无奈,只能自己想办法。
找不到问题的原因(不能跟踪,系统代码),就只能猜测。我的思路是:我仅仅保存了Image属性的图片,也许,Image属性还有其它分量。那么,我应当保存整个Image属性,而不仅仅是其引用的图片。
于是修改代码,保存属性的代码如下:
if (this.Image != null)
{
bkImage.SetAttribute("IsSet", "1");
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(this);
PropertyDescriptor ptyImage = properties.Find("Image", false);
if (ptyImage != null)
{
MemoryStream ms = new MemoryStream();
BinaryFormatter format = new BinaryFormatter();
object obj = ptyImage.GetValue(this);
format.Serialize(ms, obj);
ms.Seek(0, 0);
byte[] serializedObject = ms.ToArray();
ms.Close();
XmlElement value = node.OwnerDocument.CreateElement("Image");
String strImage = Convert.ToBase64String(serializedObject);
value.AppendChild(node.OwnerDocument.CreateTextNode(strImage));
bkImage.AppendChild(value);
}
}
else
bkImage.SetAttribute("IsSet", "0");
恢复属性的代码:
byte[] dsBytes;
dsBytes = Convert.FromBase64String(value.InnerText);
if (dsBytes.Length > 0)
{
MemoryStream ms = new MemoryStream(dsBytes);
BinaryFormatter format = new BinaryFormatter();
object obj = format.Deserialize(ms);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(this);
PropertyDescriptor ptyImage = properties.Find("Image", false);
if (ptyImage != null)
ptyImage.SetValue(this, obj);
ms.Close();
}
编译,通过;运行,保存成功;关闭,再次打开,GIF在动,激动啊!
总结:我把整个属性以二进制的形式保存下来,这些内容相当于是从内存里面抠出来的,等到需要的时候再次写回内存,类似于XP里面的休眠机制,能够避免一些系统底层的未知原因导致的错误。
起先一切都顺利,用PictureBox的Image属性很快就能够搞定一切,能够支持的图片格式也基本满足要求,遂以为一切大功告成。工作完成,我需要将背景图片等信息保存,然后在另外一个程序中打开、运行(一个设计时效果,一个运行时效果)。然而,再次打开时,程序报错:GDI+中出现一般性错误(Generic error in GDI+),报错的代码是base.Paint(e),也就是执行PictureBox基类的Paint函数时出错。
保存图片的代码是这样的:
MemoryStream ms = new MemoryStream();
this.Image.Save(ms, Image.RawFormat);
byte[] serializedObject = ms.ToArray();
ms.Close();
XmlElement value = node.OwnerDocument.CreateElement("Image");
String strImage = Convert.ToBase64String(serializedObject);
value.AppendChild(node.OwnerDocument.CreateTextNode(strImage));
bkImage.AppendChild(value);
打开是其逆过程:
XmlElement value = xmlImage.SelectSingleNode("Image") as XmlElement;
if (value == null)
return;
byte[] dsBytes;
dsBytes = Convert.FromBase64String(value.InnerText);
if (dsBytes.Length > 0)
{
MemoryStream ms = new MemoryStream(dsBytes);
this.Image = Image.FromStream(ms);
ms.Close();
}
自认为这样的代码不会有错的,除非反持续化过程中图片的格式发生了改变。可是跟踪下去,发现持续化前后图片类型的GUID是相同的,由此肯定图片格式没有出错。
那问题在哪里?
VS2005的异常报告里面显示,是在Image.SelectActiveFrame()这个函数里面出错了,查MSDN,没有任何有用的信息。于是Google,一搜才知道,原来不少人都遇到了这个问题。于是兴奋,以为会有很好的解决方法。可惜失望了,遇到这个问题的人不少,可有解决方法的却寥寥无几。似乎我找到的唯一的一个解决办法就是使用Load()函数代替动态创建图片,这显然不能满足我的需求(我需要将图片也保存到XML字段中)。
无奈,只能自己想办法。
找不到问题的原因(不能跟踪,系统代码),就只能猜测。我的思路是:我仅仅保存了Image属性的图片,也许,Image属性还有其它分量。那么,我应当保存整个Image属性,而不仅仅是其引用的图片。
于是修改代码,保存属性的代码如下:
if (this.Image != null)
{
bkImage.SetAttribute("IsSet", "1");
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(this);
PropertyDescriptor ptyImage = properties.Find("Image", false);
if (ptyImage != null)
{
MemoryStream ms = new MemoryStream();
BinaryFormatter format = new BinaryFormatter();
object obj = ptyImage.GetValue(this);
format.Serialize(ms, obj);
ms.Seek(0, 0);
byte[] serializedObject = ms.ToArray();
ms.Close();
XmlElement value = node.OwnerDocument.CreateElement("Image");
String strImage = Convert.ToBase64String(serializedObject);
value.AppendChild(node.OwnerDocument.CreateTextNode(strImage));
bkImage.AppendChild(value);
}
}
else
bkImage.SetAttribute("IsSet", "0");
恢复属性的代码:
byte[] dsBytes;
dsBytes = Convert.FromBase64String(value.InnerText);
if (dsBytes.Length > 0)
{
MemoryStream ms = new MemoryStream(dsBytes);
BinaryFormatter format = new BinaryFormatter();
object obj = format.Deserialize(ms);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(this);
PropertyDescriptor ptyImage = properties.Find("Image", false);
if (ptyImage != null)
ptyImage.SetValue(this, obj);
ms.Close();
}
编译,通过;运行,保存成功;关闭,再次打开,GIF在动,激动啊!
总结:我把整个属性以二进制的形式保存下来,这些内容相当于是从内存里面抠出来的,等到需要的时候再次写回内存,类似于XP里面的休眠机制,能够避免一些系统底层的未知原因导致的错误。