Laden...

An abgeleitete Collection gebundene ListBox bleibt leer

Erstellt von User26886 vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.651 Views
U
User26886 Themenstarter:in
10 Beiträge seit 2015
vor 4 Jahren
An abgeleitete Collection gebundene ListBox bleibt leer

Hallo,
wie unten gezeigt, versuche ich, an eine eigene Collection (abgeleitet von System.Collections.ObjectModel.Collection) eine ListBox zu binden. ANhand der Ausgaben in den MessageBopxen ist zu erkenne, dass die Collection funktioniert. Sowohl die Count Eigenschaft als auch der Zugriff auf die Elemente der COllection funktionieren.
Leider bleibt die an die Collection gebundene ListBox leer. Das binden an eine List<string> funktioniert aber.
An den MessageBoxen erkennt man ausserdem, dass die Collection-Methoden nach dem BInding nicht aufgerufen werden.
Das Ändern des Basistyps meiner COllection von System.Collections.ObjectModel.Collection auf System.Collections.Generic.List ändert auch nichts-
Warum bleibt die ListBox leer?
Der Code stammt übrigens aus einem normalen VS2017 WinForms Projekt. Die restlichen Projektdateien dürften irrelevant sein.
LG, Pisacou


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

namespace ListBoxBindingTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            ListBox teamBox = new ListBox();
            List<string> members = new List<string>{ "Member 0", "Member 1","Member 2","Member 3", "Member 4"};
            TeamList tl = new TeamList(new Team(5));
            //MessageBox.Show(tl.Count + " members in team, member 2: '" + tl[2]+"'");//bringt das erwartete Ergebnis
            teamBox.DataSource = tl;
            //teamBox.DataSource = members;//bringt das erwartete Ergebnis
            Controls.Add(teamBox);
        }
    }

    class Team
    {
        public Team(int count)
        {
            MemberCount = count;
        }

        public int MemberCount { get; }

        public string GetMember(int index)
        {
            return "Member " + Convert.ToString(index);
        }
    }
    class TeamList: List<string>
    {
        private Team team;

        public TeamList(Team team)
        {
            this.team = team;
        }

        public new int Count{
            get {
                MessageBox.Show("In TeamList.Count, members in team " + team.MemberCount);
                return team.MemberCount;
            }
        }

        public new string this[int index]
        {
            get {
                MessageBox.Show("Returning member " + index);
                return team.GetMember(index);
            }
        }
    }
}

5.657 Beiträge seit 2006
vor 4 Jahren

Du fügst nirgendwo einen Eintrag zur Liste hinzu. Dazu müßtest du die Add-Methode der List-Klasse aufrufen.

Weeks of programming can save you hours of planning

4.927 Beiträge seit 2008
vor 4 Jahren

Oder die Members auch noch an den TeamList-Konstruktor übergeben und den base-Konstruktor damit aufrufen:


public TeamList(Team team, IEnumerable<string> members)
  : base(members)
{
    this.team = team;
}

PS: Wenn die Team-Klasse einzig für die Anzahl der Member zuständig sein soll, dann ist diese aber überflüssig, da die Anzahl ja durch die Anzahl der Elemente in TeamList (bzw. List<string>) gegeben ist (ebenso dann die Überladungen von Count und this[int]).

T
2.219 Beiträge seit 2008
vor 4 Jahren

Ich würde sogar noch einen Schritt weiter gehen.
Erst einmal würde ich eine TeamMember Klasse anlegen, die ein Mitglied des Teams darstellt.
Die Team Klasse würde ich neu implementieren, diese würde dann die Property Members vom Typen List<TeamMember> anbieten.
Damit wäre dann eine sinnvollere Umsetzung als die Ableitung von List<string> umgesetzt.

Die TeamMember Klasse kann dann in Ruhe ausgebaut werden und enthält alle nötigen Informationen über ein Mitglied des Teams während die Team Klasse als Container für die Mitglieder eines Teams dient sowie auch Informationen über das Team selbst enthalten kann.

Somit sind die Daten alle sauber gekapselt und die unnötige Ableitung von List<string> durch saubere Kapselung ebenfalls erledigt.
Die Informationen über die Anzahl der Mitglieder kann man dann einfach über Members.Count abrufen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

U
User26886 Themenstarter:in
10 Beiträge seit 2015
vor 4 Jahren

