Может ли это использование System.Drawing.Image.FromStream указывать на ту же память?

Мне нужно исправить ошибку в каком-то очень старом коде, который преобразует строку Base64 в изображение с помощью потока памяти. По сути, он перебирает список изображений, хранящихся в виде строк base64, и преобразует их в изображения, а затем рисует их с помощью ActiveReports.

Ошибка в том, что после загрузки одного изображения все последующие изображения будут копиями первого изображения.

Я нашел код, который выполняет преобразование строки в изображение, и сразу заметил, что он не избавляется от потока памяти. Если я оборачиваю поток памяти в используемый блок, я получаю исключение 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)
        {
            .....
        }

введите здесь описание изображения


person NullReference    schedule 23.05.2012    source источник


Ответы (5)


Единственное, что я могу себе представить, это если в том, что у вас есть, отсутствует строка кода:

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

Нет причин объявлять этот byte[] вне блока try { }. Этот массив заворачивается в MemoryStream, который затем заворачивается в изображение. Это изображение прикреплено к совершенно новому изображению, которое добавляется в коллекцию изображений.

Что мы не видим?

person Tergiver    schedule 23.05.2012
comment
Я добавил объявление переменной oGraphic, почти все остальное есть. - person NullReference; 24.05.2012
comment
@NullReference, что происходит с послесловиями oGraphic? Я сейчас хватаюсь за соломинку. - person Tergiver; 24.05.2012
comment
oGraphic — это локальная переменная, и после ее использования здесь используется new MemoryStream(oGraphic); он больше не используется. - person NullReference; 24.05.2012

Вот еще одна догадка (я оставлю первую догадку для потомков):

Я не знаком с Active Reports, но похоже, что вы устанавливаете для свойств Top и PictureAlignment объекта Picture одно и то же значение и добавляете более одного изображения. Возможно ли, что они все там, но один над другим? То есть в результате получается одна картинка?

person Tergiver    schedule 23.05.2012
comment
похоже, что oImg.Top увеличивается, и после просмотра кода я подтвердил, что значения .Top разные - person NullReference; 24.05.2012

Угадай №3 (один из них получит галочку, я точно знаю!)

В представленном коде все выглядит нормально, поэтому проблема в другом (хотя вполне возможно, что я ошибаюсь в том, что все в порядке).

Вы уверены, что psGraphic меняется каждый раз при выполнении этого кода?

person Tergiver    schedule 23.05.2012

Причина проблемы в том, что элемент управления Picture является единственным экземпляром элемента управления в одном разделе. Таким образом, вы просто перезаписываете изображение на этом единственном элементе управления снова и снова.

Если единственное, что вы хотите видеть в этом отчете, — это изображения, то лучше всего использовать несвязанный режим ActiveReports и рассматривать каждое изображение как еще одну «запись». См. это пошаговое руководство для примера использования несвязанного режима (см. события DataInitialize и FetchData по сути дела).

Используя несвязанный режим, ActiveReports будет отображать изображения одно за другим в разделах, рассматривая каждое изображение как новую запись. Код будет примерно таким (извините, в данный момент у меня нет под рукой ActiveReports, поэтому я не могу проверить этот код, но он должен быть довольно близким. Дайте мне знать, если у вас возникнут какие-либо проблемы, и я уберу это утром):

В событии ReportStart ActiveReports:

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);

В событии DataInitialize ActiveReports:

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 также автоматически привязывается к набору объектов POCO в IEnumerable (или IList, я забыл). Таким образом, вы могли бы просто иметь класс «MyImage» со свойством, таким как «MyImage», и ActiveReports будет читать его и связываться с ним (вам не нужно будет писать какой-либо код в DataInitialize и FetchData). Я думаю, что вы также можете просто поместить туда MemoryStream в качестве привязки, и ActiveReports прочитает его, но я не уверен в этом.

Кстати: причина, по которой исключение GDI возникает при удалении MemoryStream, заключается в том, что GDI пытается просто искать в этом единственном MemoryStream данные изображения, а не делать его копию. Таким образом, вам нужно снабдить каждый экземпляр System.Drawing.Image новым потоком (не волнуйтесь, MemoryStream очистится, когда все будет выпущено).

person Scott Willeke    schedule 24.05.2012
comment
Вау, большое спасибо за подробный ответ. Я попробую это очень быстро и вернусь к вам. Спасибо еще раз! - person NullReference; 24.05.2012
comment
Итак, мой дополнительный вопрос: есть ли способ создать новый экземпляр элемента управления изображением в существующем методе? - person NullReference; 24.05.2012
comment
@NullReference конечно, вы можете создать столько экземпляров, сколько хотите, просто обязательно сделайте это до того, как отчет начнет выполняться (до или до события ActiveReport_ReportStart), и обязательно добавьте его в раздел, как это было сделано в первом блоке код выше в моем ответе. Однако в целом использование подхода без привязки или привязки к данным с одним элементом управления Picture будет более эффективным (и, вероятно, проще). - person Scott Willeke; 24.05.2012
comment
Я могу подтвердить, что код в моем исходном сообщении происходит в событии ActiveReport_ReportStart. Часть разделов - это то, что похоже, отсутствует. Из моего исходного сообщения this.GHMap.Controls.Add имеет тип GroupHeader, так что мне нужно добавлять к нему разделы? - person NullReference; 24.05.2012
comment
Можно использовать GroupHeader для изображений, но это имеет смысл только в том случае, если у вас есть несколько экземпляров группы. Например, если в GroupHeader отображается информация о категории продуктов, и в выходных данных отчета есть несколько категорий, а в деталях отображается список продуктов в этой категории, это имеет смысл. Однако имейте в виду, что во время ReportStart вы имеете дело только с первым экземпляром GroupHeader/PictureControl. Поэтому, если этот GroupHeader повторяется (из-за данных), вам нужно будет использовать FetchData, чтобы получить последующие изображения в правильном экземпляре раздела. - person Scott Willeke; 24.05.2012
comment
@NullReference, если вам нужна дополнительная помощь, было бы здорово, если бы вы могли рассказать нам больше об отчете, например, привязан ли он к данным? Какой еще код содержится в программном коде отчета (был бы полезен весь код из этого файла)? И вообще, какой результат вы пытаетесь получить. - person Scott Willeke; 24.05.2012
comment
Я очень ценю помощь. Я обновил вопрос, добавив более подробную информацию о выводе отчета. Поиграв с ним еще немного, я заметил, что изображения определенного типа, круглые цветные значки, являются единственными, которые дублируются. У меня есть другие значки, O и I на изображении, которые отображаются без дублирования с использованием того же метода. Красные значки дублируются... так расстраивает - person NullReference; 25.05.2012
comment
Я не понимаю. В каком событии код выше? Какой еще код есть в отчете? Нам понадобится больше информации, чтобы выяснить это. Возможно, вам лучше всего обратиться в службу поддержки ActiveReports, чтобы вы могли отправить отчет и некоторые образцы данных по электронной почте, если вы не можете опубликовать эту информацию публично. - person Scott Willeke; 25.05.2012

Оказалось, что проблема в том, как мы создавали образ. Добавление приведенного ниже кода устранило проблему.

   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);
            }
        }
person NullReference    schedule 25.05.2012