0

我不得不修复一些非常旧的代码中的一个错误,该代码使用内存流将 Base64 字符串转换为图像。基本上,它遍历存储为 base64 字符串的图像列表并将它们转换为图像,然后使用 ActiveReports 绘制它们。

错误是一旦加载一个图像,所有后续图像都将是第一个图像的副本。

我找到了将字符串转换为图像的代码,并立即注意到它没有处理内存流。如果我将内存流包装在 using 块中,则会出现 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)
        {
            .....
        }

在此处输入图像描述

4

5 回答 5

1

我唯一能想象的是,如果您在这里拥有的代码中缺少一行代码:

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

没有理由在 try { } 块之外声明这个 byte[]。该数组被包装到 MemoryStream 中,然后再被包装到 Image 中。该图像附加到添加到图片集合中的全新图片。

什么是我们看不到的?

于 2012-05-23T22:20:29.043 回答
1

这是另一个猜测(我将把第一个猜测留给后代):

我不熟悉 Active Reports,但看起来您正在将 Picture 对象的 Top 和 PictureAlignment 属性设置为相同的值并添加多个图片。有没有可能它们都在那里,但一个在彼此之上?所以结果是单张图片?

于 2012-05-23T22:44:00.537 回答
1

猜猜#3 (其中一个会打勾,我就知道!)

在提供的代码中一切看起来都很好,因此问题出在其他地方(尽管我仍然完全有可能错了它是好的)。

您确定每次执行此代码时 psGraphic 都不同吗?

于 2012-05-23T23:06:16.173 回答
1

问题的原因是图片控件是单个部分上的单个控件实例。所以你只是一遍又一遍地覆盖这个单一控件上的图像。

如果您只想在此报告中看到图像,那么最好的做法是使用 ActiveReports 的 Unbound 模式并将每个图像视为另一个“记录”。有关使用未绑定模式的示例,请参阅此演练(请参阅 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(或 IList,我忘记了)中的一组 POCO 对象。因此,您可以简单地拥有一个具有类似“MyImage”属性的“MyImage”类,ActiveReports 会读取它并与之绑定(您不必在 DataInitialize 和 FetchData 中编写任何代码)。我认为您也可以将 MemoryStream 作为绑定放在那里,ActiveReports 会读取它,但我对此并不积极。

顺便说一句:处理 MemoryStream 时发生 GDI 异常的原因是因为 GDI 试图仅在该单个 MemoryStream 中寻找图像数据,而不是制作它的副本。因此,您需要为每个 System.Drawing.Image 实例提供一个新流(不要担心 MemoryStream 会在所有内容发布后自行清理)。

于 2012-05-24T05:30:56.373 回答
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);
            }
        }
于 2012-05-25T16:13:36.303 回答