Laden...

DataBinding: Wann welche Source zum Binden verwenden? Wie Textboxen leeren?

Erstellt von tristar vor 7 Jahren Letzter Beitrag vor 7 Jahren 3.635 Views
T
tristar Themenstarter:in
98 Beiträge seit 2016
vor 7 Jahren
DataBinding: Wann welche Source zum Binden verwenden? Wie Textboxen leeren?

Guten Morgen 😃

Nachdem ich nun immer wieder von DataBinding lese (und inzwischen auch selbst Vorteile sehe), habe ich mich an einem kleinen Beispiel versucht.
Leider tauchen immer noch einige Fragen auf und bin auch teilweise verwirrt, was wann wie verwenden 😦
Ich hoffe, jemand von euch ist geduldig genug, mir dies so zu erklären, damit ich es endlich verstehe!

Zuerst, wann wird List<Klasse>, BindingList<Klasse>, IEnumerable<Klasse>, DataTable (zur Anzeige der Daten) verwendet? Im Prinzip kann ich ja alles als DataSource verwenden und binden.
Was ist "Standard"?

Für einen einzelnen Datensatz genügt es ja, das Klassenobjekt zurück zu geben bzw. zu binden?

Wann/wofür benötige ich INotifyPropertyChanged?

Nun noch mein Beispiel, Fragen habe kommentiert



public class Person
{
    public int Id { get; set; }
    public string Vorname { get; set; }
    public string Name { get; set; }
    public DateTime Geburtstag { get; set; }
}

public class DB_Abfrage
{
     public static List<Person> GetPersonen()
        {
            try
            {
                using (entities1 ctx = new entities1())
                {
                    var query = (from p in ctx.tblperson
                                orderby p.name
                                select new Person
								{
									Id = p.id,
									Vorname = p.vorname,
									Name = p.name,
									Geburtstag = p.geburtstag
								};

                    return query.ToList();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Fehler: GetPersonen");
                return null;
            }
        }
}






BindingSource bs = new BindingSource();
Person p = new Person();

private void Form1_Load(object sender, EventArgs e)
{
	bs.DataSource = typeof(Person);

	List<Person> list = DB_Abfrage.GetPersonen();
	foreach (Person p in list)
	{
		bs.Add(p);
    }

	dataGridView1.DataSource = bs;
	dataGridView1.AutoGenerateColumns = true; 
	
	tbVorname.DataBindings.Add("Text", bs, "Vorname");
	tbName.DataBindings.Add("Text", bs, "Name");
	tbGeburtstag.DataBindings.Add("Text", bs, "Geburtstag");
}


private void btnNeuePerson_Click(object sender, EventArgs e)
{
     // hier sollen nun die Textboxen geleert werden.
    // muss dazu das Binding gelöst werden?
    // wie?
}

private void btnSpeichern_Click(object sender, EventArgs e)
{
	p = new Person();
	p.Vorname = tbVorname.Text; 
	p.Name = tbName.Text;
	p.Geburtstag = tbGeburtstag.Text;
        
	
    bs.Add(p);
    bs.EndEdit();
	// neuer Datensatz wird zwar angezeigt, jedoch nicht gespeichert
	// muss ich dazu dann eine extra Insert Methode schreiben und aufrufen?
}


Ich danke Euch!

Hinweis von Coffeebean vor 7 Jahren

Bitte verwende immer eine Frage pro Thread. Mehrere Fragen führen zu einem absolut allgemeinen Titel mit dem niemand was anfangen kann. [Hinweis] Wie poste ich richtig? . Dazu bekommst du antworten, die in den Fragen springen und der Thread wird unübersichtlich. Ich habs mal versucht zu konkretisieren. Durch deine Vielzahl an Fragen ist es jedoch fast unmöglich. Wenn du was besseres hast, editiere es bitte selbst.

771 Beiträge seit 2009
vor 7 Jahren

Hi,

du mußt die DataBinding-Denkweise verstehen. Füge schon in der NeuePerson-Methode eine neue Person (so wie der Name ja auch lautet) der BindingSource hinzu - dann aktualisieren sich automatisch alle gebundenen Controls.
Für das Speichern mußt du dann die Daten wieder mit der DB synchronisieren (je nach DB-Zugriff per DataTableAdapter o.ä.).

2.207 Beiträge seit 2011
vor 7 Jahren

Hallo tristar,

auch wenn das ganze für WPF / MVVM ist, hat MrSparkle einen Artikel darüber veröffentlicht. Vielleicht wird das Prinzip damit klarer:

[Artikel] MVVM und DataBinding

Gruss

Coffeebean

3.003 Beiträge seit 2006
vor 7 Jahren

Zuerst, wann wird List<Klasse>, BindingList<Klasse>, IEnumerable<Klasse>, DataTable (zur Anzeige der Daten) verwendet? Im Prinzip kann ich ja alles als DataSource verwenden und binden.

Im Prinzip, ja, aber. Für die Erklärung vergiss mal kurz die Aufzählungen.

Controls, die ein DataBinding hinzugefügt bekommen, werden einmal mit dem Wert der Datenquelle initialisiert. Jedesmal, wenn sich die Datenquelle ändert, wird der Wert der gebundenen Eigenschaft des Controls neu gesetzt. Nun ist das keine Magie, ob und wie das gebundene Control merkt, dass sich die Datenquelle geändert hat, sondern dafür muss die Datenquelle ein Ereignis absetzen, das signalisiert, dass sich etwas verändert hat. Beim Setzen des Databindings abonniert das Control dieses Signal und reagiert fortan darauf.

Die Verantwortung dafür, dass die Datenquelle auch das Signal abfeuert, liegt beim Anwender. Und jetzt zurück zu den Listen: das Listenobjekt selbst, wenn es als Datenquelle gesetzt wird, kann dem Control mitteilen, dass es sich geändert hat. Wir reden von Änderungen der LIste, NICHT von Änderungen der Inhalte der Liste. Also: hinzufügen neuer Elemente, Entfernen von Elementen, so etwas. Eine List<Klasse>, genau wie IEnumerable<Klasse>, haben dafür keine Mechaniken. Man könnte sich also eine von LIst<Klasse> abgeleitete Liste erstellen, und dort die Add() und Remove()-Methoden (auch Clear() usw.) überschreiben, so dass sie ein Signal absetzen, wenn sich was ändert. Oder man verwendet die BindingList<Klasse>, die genau das macht. Wenn das Control also Änderungen der Liste abbilden soll: BindingList. Wenn das Control nur eine Liste von Inhalten ist, die einmal geladen werden und sich nicht ändern: List<>.

DataTable kann man als Datenquelle für DataGridViews verwenden. Dort ergeben sie auch Sinn - stell sie dir als aufgebohrte BindingList vor.

Wann/wofür benötige ich INotifyPropertyChanged?

INotifyPropertyChanged gibt den Rahmen für die oben erwähnte Signalisierung vor. Wenn das Control also auf Änderungen im Inhalt eines Objekts reagieren soll, MUSS dieses Objekt INPC implementieren und bei Änderungen das Ereignis "PropertyChanged" absetzen. Das ist genau das Signal, von dem oben die Rede war.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

T
tristar Themenstarter:in
98 Beiträge seit 2016
vor 7 Jahren

Ich raff es einfach nicht 😦 Langsam bin ich echt am Verzweifeln


       // ...


        public String Name
        {
            get { return _name}
            set
            {
                if (value == "")
                {
                    return;
                }

                _name= value;
                NotifyPropertyChanged("Name");
            }
        }


       // ....



        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }











