ByVal prop As PropertyDescriptor

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von Manfred_62.

    ByVal prop As PropertyDescriptor

    Hallo zusammen,

    ich bin neu hier und bei VB.net. Habe momentan ein Problem bei dem ich nicht weiter komme und nichts finde was ich verstehe und mir helfen könnte.

    Ich implementiere in einer Klasse die Schnittstelle IBindingList.

    VB.NET-Quellcode

    1. Public Class AllClubs
    2. Inherits BindingList(Of AllClubs)
    3. Implements IBindingList


    Ich möchte die Find-Funktion benutzen. Also muss ich den Code in der Klasse dafür schreiben, soweit ich bisher verstanden habe.

    VB.NET-Quellcode

    1. Function Find(ByVal prop As PropertyDescriptor, ByVal key As Object) As Integer Implements IBindingList.Find
    2. Dim i As Integer
    3. While i < Count
    4. If Item(i).ID = CStr(key).ToLower() Then
    5. Return i
    6. End If
    7. i += 1
    8. End While
    9. Return -1
    10. 'Throw New NotSupportedException()
    11. End Function


    Ich verwende die Find-Funktion im Programm als Test ob sie funktioniert.

    VB.NET-Quellcode

    1. MessageBox.Show(oBindingSource.Find("ID", oBindingSource.Current.ID))


    So funktioniert es auch. Ich suche im Feld "ID" den Wert "key". Jedoch übergebe ich doch den Text "ID" in "prop". Wie kann ich "prop" verwenden um nicht direkt "Item(i).ID" angeben zu müssen? Beim nächsten Suchvorgang möchte ich dann nicht das Feld "ID" sondern ein anderes Feld verwenden. Wie schreibe ich das, dass die Angabe "Item(i).ID " nicht nur das Feld "ID" verwendet sondern über "prop" flexibel wird.

    Ich hoffe es versteht jemand mein Kauderwelsch ;(

    LG
    Manfred

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Manfred_62“ ()

    Hi, willkommen im Forum.

    Dein erstes Listing macht keinen Sinn: Du leitest von BindingList(T) ab und willst gleichzeitig IBindingList implementieren. BindingList(T) implementiert aber IBindingList bereits. Leite nur ab und überschreibe dann FindCore. Vergiss nicht, in SupportsSearchingCore "True" zurückzuliefern (vgl. msdn.microsoft.com/en-us/libra…glist.find(v=vs.110).aspx).

    Die Frage ist, wofür du den Elementindex brauchst. Falls du das Element selbst suchst, lass das IndexOf weg. Das hier ist einfacher als dein Ansatz:

    VB.NET-Quellcode

    1. Dim bl As New BindingList(Of String)
    2. bl.Add("b") ' 0
    3. bl.Add("A") ' 1
    4. bl.Add("c") ' 2
    5. Dim textToFind = "a"
    6. Dim result = bl.IndexOf(bl.SingleOrDefault(Function(e) e.ToLower().Equals(textToFind)))
    7. ' result = 1

    ...oder ich habe nicht verstanden, was genau du erreichen willst.
    Gruß
    hal2000
    Hallo Hal2000,

    danke für deine Infos. Leider komme ich mit beiden Varianten nicht zum Ziel.

    Ich habe nun geändert:

    VB.NET-Quellcode

    1. Public Class AllClubs
    2. Inherits BindingList(Of AllClubs)
    3. 'Implements IBindingList
    4. Protected Overrides ReadOnly Property SupportsSearchingCore() As Boolean
    5. Get
    6. Return True
    7. End Get
    8. End Property
    9. Protected Overrides Function FindCore(ByVal prop As PropertyDescriptor, ByVal key As Object) As Integer
    10. Dim i As Integer
    11. While i < Count
    12. If Item(i).ID = CStr(key).ToLower() Then
    13. Return i
    14. End If
    15. i += 1
    16. End While
    17. Return -1
    18. 'Throw New NotSupportedException()
    19. End Function


    Ich möchte nun von außerhalb der Klasse AllClubs die Funktion benutzen:

    VB.NET-Quellcode

    1. MessageBox.Show( oAllClubs.FindCore("ID", oBindingSource.Current.ID))


    Nun bekomme ich eine rote Wellenlinie mit dem Hinweis es sei Protected und in diesem Kontext nicht zugreifbar. Jetzt hab ich herumprobiert (man sieht ich kenn mich nicht aus). Kam zu keinem Ziel.

    Habe deine Variante mit IndexOf versucht umzusetzen:

    VB.NET-Quellcode

    1. MessageBox.Show(oAllClubs.IndexOf(oAllClubs.SingleOrDefault(Function(e) oAllClubs.ID.Equals("1"))))


    Auch hier hab ich natürlich "experimentiert". Egal was ich mache. Das Ergebnis ist immer -1.
    Ich habe in meiner Versuchsliste 3 "Datensätze" mit einer Menge "Feldern". Eines davon ist das Feld "ID" mit einer eindeutigen Nummer. Es ist der zweite Datensatz in der Listbox ausgewählt. In den TextBoxen werden die richtigen Werte angezeigt. Das funkt alles super.
    Ich habe nun versucht den Inhalt des Feldes "ID" in der Auflistung der drei Versuchsdatensätze zu finden:

    VB.NET-Quellcode

    1. MessageBox.Show(oAllClubs.IndexOf(oAllClubs.SingleOrDefault(Function(e) oAllClubs.ID.Equals(oBindingSource.Current.ID))))


    Das Ergebnis ist immer -1. Irgendwie stehe ich völlig am Schlauch.

    LG
    Manfred
    Zunächst: Du kannst FindCore nicht extern aufrufen, weil diese Methode Protected ist. Dazu ist der Umweg über IBindingList.Find() nötig (via Cast auf IBindingList). Allgemein sehe ich noch immer keinen Sinn in deinem Vorhaben, schon allein weil die Parametertypen nicht passen. Der Aufruf oAllClubs.FindCore("ID", oBindingSource.Current.ID) passt nicht zur Signatur der FindCore-Methode - dein erster Parameter ist vom Typ String, der erste Parameter in der Signatur ist aber ein PropertyDescriptor.

    Bitte schildere mal den größeren Rahmen deines Vorhabens. Die Klassen, die du verwendest, stammen aus WPF, also geht es vermutlich um ein WPF-Projekt. Nun hast du eine ListBox mit Datensätzen und mehrere Textboxen, die bestimmte Eigenschaften des aktuell ausgewählten Datensatzes anzeigen. Dabei stellen sich mir folgende Fragen:
    - Welchen Typ haben die Datensätze der ListBox (Klassendeklaration / was sind die relevanten Eigenschaften und deren Datentypen; offenbar gibt es eine Eigenschaft "ID")
    - Was ist die Datenquelle / wie kommen die Daten in die ListBox (Add[Range], per Binding, ItemsSource, ...?)
    - Welche Aktion löst das Suchen der ID aus? Ein Button?
    - Warum willst du die ID finden, und wo kommt das Suchkriterium her? <-- Das ist wichtig, weil du den markierten Datensatz bereits kennst. Da gibts nichts mehr zu suchen.
    Gruß
    hal2000
    Hallo Hal2000,

    *grins* du zeigst mir auf wie wenig ich mich auskenne. Trotz wochen- / monatelangem suchen und lesen (MSDN, Bücher, Google).

    Ok, gerne gebe ich dir einen Einblick.
    Vorweg gesagt, habe ich mich mit diesem Projekt entschlossen endlich meine Programmiersprache zu ändern weil MS Visual Foxpro nicht mehr weiter entwickelt. Meine Wahl viel auf VB.net. Hier ist alles ganz anders. Man muss völlig anders denken. Egal. Ist nun mal so.

    Dieses Projekt wird ein Spieleverwaltungsprogramm für einen oder mehrere Faustballvereine. Um am Platz eine aktuelle Anzeige via Monitore von den gerade laufenden Spielen zu haben. Um immer am letzten Stand zu sein. Zusätzlich natürlich wird mit der Zeit eine History über Meisterschaften usw. angelegt sein. Die Technik schaut grob so aus: Eine Datenbank liegt im Internet. Ist notwendig damit man von jedem Punkt der Welt aus zugreifen kann. Natürlich ist ein direkter Zugriff übers I-Net nicht denkbar. ich gehe den Umweg über Scripte für den Datenaustausch. Lokal liegen die Daten dann in Textfeldern auf der Platte. Datensatz für Datensatz. Was halt aktuell gebraucht wird.
    Der konkrete Programmteil an dem ich arbeite ist eine Form zur Verwaltung der Vereinsdaten. In einer Listbox sind die Vereine aufgelistet. Wenn ich einen Verein anklicke werden dessen Daten in TextBoxen dargestellt und können geändert werden usw. Das funkt auch alles prima.
    • Ich lese die Daten aus der Textdatei aus und lade sie in eine BindingList(of T). Das sollte meine unterste Ebene sein.
    • Dann habe ich eine BindingSource als nächste Ebene.
    • Danach die TextBoxen und die Listbox zum Anzeigen und auswählen der Daten aus der BindingSource
    Soweit funkt das auch prima. Ich klicke in der Listbox auf einen Verein und es werden mir sofort seine Daten in den TextBoxen angezeigt. Ich ändere Daten. Diese Änderungen werden sofort übernommen und überall auf der Form aktualisiert. Bin bisher von der BindingList(of T) begeistern.

    So, jetzt komme ich zum eigentlichen Problem. Wenn ich mit dem Abbrechen Button die Form verlasse müssen die original Daten wieder hergestellt werden oder die Änderungen einfach verworfen. Natürlich kann ich mir beim Laden der Form eine Sicherungskopie anlegen usw.
    Nachdem ich aber nicht weiß wie diese Ebenen untereinander Daten austauschen und zu welchem Zeitpunkt, musste ich das irgendwie feststellen. Es wäre ja super wenn die geänderten Daten nur in der BindingSource stehen und in der BindingList(of T) noch die originalen Daten. Dann wäre ein Abbrechen höchst einfach handhabbar.
    Oder verhält es sich so, dass in der BindingSource nichts drinnen steht und sie nur als Vermittler fungiert? Viele Fragen!
    Daher wollte ich die aktuell angezeigten Daten, wenn ich sie geändert habe, mit den Daten der untersten Ebene vergleichen. Daher diese Probleme mit dem "Find".

    Aber eventuell habe ich hier auch grobe Gedankenfehler im Vorgehen drinnen?
    Aber das mit den Schnittstellen ist mir immer noch irgendwie nicht durchschaubar. Besonders das Händling damit. Ich will doch blos eine Find-Funktion haben. Ich glaub ich programmiere mir diese Funktion komplett selber ohne diesen ganzen Schnittstellen-schnick-schnack ............. :/

    LG
    Manfred
    Ich verstehe. Du hast ein anderes, viel größeres Problem: Du lernst eine für dich neue Programmiersprache (VB.NET - gute Wahl :)), möchtest dich gleichzeitig ein dir unbekanntes sehr komplexes neues UI-Framework (WPF) einarbeiten und gleich zu Beginn eine ziemlich komplexe Anwendung schreiben. Nichts gegen dich, aber glaubst du wirklich, dass das so funktionieren kann? Wie auch immer - mein Tipp an dieser Stelle sind erstmal folgende Bücher:

    Zu VB.NET: "Microsoft Visual Basic 2010 - Das Entwicklerbuch" (Microsoft Press, kostenlos) oder "Visual Basic 2008 - Das umfassende Handbuch" (Rheinwerk Verlag; kostenlos; openbook.rheinwerk-verlag.de/visualbasic_2008/). Bei den Büchern gehen die Meinungen im Forum auseinander - such dir eins aus.
    Zu WPF: "WPF - Das umfassende Handbuch" (Rheinwerk, nicht kostenlos)

    Zu dem, was du als "Schnittstellen" bezeichnest: Die WPF trennt Daten und GUI. Das bedeutet, dass du eine modernere Variante des sogenannten MVC-Patterns implementieren solltest (bei WPF heißt es MVVM-Pattern). Hier die Kurzfassung davon:

    - Das View (GUI) wird mit XAML deklarativ beschrieben. Daneben gibt es eine Codebehind-Datei, die z.B. Ereignishandler beinhaltet.
    - An geeigneter Stelle ist im View der sogenannte Datenkontext definiert, der auf eine Instanz des ViewModels zeigt.
    - Das View zeigt Daten lediglich an - es verwendet Vermittler (Bindings), die die Verbindung zu den Daten im ViewModel herstellen und z.B. auf Änderungen überwachen.
    - Das ViewModel enthält die Daten des Programms nicht selbst, sondern dient als Proxy für das Model. Im ViewModel ist die Logik der Anwendung implementiert - d.h. ändert der Benutzer Daten über das View, bekommt das ViewModel die Änderung mit. Es aktualisiert das Model entsprechend, woran das View nicht beteiligt ist.
    - Das Model ist unabhängig und lediglich für die Datenhaltung zuständig. Es stellt z.B. die Verbindung zu einer Datenbank her.

    Die Anwendung ist also ganz anders aufgebaut, als du vielleicht vermutest. Die Sache mit der Find-Funktion kann daher nicht funktionieren. Natürlich kann man bei kleinen Anwendungen Abstraktionsschichten weglassen und alles ins CodeBehind schreiben - die erwartete Größe deiner Anwendung ist aber alles andere als klein.

    Eine pragmatische Lösung für dein konkretes Problem: Verwende eine BindingGroup. Damit kannst du eine Transaktion definieren, die du zurücknehmen kannst. Beispiel:

    XAML:

    XML-Quellcode

    1. <Window x:Class="MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. Title="MainWindow" Height="350" Width="525">
    5. <Window.BindingGroup>
    6. <BindingGroup />
    7. </Window.BindingGroup>
    8. <StackPanel>
    9. <TextBox Text="{Binding Firstname}" />
    10. <TextBox Text="{Binding Lastname}" />
    11. <Button Content="OK" Click="ButtonOK_Click" />
    12. <Button Content="Cancel" Click="ButtonCancel_Click" />
    13. </StackPanel>
    14. </Window>


    CodeBehind:

    VB.NET-Quellcode

    1. Class MainWindow
    2. Sub New()
    3. ' Dieser Aufruf ist für den Designer erforderlich.
    4. InitializeComponent()
    5. ' Fügen Sie Initialisierungen nach dem InitializeComponent()-Aufruf hinzu.
    6. Me.DataContext = New User With {.Firstname = "John", .Lastname = "Doe"}
    7. Me.BindingGroup.BeginEdit()
    8. End Sub
    9. Private Sub ButtonOK_Click(sender As Object, e As RoutedEventArgs)
    10. If Me.BindingGroup.CommitEdit() Then
    11. Me.BindingGroup.BeginEdit()
    12. End If
    13. End Sub
    14. Private Sub ButtonCancel_Click(sender As Object, e As RoutedEventArgs)
    15. Me.BindingGroup.CancelEdit()
    16. End Sub
    17. End Class
    18. Class User
    19. Property Firstname As String
    20. Property Lastname As String
    21. End Class

    Die Anwendung zeigt "John Doe" als Namen an. Ändere den Vor- oder Nachnamen und klicke auf "Cancel" - die Änderung wird zurückgenommen. Der Klick auf OK übernimmt die Änderung und schreibt diese auch in das User-Objekt.
    Gruß
    hal2000
    Hallo Hal2000,

    vielen Dank für deine bisherigen Ausführungen. Werde mir die beiden Literaturhinweise zu Herzen nehmen. Den einen Download hab ich mir schon geholt und darin geschmökert.

    LG
    Manfred