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.
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 😉
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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.
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 😉
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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 🙁
Was für Dich eine "Hauptklasse" ist weiß ich nicht
Ich meinte damit nur die Klasse meiner Hauptuserform 😉
Wo auch immer das Image Objekt verwaltet wird; wird wohl nicht richtig verwaltet.
Hmm und was mach ich da jetzt? ?(
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.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo theSoulT,
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
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!
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 😉
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
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code