       BindingSource bs = new BindingSource();
       BindingList<Person> bList = new BindingList<Person>();
       Person p = new Person();


        public F_Textbausteine()
        {
            InitializeComponent();

            bs.DataSource = typeof(Person);

            List<Person> list = DB_Abfrage.GetPersonen();
            bList = new BindingList<Person>(list);
            dataGridView1.AutoGenerateColumns = true;
            bs.DataSource = bList;

            dataGridView1.DataSource = bList;
            textBox1.DataBindings.Add("Text", bList, "Name", true, DataSourceUpdateMode.OnPropertyChanged);
        }

        private void btnNeuePerson_Click(object sender, EventArgs e)
        {
            p = new Person();
	    p.Name = textBox1.Text;
        }


 	private void textBox1_TextChanged(object sender, EventArgs e)
        {
           
        }

        private void btnSpeichern_Click(object sender, EventArgs e)
        {
	    bList.Add(p);
            bs.EndEdit();
        }
    }

Ich verstehe einfach nicht, wie ich die Textbox leer bekomme, um einen neuen Datensatz eintragen zu können?

Muss ich z.B. zum Speichern eines neuen Datensatzes ein INSERT schreiben, diesem das neue Objekt (p) übergeben. Und anschließend die Liste wieder neu Laden?

Und greif ich beim UPDATE einfach die Spalte ID ab, übergebe diese an die UPDATE Funktion, ändere die Daten und lade die Liste wieder neu?

Ich glaub ich seh den Wald vor lauter Bäumen nicht mehr ... 😦 Leider helfen mir die ganzen Beispiele die ich finde nicht weiter (oder ich kapiers einfach nicht).

T
tristar Themenstarter:in
98 Beiträge seit 2016
vor 7 Jahren

Jetzt habe ich gehofft, dass ich nun endlich die Lösung habe.


BindingSource bs = new BindingSource();
BindingList<Person> bList = new BindingList<Person>();
Person p = new Person();

public Form1()
{
            InitializeComponent();

            bs.DataSource = typeof(Person);

            List<Person> list = DB_Abfrage.GetPersonen();
            bList = new BindingList<Person>(list);
            dataGridView1.AutoGenerateColumns = true;
            bs.DataSource = bList;

            dataGridView1.DataSource = bList;
            textBox1.DataBindings.Add("Text", this.p, "Name", true, DataSourceUpdateMode.OnPropertyChanged);
}


