Entwickler-Ecke

WinForms - Grafische Fehler bei Form.Close


Suras78 - Di 07.06.11 14:38
Titel: Grafische Fehler bei Form.Close
Hey,
ich mache einen kleinen Terminkalender. Hierzu wird beim Drücken auf einen Button ein neues Objekt einer "Termin"-Klasse erstellt, die in ihrem Konstruktor eine Form erstellt, in der man dann Name, Uhrzeit und Beschreibung eingeben kann, welche dann in den Fields des Objekts gespeichert werden. Das Speichern soll aber erst über einen "Speichern" Button geschehen, nicht im Konstruktor (geht ja auch kaum). Außerdem soll es dann einen Abbruch Button geben, der das Fenster einfach schließt und nichts weiter macht. Hier aber liegt das Problem: durch


C#-Quelltext
1:
newAppointment.Close()                    


wird die Form zwar geschlossen, aber weiterhin von Windows dargestellt, eben bis ich mit der Maus oder einem anderen Fester drüberfahre. Invalidate und dispose habe ich schon probiert... woran könnte das liegen?.

Danke


jaenicke - Di 07.06.11 14:58

Ich schätze einmal, dass das Fenster dahinter sich aus irgendeinem Grund schlicht nicht aktualisiert. :nixweiss:

Hast du vielleicht ein Testprojekt?


Suras78 - Di 07.06.11 15:36

Naja,
habs mit Firefox, dem Desktop und VS im Hintergrund probiert, denke nicht, dass es daran liegt. EinTestprojekt kann ich leider nicht schicken, da ich die dafür benutzen Grafiken nicht "veröffentlichen" will.


Suras78 - Mi 08.06.11 12:15

Gibt es denn einen Befehl, mit dem ich ganz Windows Invalidate()" sagen kann=


thepaine91 - Mi 08.06.11 13:12

http://msdn.microsoft.com/en-us/library/dd145002%28v=vs.85%29.aspx


Dr. Hallo - Mi 08.06.11 14:04

Zitat:
Gibt es denn einen Befehl, mit dem ich ganz Windows Invalidate()" sagen kann=

:D wow, das ist dann aber die brachial-methode. da gibt es mit sicherheit bessere möchlichkeiten.
da ich dein code nicht kenne kann ich aber nichts weiter dazu sagen. ich weiß nich obs usus ist ein formular
im konstruktor zu erstellen. im zweifel separates formular.

Moderiert von user profile iconChristian S.: C#- durch Quote-Tags ersetzt


Th69 - Mi 08.06.11 14:22

Hallo Suras78,

Sura78 hat folgendes geschrieben:

Hierzu wird beim Drücken auf einen Button ein neues Objekt einer "Termin"-Klasse erstellt, die in ihrem Konstruktor eine Form erstellt, ...

Dies hört sich nach einem unsauberen Design an. Trennung von GUI und Logik (Daten) ist oberste Prämisse.

Und zu
Sura78 hat folgendes geschrieben:

Gibt es denn einen Befehl, mit dem ich ganz Windows Invalidate()" sagen kann

Nicht an den Symptomen herumdoktorn, sondern die Ursachen suchen und behandeln ;-)

Am besten du zeigst mal deinen Code, wie du das Formular aufrufst.

