Laden...

Wie löscht die BindingSouce Listenelemente?

Erstellt von mec vor 9 Jahren Letzter Beitrag vor 9 Jahren 3.552 Views
M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren
Wie löscht die BindingSouce Listenelemente?

verwendetes Datenbanksystem: keines - nur DataSet

Da ich in meinem vorangegangenen Post "Selbstdefinierte Properties in typisierter DataRow können nicht gebunden werden" leider zu keiner Lösung gekommen bin, habe ich einen anderen Weg probiert, indem ich meine DataRows in einer eigenen generischen Liste selbst verwalte, die ich dann der BindingSource als DataSource übergebe. Das setzt jedoch voraus, dass ich besonders die Löschmethoden meiner Liste gesondert behandele.

Nun hat sich aber gezeigt, dass ich die Remove-Methoden meiner Liste nicht abfangen kann, weil sie von der BindingSource gar nicht aufgerufen werden:

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Test
{
    static class Program
    {
        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FormTest());
        }
    }

    public class FormTest : Form
    {
        public FormTest()
        {
            BindingSource bindingSource = new BindingSource();
            bindingSource.DataSource = new ItemList();

            BindingNavigator bindingNavigator = new BindingNavigator(bindingSource);

            DataGridView dataGridView = new DataGridView();
            dataGridView.DataSource = bindingSource;
            dataGridView.Dock = DockStyle.Fill;

            this.Controls.AddRange(new Control[] { bindingNavigator, dataGridView });
        }
    }


    class Item
    {
        public int Id { get; private set; }

        public string Name { get; private set; }


        public Item(int id, string name)
        {
            this.Id = id;
            this.Name = name;
        }
    }


    class ItemList : List<Item>
    {
        public ItemList() : base()
        {
            for (int i = 1; i <= 15; i++)
                this.Add(new Item(i, string.Format("Item {0}", i)));
        }


        new public bool Remove(Item item)
        {
            return base.Remove(item);
        }


        new public void RemoveAt(int index)
        {
            base.RemoveAt(index);
        }
    }


}

Setze ich nun Haltepunkte in meinen redefinierten Remove-Methoden, so werden diese gar nicht angelaufen, wenn ich im DatgridView oder mit dem Remove-Button auf dem BindingNavigator Datensätze lösche. Dennoch wir meine Liste entsprechend verändert.

Aber welcher Methoden bedient sich die BindingSource zum Löschen eigentlich?

Der gleiche Effekt tritt übrigens auch ein, wenn ich meine Liste als komplett eigene IList<T>-Klasse implementiere.

Gelöschter Account
vor 9 Jahren

Das ausblenden durch new ist in dem Fall völlig sinnfrei da in solchen Szenarien vom DataBinding Provider lediglich immer nur ein Interface genutzt wird. In dem Fall wahrscheinlich IList oder so. Die Implementierung dahinter also deine Listeninstanz ist ihm damit richtiger Weise egal und daher greift dein new nicht. (Ich glaube auf MyCSharp gibt es im Augenblick leider kein Tutorial das den Unterschied zwischen new und virtual/override verdeutlicht, wenn nicht möge man mich bitte gerne korrigieren)

Spontan würde ich zur Nutzung einer Sytem.ComponentModel. BindingList<T> raten. Diese bietet sowohl Events als auch virtuelle Methoden an um die Kontrolle über die eigene Liste besser zu steuern.

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

Hallo Sebastian,

etwas in der Art habe ich auch schon vermutet und deshalb eine eigene IList-Klasse implementiert. Aber ganz verstehe ich es dennoch nicht. Die Eigenschaft List der BindingSource ist doch eine IList-Schnittstelle und sollte sich dann doch auch deren Remove-Methoden bedienen. Aber ich werde das mit der Implementierung von BindingList<T> auf alle Fälle probieren.

Der Vollständigkeit halber aber hier nochmals meine Implementation von IList. Hier findet keine Redefinition der Remove-Methoden statt.
Kann es auch sein, dass die BindingSource gar nicht mit dem Objekt selbst sondern mit dessen Enumerator arbeitet?

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Test
{
    class RowList<T> : IList<T>
    {
        private List<T> list = new List<T>();




        public void Add(T item)
        {
            this.list.Add(item);
        }


        public bool Contains(T item)
        {
            return this.list.Contains(item);
        }


        public void Clear()
        {
            this.list.Clear();
        }


        public void CopyTo(T[] array, int arrayIndex)
        {
            this.list.CopyTo(array, arrayIndex);
        }


        public int Count
        {
            get { return this.list.Count; }
        }


        public bool IsReadOnly
        {
            get { return false; }
        }


        public bool Remove(T item)
        {
            return this.list.Remove(item);
        }


        public void RemoveAt(int index)
        {
            this.list.RemoveAt(index);
        }


        public int IndexOf(T item)
        {
            return this.list.IndexOf(item);
        }


        public void Insert(int index, T item)
        {
            this.list.Insert(index, item);
        }


        public T this[int index]
        {
            get { return this.list[index]; }
            set { this.list[index] = value; }
        }


        public IEnumerator<T> GetEnumerator()
        {
            //return new RowEnumerator<T>(this);
            //return this.list.GetEnumerator();
            return this.list as IEnumerator<T>;
        }


        IEnumerator IEnumerable.GetEnumerator()
        {
            //return new RowEnumerator<T>(this);
            //return this.list.GetEnumerator();
            return this.list as IEnumerator<T>;
        }
        


        class RowEnumerator<T> : IEnumerator<T>
        {
            private RowList<T> _List;
            private int _Index = -1;
            private T _Current;

            public RowEnumerator(RowList<T> list)
            {
                this._List = list;
                this._Index = -1;
                this._Current = default(T);
            }


            public bool MoveNext()
            {
                if (++this._Index >= this._List.Count)
                    return false;

                this._Current = this._List[this._Index];

                return true;
            }


            public void Reset()
            {
                this._Index = -1;
            }


            public T Current
            {
                get { return this._Current; }
            }


            object IEnumerator.Current
            {
                get { return this._Current; }
            }

            void IDisposable.Dispose() { }
        }
    }
}

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