private void btnNeuePerson_Click(object sender, EventArgs e)
{
     p.Name = "";
     p.Name = textBox1.Text;
}


// Aktuell ausgewählte Zeile als Objekt übergeben.
 private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex != -1)
            {
                p = (Person)dataGridView1.CurrentRow.DataBoundItem;             
            }
        }

Jetzt bleibt meine TextBox allerdings komplett leer, obwohl mein Objekt Daten enthält - weshalb?

F
10.010 Beiträge seit 2004
vor 7 Jahren

Das ist immer noch nicht wirklich das was du machen willst.

Wenn du schon eine BindingSource erstellst, dann benutze sie auch zum DataBinding.


BindingSource m_PersonBindingSource = new BindingSource();
BindingList<Person> m_PersonBindingList = new BindingList<Person>();

Person m_CurrentPerson 
{
	get{ return m_PersonBindingSource.Current as Person;}
	set{ m_PersonBindingSource.Current = value;}
}

public Form1()
{
	InitializeComponent();

    List<Person> list = DB_Abfrage.GetPersonen();
    m_PersonBindingList = new BindingList<Person>(list);
	dataGridView1.AutoGenerateColumns = true;
    m_PersonBindingSource.DataSource = bList;

	dataGridView1.DataSource = m_PersonBindingSource;
    textBox1.DataBindings.Add("Text", m_PersonBindingSource, "Name", true, DataSourceUpdateMode.OnPropertyChanged);
}

private void btnNeuePerson_Click(object sender, EventArgs e)
{
    var person = new Person();
    m_PersonBindingList.Add(person);
    m_CurrentPerson = person;		
}


T
tristar Themenstarter:in
98 Beiträge seit 2016
vor 7 Jahren

Danke für Deine Hilfe! Leider klappt es immer noch nicht.
In der Zeile des Setter für das Objekt erhalte ich die Fehlermeldung: > Fehlermeldung:

Einer Eigenschaft oder einem Indexer "System.Windows.Forms.BindingSource.Current" kann nichts zugewiesen werden -- sie sind schreibgeschützt

  
Person m_CurrentPerson  
{  
    get{ return m_PersonBindingSource.Current as Person;}  
    set{ m_PersonBindingSource.Current = value;}  
}  
  
  

Muss ich dazu an meiner Klasse selbst noch etwas ändern?

16.835 Beiträge seit 2008
vor 7 Jahren

Compiler/Syntax-Fehlermeldungen kannst Du wunderbar in der Dokumentation nachlesen.
[Hinweis] Syntaxfehler selbst lösen (Compilerfehlermeldungen)

Einer Eigenschaft, wie nur einen Getter und keinen Setter hat, kann man keinen Wert zuweisen.
Dein Current hat halt nur einen Getter.

In so einem Fall verwendet man dann eine Suchmaschine seiner Wahl mit zB dem Suchbegriff c# bindingsource set current item.
Dort wird man dann sehr wahrscheinlich auf die Eigenschaft Position verwiesen.

Hilfreich ist auch einfach mal die Dokumentation der Klasse anzuschauen, mit der man arbeitet.
Dort steht nämlich - bei der Eigenschaft Position - genau das was Du brauchst:

BindingSource.Position Property: Gets or sets the index of the current item in the underlying list. Changing the Position property will adjust the Current property likewise.

Mit ein wenig Doku-Lesen kommt man oft viel schneller ans Ziel 👍

5.299 Beiträge seit 2008
vor 7 Jahren

Hier ein Tut, was Databinding an typisierte Datasets schritt für schritt entwickelt (3 aufeinander aufbauende Artikel):
Data-Modelling for Beginners
Vlt. hopfst du als erstes zum Code-Download des letzten Artikels, und probierst das aus, um einen Eindruck der Möglichkeiten zu gewinnen - als Motivation, die Artikel wirklich durchzuackern.

Wie du offsichtlich erlebt hast, sind beim Selber-Basteln von Datenklassen eine erhebliche Menge an Fussfallen zu umschiffen, bevor man ühaupt ein erstes Ergebnis zu Gesicht bekommt.

typDataset kann einem da enorm (stupide) Arbeit abnehmen, sodass man sich besser aufs Wesentliche, aufs Datenmodell konzentrieren kann.

Hat man das Wesentliche verstanden und zum Laufen gebracht, steht einem ja immer noch offen, es selbst und quasi "handmade" nochmal zu versuchen - warum auch immer.

Der frühe Apfel fängt den Wurm.

W
872 Beiträge seit 2005
vor 7 Jahren

Ich würde gerade am Anfang immer PropertyChanged.Fody empfehlen, indem man das Nuget-Package einbindet.
Das umgeht einfach und elegant alle Probleme beim Implementieren von INotifyPropertyChanged.

T
tristar Themenstarter:in
98 Beiträge seit 2016
vor 7 Jahren

Super! Danke euch beiden noch für die Tipps!