none
Übergabe Eigenschaft funktioniert nicht RRS feed

  • Frage

  • Hallo,

    erstmal kurz zu mir. Ich lerne gerade C# und bin noch ganz am Anfang. Um Praxisnah zu lernen, programmiere ich mir eine Kontaktverwaltung. Die 100-ste ;).

    Die SQL Datenbank steht, ich habe mir einige  Forms angelegt. Ich kann Datensätze anlegen, DGV füllen diese filtern.

    Jetzt will den gefilterten Datensatz auslesen ind eine Maske schreiben, editieren und in die Datenbank ändern.

    Das Auswählen (Doppel Klick RowHeader) funktioniert. Die KontaktID wird ausgelesen und nun kommt das Problem. Die Übergabe der KontaktID ins Bearbeitungsform funktioniert nicht. folgender Code:

    public partial class frmHaupfenster : Form
     {

          private string id = "";
          public string ID
          {
          get { return id; }

           }

    ...

    private void dataGridView2_RowHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
      {
       if (e.RowIndex >=0)
       {

        DataGridViewRow row = this.dataGridViewUnternehmen.Rows[e.RowIndex];

        id = row.Cells["firmen_id"].Value.ToString();

       ...

      id bekommt nach Doppelklick den richtigen Wert aus dem Feld "firmen_id" zugewiesen. Der steht dann auch oben in ID, soweit hoffentlich richtig.

    Dann wird das Form fürs Bearbeiten aufgerufen und das Objekt hauptfenster erzeugt.

    frmHaupfenster hauptfenster = new frmHaupfenster();
       string Var = hauptfenster.ID;

    Hier kommt jetzt das Problem. Eigentlich sollte Var der Wert von hauftfenster.ID also die FirmenID zugewiesen werden. Es wird aber ein "" zugewiesen. :(

    Wo ist mein (Denk)Fehler?

    Über einen Tipp oder Lösung würde ich mich freuen.

    Gruß

    Jürgen

          

    Freitag, 6. Mai 2016 09:20

Antworten

  • Hallo Jürgen,

    in deinem 3. Codeblock erzeugst du eine neue Instanz von frmHauptfenster und das ist das Problem da du eigentlich die schon existierende Instanz bräuchtest. Nun ist die Frage wie weit du dich schon mit OOP auskennst. Und zwar kannst du von einer Klasse mehrere Instanzen anlegen. Wenn du das machst, dann kannst du jeder Instanz andere Eigenschaftswerte zuweisen. Besser lässt sich das mit einer Klasse Person erklären. Eine Instanz könnte dabei den Namen Jürgen und eine andere Instanz den Namen Tom erhalten. Wenn du nun einen Namen auslesen willst, brauchst du die passende Instanz.

    Das beste ist es beim Aufrufen von frmBearbFirma die ID als Parameter an den Konstruktor weiter zu geben oder eine Eigenschaft dafür einzusetzen. Nachfolgend mal mit einer Überladung des Konstruktors gezeigt.

    Hauptform:

    frmBearbFirma bearbFirma = new frmBearbFirma(this.ID); //Übergabe des Parameters
    if(bearbFirma.ShowDialog()==DialogResult.OK) //usw.

    Bearbeitenform:

    public frmBearbFirma(string id){
      this._id=id;//Wert des Parameters in der Klasseninstanz speichern
      InitializeComponent();//Dient zum Aufbauen der UI
    }
    
    string _id;
    

    SQL-Abfrage:

    string sqlString = @"SELECT firmen_id, namen_u, strasse_u, plz_u, ort_u, telefon1_u, fax1_u, email1_u, branche, webadd, country, bundesland, unterngroesse," +
    "klassifizierung, status_u, betreuer, bemerkung, usteuer_id, steuer_nr, handelsreg FROM dbo.unternehmen WHERE firmen_id = @firmenID";
    
    SqlCommand cmd = new SqlCommand(sqlString,connection);
    cmd.Parameters.Add("@firmenID", SqlDbType.Int);
    
    //Benutzt du falsch ;)
    //cmd.Parameters["@firmenID"].Value = int.TryParse(hauptfenster.ID, out firmenID );
    
    //So sollte es aussehen:
    int id;
    if(int.TryParse(this._id, out id)){
      //parsen erfolgreich
      cmd.Parameters["@firmenID"].Value = id;((id enthält den Wertw enn das parsen geklappt hat
    }
    else{
      //Fehler beim parsen
    }

    BTW: Wenn du in der SQL-Abfrage sowieso einen Integer aus dem String machst, dann solltest du auch gleich überall einen Integer anstelle eines Strings nutzen. Das macht den Code einfacher zu lesen und macht es einfacher Fehler zu finden bzw. Eingaben zu validieren.

    Noch ein anderer kleiner Tipp: Auch wenn es am Anfang schwer fällt (ich rede aus Erfahrung), so macht es sich doch meistens besser sich an die Benennungskonventionen von C# zu halten. Siehe hierfür:

    Es gibt hin- und wieder Abweichungen, aber im großen und ganzen erleichtert es uns deinen Code zu lesen und du kannst leichter den Code von anderen lesen. Englische Bezeichnungen sind dabei fast schon selbstverständlich, wenn auch nicht immer notwendig. Trotzdem programmiere ich mittlerweile fast nur noch englisch, selbst wenn die UI nur auf Deutsch ist.


    Tom Lambert - .NET (C#) MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Freitag, 6. Mai 2016 20:53
    Moderator

Alle Antworten

  • Hi und willkommen ;)

    zunächst solltest du Code-Zeilen mit dem entsprechenden Tag markieren und deine Programmiersprache auswählen, dass erleichtert ungemein das lesen ;) (siehe folgende Grafik)

    Ich denke, dass du in deiner Datenbank firmen_id entweder als Integer oder als Guid festgelegt hast. Andernfalls würde das erstmal keinen Sinn ergeben. Diesen Datentyp wird die Datenbank auch so zurückgeben, allerdings als Object, sodass du erst einmal mit folgenden Code einen Cast (Typkonvertierung) durchführen musst:

    id = (row.Cells["firmen_id"].Value as Int32).ToString();

    /* ######################################## */ id = (row.Cells["firmen_id"].Value as Guid).ToString();

    Bei diesen beiden Code-Zeilen musst du natürlich schauen welchen Datentyp du für firmen_id in der Datenbank festgelegt hast (Guid ist in MS-SQL als uniqueidentifier zu finden). Entsprechend musst du dann noch deine ID-Eigenschaft anpassen:

    private Int32 id = -1;
    public Int32 ID
    {
        get { return id; }
    }
    
    /* ######################################## */
    
    private Guid id = Guid.Empty;
    public Guid ID
    {
        get { return id; }
    }
    
    Ich hoffe ich konte dir damit helfen ;)


    Viele Grüße
    Lars Krämer - Software Entwickler für Web und Microsoft-Technologien


    lkraemer.de software solutions

    Freitag, 6. Mai 2016 09:44
  • Hallo Jürgen,

    wenn Du das Hauptfenster erzeugst ist ja noch kein Doppelklick ausgeführt worden. Daher ist id noch auf dem default Wert "" und die Abfrage der Property ID gibt natürlich damit "" zurück.

    Die Zuweisung von hauptfenster.ID nach Var ist eine einmalige Sache. Var bekommt dann den Wert von ID (der direkt nach der Erzeugung "" ist - wie oben erläutert). Wenn sich ID ändert, dann ändert sich Var nicht mehr mit. Du musst also hier einen anderen Weg finden.

    Eine Möglichkeit, die hier sehr gerne genommen wird, ist die Nutzung von Events. Deinem Hauptfenster kannst Du ein Event geben. Von mir aus "RowSelected". Dieses RowSelected Event kann dann das Hauptfenster auslösen.

    Da das Event ja eine Id mitbekommen soll, benötigen wir eine Klasse, die von EventArgs abgeleitet ist und die eine ID enthalten kann.

    Der Code dazu könnte dann wie folgt aussehen (Bitte in zwei Dateien - einmal IdChoosenEventArgs und einmal gehört es in deine Klasse):

        public class IdChoosenEventArgs : EventArgs
        {
            /// <summary>
            /// The choosen Id.
            /// </summary>
            public object Id { get; set; }
    
            /// <summary>
            /// Creates a new instance of <see cref="IdChoosenEventArgs"/>
            /// </summary>
            public IdChoosenEventArgs()
            { }
    
            /// <summary>
            /// Creates a new instance of <see cref="IdChoosenEventArgs"/>
            /// </summary>
            /// <param name="id">Id that was choosen.</param>
            public IdChoosenEventArgs(object id)
            {
                Id = id;
            }
        }
    
    
    
    // In Deinem Hauptfenster brauchst Du nun:
    
            /// <summary>
            /// Event that is triggered when an Id was choosen.
            /// </summary>
            public event EventHandler<IdChoosenEventArgs> IdChoosen;
    
            /// <summary>
            /// An id was choosen.
            /// </summary>
            /// <param name="sender">Sender of the event.</param>
            /// <param name="args">Argument with choosen id.</param>
            /// <remarks>
            /// This implementation invokes the event. If you derive this class and override this method
            /// then please do not forget to call the base class implementation!
            /// </remarks>
            protected virtual void OnIdChoosen(object sender, IdChoosenEventArgs args)
            {
                // handle racing conditions
                var handle = IdChoosen;
                handle?.Invoke(sender, args);
            }
    
    
    // Und im Code, bei dem die Id gewählt wird, brauchst Du dann den Aufruf:
    OnIdChoosen(this, new IdChoosenEventArg(id));
    
    // Und beim Code, der das Hauptfenster ausführt musst Du dann natürlich auf das Event reagieren. Also auch eine Funktion a.la. OnIdChoosen und dann ein
    
    hauptfenster.IdChoosen += OnIdChoosen; 
    

    Das wäre dann eine Lösung, bei dem das Hauptfenster so angezeigt wird. Wenn das Hauptfenster aber als Dialog angezeigt wird (also die Ausführung an der stelle stoppt), dann brauchst Du das nicht. Dann hättest Du nur ein:

    frmHaupfenster hauptfenster = new frmHaupfenster();
    hauptfenster.ShowDialog();
    string Var = hauptfenster.ID;

    Ich hoffe, das hat etwas geholfen.

    Mit den besten Grüßen,

    Konrad Neitzel

    Freitag, 6. Mai 2016 09:53
  • Vielen Dank für den Hinweis.

    Ja, die FirmenID ist als Integer festgelegt.

    Die Typkonvertierung scheitert aber mit folgender Fehlermeldung.

    "Der as-Operator muss mit einem Referenztyp oder einem Typ, der NULL-Werte zulässt, verwendet werden. ("int" ist ein Werttyp, der keine NULL-Werte zulässt."

    Ich wollte die Konvertierung dann erst später vornehmen. Also die ID die ich aus der SQL bekommen habe als String übergeben und dann wie nachfolgend in Int konvertieren.

    cmd.Parameters.Add("@firmenID", SqlDbType.Int);

    cmd.Parameters["@firmenID"].Value = int.TryParse(hauptfenster.ID, out firmenID );

    Freitag, 6. Mai 2016 10:22
  • Hallo Konrad,

    ich weiß nicht ob es das ist was mir hilft. Wahrscheinlich hab ich es aber nur nicht wirklich verstanden.

    Ich beschreib das Problem noch mal. Vielleicht hilf das ja auch.

    ich habe zwei Forms. Form1 ist  frmHauptfenster, form2 ist frmBearbFirma.

    In Form1 ist eine DGV eingebunden die die Firmendatensätze zeigt. Bei Doppelklick auf eine Zeile  (siehe unten), wird die Firmen_ID ausgelesen und an als String id zugewiesen. (Codeblock2)

    id und auch ID haben am Anfang den Wert "".  Das ist korrekt. (codeblock 1)

    Nach Auslesen bekommen sie aber den Wert der FirmenID von mir aus 1007. Da ein String "1007". Das habe ich überprüft.

    Nach Zuweisung des Wertes der FirmenID öffnet sich Form2. (Codebock 3)

    Block1

    private string id = "";
           public string ID
           {
           get { return id; }
           }


    Block2

    

    public void dataGridView2_RowHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e) { if (e.RowIndex >= 0) { DataGridViewRow row = this.dataGridViewUnternehmen.Rows[e.RowIndex]; id = row.Cells["firmen_id"].Value.ToString();

    frmBearbFirma bearbFirma = new frmBearbFirma();
    if (bearbFirma.ShowDialog() == System.Windows.Forms.DialogResult.OK)

    Wenn nun das Form2 aufgerufen wird soll als erstes die ID aus dem Hauptfenster übernommen werden, der Variable Var zugewiesen werden und fünf Zeilen weiter unten in eine Integer umgewandelt werden.

    Danach dann mit DataReader das Auslesen des Datensatzes und Zuweisung der Felder an TextBoxen.

    int firmenID;
    frmHaupfenster hauptfenster = new frmHaupfenster();
    string Var = hauptfenster.ID;
    string sqlString = @"SELECT firmen_id, namen_u, strasse_u, plz_u, ort_u, telefon1_u, fax1_u, email1_u, branche, webadd, country, bundesland, unterngroesse," +
    "klassifizierung, status_u, betreuer, bemerkung, usteuer_id, steuer_nr, handelsreg FROM dbo.unternehmen WHERE firmen_id = @firmenID";
    
    SqlCommand cmd = new SqlCommand(sqlString,connection);
    cmd.Parameters.Add("@firmenID", SqlDbType.Int);
    cmd.Parameters["@firmenID"].Value = int.TryParse(hauptfenster.ID, out firmenID );

    Das Problem ist wiegesagt, das hauptfenster.ID ein "" enthält. Obwohl doch die Variable im Form1 die "1007" zugewiesen bekommen hat. (Ich habe es überprüft, die "1007" ist wirklich zugewiesen) Eigentlich dachte ich, das ich mit hauptfenster.ID den letzten Zustand abgreife. Also nicht als ID am Anfang noch "" hatte sondern nach Zuweisung von "1007". Andersrum (Erstellung eine Datensatzes)  funktioniert es mit der Methode. Ich weiß nicht warum in die Richtung nicht.

    Gruß

    Jürgen


    • Bearbeitet JKeitzl Freitag, 6. Mai 2016 19:31 etwas vergessen
    Freitag, 6. Mai 2016 19:27
  • Hallo Jürgen,

    in deinem 3. Codeblock erzeugst du eine neue Instanz von frmHauptfenster und das ist das Problem da du eigentlich die schon existierende Instanz bräuchtest. Nun ist die Frage wie weit du dich schon mit OOP auskennst. Und zwar kannst du von einer Klasse mehrere Instanzen anlegen. Wenn du das machst, dann kannst du jeder Instanz andere Eigenschaftswerte zuweisen. Besser lässt sich das mit einer Klasse Person erklären. Eine Instanz könnte dabei den Namen Jürgen und eine andere Instanz den Namen Tom erhalten. Wenn du nun einen Namen auslesen willst, brauchst du die passende Instanz.

    Das beste ist es beim Aufrufen von frmBearbFirma die ID als Parameter an den Konstruktor weiter zu geben oder eine Eigenschaft dafür einzusetzen. Nachfolgend mal mit einer Überladung des Konstruktors gezeigt.

    Hauptform:

    frmBearbFirma bearbFirma = new frmBearbFirma(this.ID); //Übergabe des Parameters
    if(bearbFirma.ShowDialog()==DialogResult.OK) //usw.

    Bearbeitenform:

    public frmBearbFirma(string id){
      this._id=id;//Wert des Parameters in der Klasseninstanz speichern
      InitializeComponent();//Dient zum Aufbauen der UI
    }
    
    string _id;
    

    SQL-Abfrage:

    string sqlString = @"SELECT firmen_id, namen_u, strasse_u, plz_u, ort_u, telefon1_u, fax1_u, email1_u, branche, webadd, country, bundesland, unterngroesse," +
    "klassifizierung, status_u, betreuer, bemerkung, usteuer_id, steuer_nr, handelsreg FROM dbo.unternehmen WHERE firmen_id = @firmenID";
    
    SqlCommand cmd = new SqlCommand(sqlString,connection);
    cmd.Parameters.Add("@firmenID", SqlDbType.Int);
    
    //Benutzt du falsch ;)
    //cmd.Parameters["@firmenID"].Value = int.TryParse(hauptfenster.ID, out firmenID );
    
    //So sollte es aussehen:
    int id;
    if(int.TryParse(this._id, out id)){
      //parsen erfolgreich
      cmd.Parameters["@firmenID"].Value = id;((id enthält den Wertw enn das parsen geklappt hat
    }
    else{
      //Fehler beim parsen
    }

    BTW: Wenn du in der SQL-Abfrage sowieso einen Integer aus dem String machst, dann solltest du auch gleich überall einen Integer anstelle eines Strings nutzen. Das macht den Code einfacher zu lesen und macht es einfacher Fehler zu finden bzw. Eingaben zu validieren.

    Noch ein anderer kleiner Tipp: Auch wenn es am Anfang schwer fällt (ich rede aus Erfahrung), so macht es sich doch meistens besser sich an die Benennungskonventionen von C# zu halten. Siehe hierfür:

    Es gibt hin- und wieder Abweichungen, aber im großen und ganzen erleichtert es uns deinen Code zu lesen und du kannst leichter den Code von anderen lesen. Englische Bezeichnungen sind dabei fast schon selbstverständlich, wenn auch nicht immer notwendig. Trotzdem programmiere ich mittlerweile fast nur noch englisch, selbst wenn die UI nur auf Deutsch ist.


    Tom Lambert - .NET (C#) MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Freitag, 6. Mai 2016 20:53
    Moderator
  • Hallo Tom,

    vielen Dank für Dir Hilfe. Die Wertübergabe klappt so. Ich werde mich bemühen entsprechend den Konventionen zu arbeiten. Stimmt schon, ich selbst kann meinen Schrott interpretieren, andere dann eher nicht.

    Also, vielen Dank

    Jürgen

    Samstag, 7. Mai 2016 19:55