BindingList<T> habe ich jetzt auch probiert. Die Klasse stellt mir exakt die gleichen Eigenschaften, Methoden und Events zur Verfügung wie BindingSource selbst. Das Problem ist aber, dass ich keinen Zugriff auf das gelöschte oder zu löschende Element habe. ListChangedEventArgs gibt mir zwar die Information, dass gelöscht wurde, jedoch keine Referenz mehr auf das gelöschte Objekt. Und genau das muss ich aber behandeln. Da es sich bei meinen Elementen um DataRows oder abgeleitete typisierte Klassen handelt, muss ich deren RowState auch auf Deleted setzen können. DataTable oder TypedDataTable können das ja auch. Die kann ich aber nicht verwenden, weil Sie nur ihre Columns zur Bindung bereitstellen (der vorherige Post).

Ich habe sogar schon versucht, berechnete Spalten hinzuzufügen, aber da bietet die Expression einfach nicht genügend Möglichkeiten.

771 Beiträge seit 2009
vor 9 Jahren

Hast du auch schon das nichtgenerische Interface IList probiert, denn BindingList<T> implementiert ja diese und nicht die generische Version?

Oder aber du erbst von BindingList<T> und überschreibst die virtuelle Methode


protected virtual void RemoveItem(int index)

und rufst darin die Basisklassenmethode auf.

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

Wow, das sieht wirklich gut aus. Dass die Remove-Methode hier virtuell ist hatte ich übersehen (Asche auf mein Haupt !!!) und vielen Dank. Dann war Sebastians Tipp doch der Richtige.

Ich werde das jetzt ausgiebig testen und dann denn Thread schließen - falls nicht noch neue Probleme auftreten.

Vielen Dank an alle!

Achim

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

So! Mit dem Überschreiben der virtuellen Methoden und dem AddingNew-Event in BindingList<T> funktioniert es.

Aber eines verstehe ich immer noch nicht: Warum hat meine Implementation von IList (siehe Beitrag weiter oben) nicht funktioniert?

Kannst Du mir das vielleicht erklären, Sebastian?

Vielen Dank !!!

Gelöschter Account
vor 9 Jahren

Wenn du IList selbst implementiert sollte es nach meinem Verständniss eigentlich auch funktionieren. (Typischerweise verwenden BindingSources/BindingControls den Enumerator nach Möglichkeit garnicht sondern verwenden nur this_ und Count für den reinen Zugriff wenn möglich). Evtl. hast du auf die falsche Methode geschaut, also Remove statt RemoveAt.

S
145 Beiträge seit 2013
vor 9 Jahren

du hast die Remove Methode mit new neu angelegt.

Die BindingSource kennt aber höchstens die Remove Methoden der List<T>,
deshalb wurden deine nie ausgelöst.

Gelöschter Account
vor 9 Jahren

@Spyke: Es geht hier um den 2. Versuch wo IList<T> selbst implementiert wird.

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

Die BindingSource manipuliert die als DataSource übergebene Liste nur bei Änderungen, Inserts (Adds) und Deletes (Remove).
Intern muss sie aber eine eigene Liste mit Verweis auf die originalen Elemente führen. Das kann nur so sein, denn sonst würden Sort und Filter nicht funktionieren ohne die originale Liste zu verändern.

Das erklärt aber leider immer noch nicht, warum meine Implementation von IList nicht funktioniert ...

Gelöschter Account
vor 9 Jahren

Funktiniert deine IList<T> Implementierung denn ansonsten wie erwartet mit der BindingSource zusammen?

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

So ganz auf Herz und Nieren habe ich sie natürlich nicht getestet. Mein Focus lag auf der Remove-Problematik. Ansonsten (Browsen, Add) hat sie aber eigentlich einwandfrei funktioniert.

Es muss irgendwie mit der internen Kopie der Liste und dem Aufruf von RemoveCurrent() zusammenhängen ...