Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Allgemeiner Fehler in der GDI+
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

Allgemeiner Fehler in der GDI+

beantworten | zitieren | melden

Guten Abend zusammen,

ich habe leider mit meinem Code ein kleines Problem und komme nicht weiter.
Ich habe eine Userform mit mehreren Buttons. Dazu eine PictureBox
Das Bild wird wie folgend hochgeladen:

private void btnLoadPicture_Click(object sender, EventArgs e)
        {
            OpenFileDialog OF = new OpenFileDialog();
            OF.Title = "Bitte Bild wählen...";
            OF.Multiselect = false;
            OF.Filter = "Bilder|*.jpeg;*.jpg;*.png;*.bmp|JPG-Bilder|*.jpeg;*jpg|PNG-Bilder|*.png|BMP-Bilder|*.bmp";
            DialogResult DR = OF.ShowDialog();

            if (DR == DialogResult.OK)
            {
                iContactPicture = Image.FromFile(OF.FileName);
                pictureContact.Image = iContactPicture;
            }
            else
            {
                pictureContact.Image = global::Personaldaten.Properties.Resources.Who_is_it;
            }
        }

Drücke ich auf den Button Save wird das Bild in einen String umgewandelt und zusammen mit den Infos aus den Textboxen in einer Datei gespeichert:

 private string ImageToString(Image img, ImageFormat imgFormat)
        {
            string sImg;
            MemoryStream MS = new MemoryStream();
            img.Save(MS, imgFormat); //Hier tritt der Fehler auf!!!!
            sImg = Convert.ToBase64String(MS.ToArray());
            MS.Close();
            return sImg;
        }

Über einen Öffnen Button kann ich jetzt die Inhalte der Textboxen und der PictureBox wieder mit den Inhalten der Datei befüllen. Drücke ich jetzt direkt wieder auf Speichern (weil ich irgendwelche Textboxen geändert habe) bekomme ich den im Titel genannten Fehler. Wenn ich aber erneut ein Bild auswähle bekomme ich diesen Fehler nicht.

Ich hoffe ihr könnt mir weiterhelfen :)

Vielen Dank schon mal.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.761

beantworten | zitieren | melden

Alle nicht verwalteten Ressourcen in .NET müssen manuell disposed werden (siehe Basics).
Gleicher Fall wie im anderen Thread; man muss nur Lösung von dort auf Deinen Code anwenden.

Es ist nicht mal im Ansatz erkenntlich, wie die Methode ImageToString verwendet wird.
Aber das Image objekt gehört zu den nicht-verwalteten Ressourcen (siehe riesiger, Monitor-großer Hinweis-Banner, den man quasi nicht übersehen kann (er ist drei Mal auf der Seite!), in der Dokumentation)

Sofern Du das Bild von einer Datei lädst, wäre zB die korrekte Implementierung


        private string ImageToString(string imagePath, ImageFormat imgFormat)
        {
            string sImg;
            using(MemoryStream memoryStream = new MemoryStream())
            using(Image img = Image.FromFile(imagePath))
            {
               img.Save(memoryStream, imgFormat);
               sImg = Convert.ToBase64String(memoryStream.ToArray());
            }
            return sImg;
        }

Alternativ direkt einfach mit ReadAllBytes ohne Stream-Handling die Inhalte lesen.

ps: [Artikel] C#: Richtlinien für die Namensvergabe.

pictureContact.Image = global::Personaldaten.Properties.Resources.Who_is_it;
Und wenn man "Global" liest, dann schaut das auch nicht so aus, dass hier noch mehr Potential im Code steckt ;-)
private Nachricht | Beiträge des Benutzers
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

beantworten | zitieren | melden

Zitat von Abt
Es ist nicht mal im Ansatz erkenntlich, wie die Methode ImageToString verwendet wird.

Sorry hier der Code:

public void SetGeneralVars(string sSpitzname, string sTitel, string sBirthday, Image contactPicture)
        {
            this.sSpitzname = sSpitzname;
            this.sTitel = sTitel;
            this.sBirthday = sBirthday;
            this.sContactPicture = ImageToString(contactPicture, contactPicture.RawFormat);
        }

Die ImageToString Methode und SetGeneralVars sitzen beide in der 2. Klasse.
Das Bild wird in der Hauptklasse geöffnet und in eine Variable gespecihert und dann über SetGeneralVars an die zweite Klasse übergeben.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.761

beantworten | zitieren | melden

Interessanter Code.... da scheint jemand (vermutlich ein Kollege von Dir..?) von einer anderen Programmiersprache zu kommen und versucht dortige Muster auf C# umzumünzen ;-)

Wo auch immer das Image Objekt verwaltet wird; wird wohl nicht richtig verwaltet.
Was für Dich eine "Hauptklasse" ist weiß ich nicht; im Kontext einer Software Architektur gibt es den Bezeichner "Hauptklasse" nicht ;-)
private Nachricht | Beiträge des Benutzers
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

beantworten | zitieren | melden

Zitat von Abt
Interessanter Code.... da scheint jemand (vermutlich ein Kollege von Dir..?) von einer anderen Programmiersprache zu kommen und versucht dortige Muster auf C# umzumünzen ;-)
)

Videotutorial Ich wollte den Code jetzt etwas anpassen aber komm hier nicht weiter
Zitat von Abt
Was für Dich eine "Hauptklasse" ist weiß ich nicht

Ich meinte damit nur die Klasse meiner Hauptuserform
Zitat von Abt
Wo auch immer das Image Objekt verwaltet wird; wird wohl nicht richtig verwaltet.