Hallo,
danke für die Antworten.
MrSparkle/Th69: Meine TeamList war eigentlich als wrapper um ein Team gedacht, um die Team-Klasse auch als List verwenden zu können, wenn erforderlich. Sprich: die members existieren nur im Team, und die TeamList-Klasse sorgt nur dafür, dass auf die member über das List-interface zugegriffen werden kann. Es war nicht geplant, die Members zusätzlich zum Team auch in der TeamList-Klasse zu erstellen.

T_Virus: Der gepostete Code ist stark vereinfacht. Im realen Projekt geht es nicht um strings, sondern um kompliziertere Objekte, und die "echte" Team-Klasse ist auch komplexer als die gepostete. Die echte Team-Klasse nutzt aber Collections zur internen Speicherung von Objekten gleichen Typs. Ich hatte auch schon darüber nachgedacht, diese internen Collections einfach als ReadOnlyCollections zugänglich zu machen, aber das schien mir eher eine quick & diurty Lösung zu sein. Im normalen Gebrauch ist es nämlich unnötig, auf die interne Collection zugreifen zu können. Ich brauche diese Option nur zum Anzeigen der Objekte in einer ListBox, und da schien mir ein wrapper die sauberere Lösung zu sein, anstatt die Team-Klasse nochmal anzufassen. Ich werde den Weg mit der Property trotzdem mal testen, und die interne Collection als ReadOnly-Variante so zugänglich machen.
Mir ist aber immer noch nicht klar, warum die ListBox den Inhalt der TeamList nicht anzeigt. Offenbar nutzt die ListBox andere Methoden als Count und den Indexer, um auf den Inhalt ihrer DataSource zuzugreifen. Da werde ich wohl noch etwas rumprobieren müssen, bis ich das Verhalten verstanden habe.

16.792 Beiträge seit 2008
vor 4 Jahren

Wenn Du Code nur "stark vereinfachst" zeigst, dann sinkt natürlich auch die potentielle Qualität von Beiträgen.

Dir bringts nichts, wenn man am Thema vorbei redet und Helfer haben i.d.R. auch keine Lust auf wissentliches Rumstochern. 👍

Mir ist aber immer noch nicht klar, warum die ListBox den Inhalt der TeamList nicht anzeigt.

Zeig den echten Code, dann sieht man evtl. auch das Problem.

U
User26886 Themenstarter:in
10 Beiträge seit 2015
vor 4 Jahren

Hallo Abt,
die starke Vereinfachung habe ich aufgrund von Abschnitt 4.1 aus dem "Wie poste ich richtig?" Thread erstellt. In dem Abschnitt geht es zwar um das Anhängen von Projekten an Beiträge, für mich war es aber naheliegend, das ganze auch auf Beispielcodes zu übertragen: keinen unnötigen Code, der mit dem eigentlichen Problem nichts zu tun hat, sondern nur davon ablenkt, und es Helfern erschwert, das eigentliche Problem nachzuvollziehen. Stattdessen habe ich einen möglichst einfachen Code gepostet, der das in der Frage beschriebene Problem zeigt. Ich bitte dafür um Entschuldigung, dass ich nicht klar gemacht habe, dass der gepostete Code nur ein vereinfachtes Beispiel ist.
Der echte Code verhält sich aber genauso wie der vereinfachte Code: mein List-Wrapper um ein Objekt wird beim Binden an eine ListBox anders behandelt als eine "richtige" List. Deshalb ich fest überzeugt, dass das Posten des echten Codes nichts zur Klärung beitragen wird.
Nach T-Virus' Post ist der Ansatz mit dem Wrapper ohnehin kein guter Weg.
Falls du dennoch die Antwort auf die einizge Frage in meinem Eingangspost ("Warum bleibt die ListBox leer?") für hilfreich hältst, kann ich gerne die Antwort posten, sobald ich sie gefunden habe.

4.927 Beiträge seit 2008
vor 4 Jahren

Die Antwort haben wir dir doch schon in den ersten beiden Beiträgen gegeben. Eine leere List<> kann nun mal nicht in einer ListBox angezeigt werden (für das DataBinding ist nur die IEnumerable-Schnittstelle relevant, nicht die Count-Eigenschaft - auch wenn diese konsistent sein sollten, was sie eben in deiner gezeigten Implementierung nicht ist)...

U
User26886 Themenstarter:in
10 Beiträge seit 2015
vor 4 Jahren