Als generelle Leselektüre kann ich dir meinen Artikel Kommunikation von 2 Forms [http://www.bitel.net/dghm1164/programming/Kommunikation_von_2_Forms.html] empfehlen...


Suras78 - Mi 08.06.11 14:40


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
namespace Terminkalender {
    class Termin {
        public string name;
        public int hours;
        public int minutes;
        public string discription;
        public int importance;
        public int date;

        private string oldTextHours = "";
        private string oldTextMinutes = "";
        public Boolean alerted1 = false;
        public Boolean alerted2 = false;
        private TextBox timeM = new TextBox();
        private RichTextBox text = new RichTextBox();
        private TextBox timeH = new TextBox();
        private TextBox title = new TextBox();

        public Termin(int day) {
            date = day;
            name = "unknown";
            hours = 12;
            minutes = 00;
            discription = "Beschreibung";
            importance = 0;
            Form newAppointment = new Form();
            newAppointment.Text = "Neuer Termin";
            newAppointment.Size = new Size(250335);
            newAppointment.FormBorderStyle = FormBorderStyle.FixedToolWindow;
            newAppointment.MaximizeBox = false;
            newAppointment.BackgroundImage = Terminkalender.Properties.Resources.bg_bright;
            newAppointment.Deactivate += new EventHandler(turnTrans);
            newAppointment.Activated += new EventHandler(turnVis);

            Label captions = new Label();
            captions.Size = new Size(22530);
            captions.Location = new Point(5,33);
            captions.BackColor = Color.Transparent;
            captions.Text = "Titel:                                                 :         Uhr";
            newAppointment.Controls.Add(captions);

            Label header = new Label();
            header.Text = "Neuer Termin für den " + (day + 1) + ". Juni";
            header.Size = new Size(25030);
            header.Font = new Font(header.Font.FontFamily.Name, 12, FontStyle.Bold);
            header.BackColor = Color.Transparent;
            header.Location = new Point(11);
            header.BorderStyle = BorderStyle.None;
            newAppointment.Controls.Add(header);

            timeH.Location = new Point(15130);
            timeH.Size = new Size(255);
            timeH.MaxLength = 2;
            timeH.TextChanged += new EventHandler(hoursChanged);
            timeH.TextAlign = HorizontalAlignment.Right;
            newAppointment.Controls.Add(timeH);
            timeH.BringToFront();

            timeM.Location = new Point(18230);
            timeM.Size = new Size(255);
            timeM.MaxLength = 2;
            timeM.TextChanged += new EventHandler(minutesChanged);
            timeM.TextAlign = HorizontalAlignment.Right;
            newAppointment.Controls.Add(timeM);
            timeM.BringToFront();

            title.Location = new Point(3530);
            title.Size = new Size(10030);
            newAppointment.Controls.Add(title);
            title.Focus();
            title.KeyPress += new KeyPressEventHandler(titleChanged);
            title.BringToFront();
            title.MaxLength = 300;
            title.Focus();

            text.Text = "Beschreibung";
            text.Location = new Point(1060);
            text.Size = new Size(220200);
            timeM.TextChanged += new EventHandler(textChanged);
            newAppointment.Controls.Add(text);
            text.BringToFront();

            Label alertCaption = new Label();
            alertCaption.Text = "Benachrichtigen:";
            alertCaption.Location = new Point(20280);
            newAppointment.Controls.Add(alertCaption);



            PictureBox save = new PictureBox();
            save.BackgroundImage = Terminkalender.Properties.Resources.yesbg;
            save.Size = new Size(5050);
            save.BackgroundImageLayout = ImageLayout.Stretch;
            save.Location = new Point(120260);
            save.MouseEnter += delegate(System.Object o, System.EventArgs e) {
                (o as PictureBox).BackgroundImage = Terminkalender.Properties.Resources.yebgact;
            };
            save.MouseLeave += delegate(System.Object o, System.EventArgs e) {
                (o as PictureBox).BackgroundImage = Terminkalender.Properties.Resources.yesbg;
            };
            save.Click += delegate(System.Object o, System.EventArgs e) {
                if (title.Text == "" || timeH.Text == "" || timeM.Text == "") {
                    MessageBox.Show("Bitte füllen Sie die Felder für Titel und Uhrzeit aus.");
                }
                else {
                    name = title.Text;
                    discription = text.Text;
                    importance = 1;
                    date = day;
                    hours = Convert.ToInt32(timeH.Text);
                    minutes = Convert.ToInt32(timeM.Text);

                    Terminkalender.Form1.appointments.Add(this);
                    Terminkalender.Form1.load();
                    newAppointment.Close();
                }
            };
            newAppointment.Controls.Add(save);

            PictureBox abort = new PictureBox();
            abort.BackgroundImage = Terminkalender.Properties.Resources.nobg;
            abort.Size = new Size(5050);
            abort.MouseEnter += delegate(System.Object o, System.EventArgs e) {
                (o as PictureBox).BackgroundImage = Terminkalender.Properties.Resources.nobgact;
            };
            abort.MouseLeave += delegate(System.Object o, System.EventArgs e) {
                (o as PictureBox).BackgroundImage = Terminkalender.Properties.Resources.nobg;
            };
            abort.BackgroundImageLayout = ImageLayout.Stretch;
            abort.Location = new Point(180260);
            abort.Click += new EventHandler(closeNewAppointment);

            newAppointment.Controls.Add(abort);
            
            newAppointment.Show();

            title.Focus();
        }


Der Konstruktor ;)


C#-Quelltext
1:
2:
3:
4:
        private void closeNewAppointment(object sender, EventArgs e) {
            Form oldForm = (sender as PictureBox).FindForm();
            oldForm.Close();
        }

die Methode (innerhalb der gleichen Klasse), die die Form schließen soll. Bei "save" passsiert das Ganze, wie man sehen kann, direkt in einer anonymen Methode, eben zum ausprobieren. Macht aber leider keinen Unterschied.


Suras78 - Mi 08.06.11 16:23

Nebenbei:
Welches "System" würdet ihr mir für das Speichern empfehlen? Außer dem Grafikbug ist es jetzt fast fertig. Allerdings halten die Termine nur bis zum beenden des Programms. Als nächstes möchte ich die Termine also speichern können, was ich leider noch nie gemacht hab. Deswegen meine Frage: Wie speicher ich sie am besten? Hab mal ein wenig geschaut, aber es gibt ja viele veschiedene Möglichkeiten... Textdateien, Ini, Reistry, DB... aber was ist für meinen Kalender (und für einen Anfänger) am besten? Falls das wichtig ist: Ich möchte aus der Datei neue Objekte meiner Termin klasse erzeugen, die einen naderen Konstruktor hat, also kein Form erstellt sondern die Werte (6 Variablen) eben aus dem Speicher kriegt.


Dr. Hallo - Do 09.06.11 01:33

Das da..


C#-Quelltext
1:
Form newAppointment = new Form();                    


nicht innerhalb sonder außerhalb des konstruktors definieren. dann könnte es klappen.
so wie dus da hast existiert deine form nur konstruktorweit. :


norman2306 - Do 09.06.11 07:02

Für das Speichern würde ich dir einen Serializer vorschlagen. Da bietet dir .NET gleich zwei an:

1. eine XmlSerializer - der produziert lesbaren XML-Code

2. ein BinSerializer - der produziert schlecht lesbare Binär-Files

Hier mal ein Beispiel:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
   using System.IO;
   using System.Runtime.Serialization.Formatters.Binary;

   [Serializable]
   public class ItemToSave
   {
        public DateTime Date { get; set;}
        public string FirstName { get; set;}
        public string LastName { get; set;}
        public string Description { get; set;}
   }

   public class MainClass
   {
        .
        .
        .

        public MainClass()
        {
            var item = new ItemToSave() 
            { 
              Date = DateTime.Now, 
              FirstName = "Norman-Hendrik"
              LastName = "Schulz",
              Description = "Danke sagen"
            } 

            SaveItem("c:\\myFile.abc", item);

            var item2 = OpenItem("c:\\myFile.abc"as ItemToSave;

            MessageBox.Show(item2.Date.ToString(), string.Format("Nicht vergessen: {0} {1} {3}!", item2.FirstName, item2.LastName, item2.Description));
        }


        private void SaveItem(string path, object item)
        {
            using (var fileStream = new FileStream(path, FileMode.Create))
            {
                 var bf = new BinaryFormatter();
                 bf.Serialize(fileStream, item);
            }
        }


        private object OpenItem(string path)
        {
            using (var fileStream = new FileStream(path, FileMode.Create))
            {
                 var bf = new BinaryFormatter();
                 return bf.Deserialize(fileStream);
            }
        }
   }


Suras78 - Do 09.06.11 08:38

@ Dr. Hallo: Danke, hat aber leider nichts geändert.
@ Norman2306: Danke, werd ich jetzt probieren (habs gerade mit einem DataSet versucht, das scheint aber eher ungeeignet zu sein)


norman2306 - Do 09.06.11 09:16

Dafür ist es auch ungeeignet. Die zu speichernde Klasse muss mit dem Attribut [Serializable] gekennzeichnet sein. Das trifft für ein DataSet nicht zu. Um ein DataSet zu serialisieren, müsste man einen serialisierbaren Wrapper basteln. Das ist zwar nicht sonderlich schwer, aber unnötig. Ein DataSet hat eine eigene Speicherfunktion, dafür muss man keine schreiben.

Ein DataSet kann man so schreiben:


C#-Quelltext
1:
2:
3:
4:
using(var filestream = new FileStream(path, FileMode.Create)
{
   myDataSet.WriteXML(filestream);   
}


Th69 - Do 09.06.11 09:36

Hallo Suras78,

dein Code entspricht leider genau meinen Befürchtungen.
Du solltest deinen Code dringend so ändern, daß du einzelne Klassen für die GUI (Forms) und Logik (z.B. Termine) erstellst.

So eigenartige Konstrukte wie

C#-Quelltext
1:
2:
3:
Terminkalender.Form1.appointments.Add(this);
Terminkalender.Form1.load();
newAppointment.Close();

können nur zu komischem Verhalten führen (hast du etwa 'appointments' sowie die 'load'-Methode als 'static' deklariert???).

Mein Beispielprojekt 'PersonManagement' (ganz unten bei meinem Artikel) zeigt, wie man ein Projekt richtig im Sinne der OOP aufsetzt.

Ich weiß, daß es für einen Anfänger schwierig ist, gleich ein "perfektes" Programm zu erstellen, aber wenn schon der Grundaufbau des Projektes falsch ist, dann führen alle weiteren Änderungen in die falsche Richtung (und es treten Probleme auf - wie bei dir -, die es sonst nicht gibt ;-).


Suras78 - Do 09.06.11 10:05

Danke, Th69,
ich werde das in Zukunft berücksichtigen, allerdigns funktioniert mein Aufbau sonst sehr gut und ist auch halbwegs durchdacht ;) Deswegen möchte ich das ganz zu ändern nur als letzte Möglichkeit in Betracht ziehen.

Nochmal zu XML: ich erzeuge mit dem Code

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
        private void saveData(object o, EventArgs e) {
            XmlTextWriter myXmlTextWriter = new XmlTextWriter("appointments.xml", System.Text.Encoding.UTF8);
            myXmlTextWriter.Formatting = Formatting.Indented;
            myXmlTextWriter.WriteStartDocument(false);

            for (int i = 0; i < appointments.Count; i++) {
                Termin currentAppointment = (Termin)appointments[i];
                myXmlTextWriter.WriteStartElement("appointment");
                    myXmlTextWriter.WriteElementString("name", currentAppointment.name);
                    myXmlTextWriter.WriteElementString("hours", Convert.ToString(currentAppointment.hours));
                    myXmlTextWriter.WriteElementString("minutes", Convert.ToString(currentAppointment.hours));
                    myXmlTextWriter.WriteElementString("description", currentAppointment.description);
                    myXmlTextWriter.WriteElementString("importance",  Convert.ToString(currentAppointment.importance));
                    myXmlTextWriter.WriteElementString("date",  Convert.ToString(currentAppointment.date));
                myXmlTextWriter.WriteEndElement();
            }

            myXmlTextWriter.Flush();
            myXmlTextWriter.Close(); 
        }


Diese XML Datei:


XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
  <?xml version="1.0" encoding="utf-8" standalone="no" ?> 
- <appointment>
  <name>Test</name
  <hours>13</hours
  <minutes>13</minutes
  <description>sdaf asdasf dfsd asdf fasd fasdf asdf f sdf asddfasfasd asdf asdf asfas dfasdfasdfasdfasd</description
  <importance>1</importance
  <date>0</date
  </appointment>


Kann ich das i dieser Form gebrauchen? Wenn ja, wie lese ich es dnn korrekt aus?

Danke nochmal

EDIT: Danke, habs schon =) Konnte mir nur nicht richtig vorstellen, wie ich die einzelnen untergruppen von jedem Appointment ansprechen soll und mir dieses appointment aus den vieles erstmal heraussuchen soll... Aber scheinbar kann er das mit foreach selbst ;)