Hmm und was mach ich da jetzt? ?(
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.761

beantworten | zitieren | melden

Ich vermute (mit meinem geringen Grafikwissen), dass irgendwas mit dem Image (oder dem Stream dahinter) passiert, das wir hier nicht sehen.

Dadurch befindet sich der Image Stream nicht in dem Zustand, der für das Save notwendig ist -> Save knallt mit einem GDI Error.

Meine Wissen ist es daher auch Best Practise, dass ein Bild in Form eines Byte-Arrays oder in einer Datei gespeichert wird; und nicht in einem Image Objekt.
Work around IIRC: Image duplizieren und mit einem zweiten Objekt arbeiten.

Der Code funktioniert bei mir:

        private string EncodeImageBase64(Image img, ImageFormat imgFormat)
        {
            byte[] imageContents;


            using (MemoryStream memoryStream = new MemoryStream())
            using (Image tempImage = new Bitmap(img))
            {
                tempImage.Save(memoryStream, imgFormat);
                imageContents = memoryStream.ToArray();
            }

            return Convert.ToBase64String(imageContents);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = EncodeImageBase64(pictureBox1.Image, ImageFormat.Jpeg);
        }

Getestet mit einer Form mit einer PictureBox, das ein Bild hält, einem Button, der das Encoding ausführt und einer Textbox, der den Inhalt des Base64 Strings zeigt.

Wenn es bei Dir nicht klappt, dann wohl weil der Code noch irgendwas anderes mit dem Bild anstellt.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo theSoulT,
Zitat
hier der Code:
daran sieht man aber leider immer noch nicht, wo das Image herkommt, und auch nicht, wie es geladen wird. Das ist doch das entscheidende. Nicht der Save-Code ist wichtig, sondern dass das Image korrekt geladen wurde. Und korrekt bedeutet, dass der Stream, aus dem es geladen wurde, während der gesamten Lebensdauer des Images offen bleibt, also insbesondere auch beim Save noch offen ist.

Wenn es nicht möglich ist, den originalen Stream offen zu halten, muss man die Bildddaten in einen MemoryStream umkopieren, das Image-Objekt aus dem MemoryStream erzeugen und diesen während der gesamten Lebensdauer des Image-Objekts offen halten. Das sollte aber eigentlich schon aus "Allgemeiner Fehler in der GDI+" beim Speichern eines Bildes [==> weil der Stream geschlossen wurde] klar geworden sein.

herbivore
private Nachricht | Beiträge des Benutzers
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

beantworten | zitieren | melden

Zitat
daran sieht man aber leider immer noch nicht, wo das Image herkommt, und auch nicht, wie es geladen wird

Hier wird das Bild geladen:

        private void FrmOp_AcceptOpenFrame(string sFile)
        {
            tabControl1.Visible = true;
            btnPreview.Visible = true;
            lblChoice.Visible = false;
            btnSave.Visible = true;

            Kontakt k = new Kontakt();
            k = Kontakt.OpenContact(sFile,sKey,sIv);
            this.txtName.Text = k.sName;
            this.txtFirstName.Text = k.sFirstName;
            this.txtTitel.Text = k.sTitel;
            this.txtSpitzname.Text = k.sSpitzname;
            if (k.bIsFemale)
            {
                this.rbtnFemale.Checked = true;
            }
            else { this.rbtnMale.Checked = true; }
            this.iContactPicture = Kontakt.StringToImage(k.sContactPicture);
            this.pictureContact.Image = iContactPicture;

        public static Image StringToImage(string sImage)
        {
            MemoryStream MS = new MemoryStream(Convert.FromBase64String(sImage));
            Image img = Image.FromStream(MS);
            MS.Close();
            return img;
        }

Danke euch, hab den Fehler gefunden. Das MS.Close() ist hier nicht richtig. Das ist schuld an der Fehlermeldung :)
Vielen Dank für eure Mühen!
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.761

beantworten | zitieren | melden

Du solltest Dir nochmal die Grundlagen von C# und die Grundlagen von OOP anschauen; da sind ein paar Dinge im Argen, die Dir früher der später ziemlich auf die Füße fallen werden.
Im Endeffekt hast Du mit sauberem Code auch mehr Spaß an der Sache ;-)

- OpenContact-Methode würde man sauber über eine Factory Method machen können
- Geschlechter, sofern sie boolsch umgesetzt werden, sollte man hier mit Radio-Buttons lösen; nicht mit Checkboxen
- Die Methode StringToImage ist vom Name her falsch; und auch von der Position; im Endeffekt sauberer durch eine Erweiterungsmethode (auf den Kontakt) umzusetzen

Ansonsten ganz klar noch das Thema Data Binding und [Artikel] Drei-Schichten-Architektur als Hinweis.
Die Gefahr, dass man bei so einem Code sehr schnell die Übersichtlichkeit verliert, ist riesig.

Und ganz arg wichtig: lass den Käse mit dem Typ-Präfix in Deinen Eigenschaften/Variablen.
Keiner außer Du, weiß, was Du damit machen willst. Unternehmen haben auch schon versucht eigene Regeln zu erfinden; sind klaglos gescheitert.
Benenn Deine Variablen lieber richtig, sodass man am Namen erkennt, was es ist inhaltlich ist.
sContactImage => zB Base64EncodedContactImage

Dass es nen String ist, das erkenn ich auch am Typ - das ist aber nicht die Aufgabe eines Namens.
Daher erneuten Hinweis auf [Artikel] C#: Richtlinien für die Namensvergabe
private Nachricht | Beiträge des Benutzers