Hallo TH69,
danke für den (erneuten) Hinweis.
Um das Thema abzuschliessen, hier die um eine konsistente IEnumerable-Implementierung ergänzte TeamList-Klasse, mit der die Teammitglieder tatsächlich in der ListBox angezeigt werden.


    class TeamList: List<string>, IEnumerable
    {
        private Team team;

        public TeamList(Team team)
        {
            this.team = team;
        }

        public new IEnumerator GetEnumerator()
        {
            for (int i = 0; i < team.MemberCount; i++)
            {
                yield return team.GetMember(i);
            }
        }

        public new int Count{
            get {
                return team.MemberCount;
            }
        }

        public new string this[int index]
        {
            get {
                return team.GetMember(index);
            }
        }
    }

16.792 Beiträge seit 2008
vor 4 Jahren

Du verfälscht damit das komplette Verhalten von List<T> und ignorierst auch das OOP Konzept.
Das ist alles andere als gut; ich kann von der Implementierung nur abraten.
Der Grundfehler ist hier, dass Du die Klasse Team schon falsch im Sinne der Architektur implementierst hast.

Der Rest ist einfach nur ein Folgefehler.

public class Team
{
    public IList<string> Members { get; set; }
}

Nun kannst Du Members einfach binden.

2.078 Beiträge seit 2012
vor 4 Jahren

Oder:

public class TeamMemberList : IList<string>
{
    private readonly IList<string> _inner;

    // Implementierung über "_inner" plus die eigenen Anpassungen.
}

Die Collection<T>-Klasse (oder die Ableitung ObservableCollection<T>) bietet neben den üblichen IList<T>-Funktionen auch noch Möglichkeiten, sie zu manipulieren, indem man ein paar der Methoden überschreibt. Das kann Arbeit ersparen, es kann's aber auch komplizierter machen.

T
2.219 Beiträge seit 2008
vor 4 Jahren

Was Abt hier vorschlägt, entspricht auch meinem Ansatz oben bei dem ich dir sogar eine Anleitung zur sauberen Kapselung sowie passenden Klassen Vorgaben für die Kapselung gegeben habe.
Dann hättest du eine saubere Lösung gehabt, die auch dann durch korrekte Bindung ohne Probleme angezeigt werden kann.

Dein aktueller Ansatz ist milde gesagt Schwachsinn.
Du machst aus einer Collection Klasse jetzt einen Container für einen Container mit einer internen Collection.
Das entbehrt jeder Grundlage eines sauberen Klassen Designs und bricht auch mit dem eigentlichen Sinn einer Ableitung von List<T>.

Du hast vorsätzliche alle Ratschläge ignoriert und sogar grundlegende Design Konzepte über Bord geworfen.
Dann hast du uns auch noch mit "vereinfachten" Pseudo Code in die falsche Richtung verwiesen.
Ich denke, du bist hier definitiv falsch wenn du so deine Probleme lösen willst!
Den damit verbrennen wir unsere Zeit mit einem Problem, was du selbst mit solchen "Lösungen" am ende hinsaust.

Nachtrag:
@Palladin007
Diese Lösung wäre auch noch gangbar, wobei ich aber in dem Fall des TE keinen Grund für eine Ableitung sehe, wenn man ein sauberes Klassen Design hat.
Am Ende reicht eigentlich eine saubere Container Klasse für die Member Liste.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.792 Beiträge seit 2008
vor 4 Jahren

Oder:

public class TeamMemberList : IList<string>  
{  
    private readonly IList<string> _inner;  
}  

Du machst hier eine Implementierung mit dem Innenleben eines Wrappers.
Man müsste ja alle Methoden selbst implementieren, wobei Du damit einfach List<T> neu entwickeln würdest.
Das macht wenig sinn.

Den damit verbrennen wir unsere Zeit mit einem Problem, was du selbst mit solchen "Lösungen" am ende hinsaust.

So würde ich das jetzt nicht formulieren, denn kein Helfer ist gezwungen zu antworten.
Mit dem anderen Teil, dass Ratschläge ignoriert und Konzepte über Bord geworfen wurden; damit hast natürlich Recht.

2.078 Beiträge seit 2012
vor 4 Jahren

Die Implementierung macht VisualStudio automatisch, Aufwand wäre das also keiner - zumindest solange Man mit einer halbwegs aktuellen VisualStudio-Version arbeitet.

Wenn man bestimmte Sonderanforderungen hat, kann das unter Umständen hilfreich sein. Ob es Sinn macht, steht dabei natürlich auf einem anderen Blatt, aber es ist definitiv eine mMn. legitime Option.

T
2.219 Beiträge seit 2008
vor 4 Jahren

@Abt
War etwas harsch formuliert, aber wenn man sich eben die Mühe schon gemacht hat und dann die Ratschläge trotz entsprechender Hinweise ignoriert werden, dann bin ich auch etwas gereizt.
Gerade wenn man häufig Code sieht, der Kraut und Rüben ist, dann ist man nicht begeistert wenn man trotz guter Ratschläge die Leute nicht dazu bekommt sauberen Code zu schreiben.

Das frustriert leider immer etwas, wenn man Jahre lang Software entwickelt und sich manche Leute scheinbar sehr Oberflächlich damit beschäftigen.

Aber zurück zum Thema.
Ich kann nur hoffen, dass der TE die Lösung nochmal überarbeitet um seinen Code sauber zu bekommen und diese Ableitung verwirft.

@Palladin007
In diesem Fall würde ich sagen, ist es keine sinnvolle Option.
Das Grundproblem des TE ist scheinbar generelle Grundlegende Konzepte zu verstehen und zu nutzen.
Das eigentliche Problem lässt sich auch am Code erkennen.
Hier würde ein sauberes Klassen Design solche Ansätze sogar überflüssig machen.
Es gab in meinen 10 Jahren als .NET/C# Entwickler bisher keinen driftigen Grund überhaupt eine Ableitung oder Implementierung eines Framework Interfaches vorzunehmen.
Wozu auch, wenn die bestehenden Collections aus dem Generics Namespace ca. 99,9% der Fälle abdecken.
I.d.R. braucht man einfach nur Listen und diesen Fall deck List<T> vollständig ab.

Auch wenn VS dir die Arbeit abnimmt, so sollte man immer unnötige Interface Implementierungen und Ableitungen vermeiden, wenn diese keinen direkten Mehrwert bieten oder einen konkreten Nutzen erfüllen.
Für die Lösung dieses schon banalen Problems, wäre dies mit Kanonen auf Spatzen schießen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.792 Beiträge seit 2008
vor 4 Jahren

dann die Ratschläge trotz entsprechender Hinweise ignoriert werden, dann bin ich auch etwas gereizt.

Bin ja auch ein Freund direkter und sachlicher Worte; aber spar Dir einfach die Energie für Themen, bei denen der Hilfesuchende Ratschläge offener annimmt 😃
Wenn er Ratschläge nich annehmen mag; dann ist das halt so. Zwingen kann man ihn auch nicht.

Die Implementierung macht VisualStudio automatisch, Aufwand wäre das also keiner - zumindest solange Man mit einer halbwegs aktuellen VisualStudio-Version arbeitet.

Und Tests schreiben wir nicht? 😉

Ehrlich: solange es keinen wirklichen Bedarf gibt, sollte man eine eigene Collection lassen. Gibt ja kaum ein Fall, der ohne Default-Collections nicht machbar wäre.
Es gibt so viele theoretisch Nachteile sowie realistische Nachteile (viele Schnittstellen akzeptieren leider Implementierungen (List<>), weil die Entwickler verpennt haben IList<T> zu verwenden), dass das kaum ohne Reibungsverluste zu machen ist.
Insbesondere wenn wie hier der Fehler einfach nur die simple Anwendung von OOP Basics und damit Code Architektur ist.

U
User26886 Themenstarter:in
10 Beiträge seit 2015
vor 4 Jahren

Hallo,
ein paar der letzten Kommentare möchte ich nicht so einfach stehen lassen.
Ein Team ist ein Team und keine erweiterte Version einer List oder IList. Ein Team ist in abstrakter Form zunächst mal nur das, was ich im Eingangspost geschrieben habe: Ein Ding mit einer Anzahl von Mitgliedern, und der Möglichkeit, auf die Mitglieder über einen Index zuzugreifen. In einem realen Team gibt es natürlich eine Möglichkeit zur Speicherung von Mitgliedern, aber wie die Speicherung genau aussieht, ist aus Gründen der Kapselung kein Bestandteil der öffentlichen API. Ich will keine Möglichkeit zulassen, an der Team-Klasse vorbei Mitglieder hinzuzufügen oder zu löschen oder gar die komplette Mitgliederliste auszutauschen.
Wir sind uns auch darüber einig, dass ein string keine vernünftige Abstraktion eines Teammitglieds oder einer Person ist, und dass es nicht sinnvoll ist, eine neue Klasse zu schreiben, die nichts weiter ist als eine List<string>, schon gar nicht, wenn die neue Klasse das IList<string> interface nicht implementiert.
Warum ist das nicht im Eingangspost geschrieben habe? Weil es mit der Frage nichts zu tun hat. Wenn es diesbezüglich zu Missverständnissen gekommen ist, bedauere ich das.