Benutzer mit den meisten Antworten
Fragen zum DataGridView

Frage
-
Hallo,
ich bin gerade dabei, ein Programm zu erstellen, welches meine DVDs verwaltet. Dazu verwende ich XML-Dateien und ein DataGridView, welches an ein Objekt gebunden ist. Hier folgt ein Ausschnitt meines Programmes mit der Dvd-Klasse, der DvdCollection-Klasse und dem Formular Form1:
Imports System.Collections.ObjectModel <System.Xml.Serialization.XmlRoot(ElementName:="dvd", DataType:="string")> _ Public Class Dvd <System.Xml.Serialization.XmlElement(ElementName:="title", DataType:="string")> _ Public Property Title() As String <System.Xml.Serialization.XmlElement(ElementName:="desc", DataType:="string")> _ Public Property Description() As String <System.Xml.Serialization.XmlElement(ElementName:="age_rating", DataType:="int")> _ Public Property AgeRating() As Integer <System.Xml.Serialization.XmlArray(ElementName:="actors")> _ <System.Xml.Serialization.XmlArrayItem(ElementName:="actor", DataType:="string")> _ Public Property Actors() As Collection(Of String) End Class
Imports System.IO Imports System.Linq Imports System.ComponentModel Public Class DvdCollection Inherits BindingList(Of Dvd) Public Sub LoadDvds() Dim dir As New DirectoryInfo("D:\My Documents") Dim fileList As FileInfo() = dir.GetFiles("*.xml", SearchOption.TopDirectoryOnly) Dim fileQuery = From file In fileList _ Where file.Name.StartsWith("DVD") _ Order By file.Name _ Select file ' Load xml files Dim dvd As Dvd For Each file As FileInfo In fileQuery dvd = XmlFile(Of Dvd).Load(file.FullName) Me.Add(dvd) Next End Sub End Class
Public Class Form1 Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load Dim dvdCollection As New DvdCollection dvdCollection.LoadDvds() ' Fill master DataGridView Me.BindingSource1.DataSource = dvdCollection Me.DataGridView1.DataSource = Me.BindingSource1 Me.DataGridView1.Columns.Remove("Description") ' Fill detail DataGridView Me.BindingSource2.DataSource = Me.BindingSource1 Me.BindingSource2.DataMember = "Actors" Me.DataGridView2.DataSource = Me.BindingSource2 End Sub Private Sub DataGridView1_SelectionChanged(sender As Object, e As System.EventArgs) Handles DataGridView1.SelectionChanged Dim selDvd As Dvd = TryCast(Me.BindingSource1.Current, Dvd) If selDvd IsNot Nothing Then Me.RichTextBox1.Text = selDvd.Description End If End Sub End Class
Nun kommen meine Fragen:
- Wie kann ich statt der Zahl in der Spalte AgeRating ein entsprechendes Bild aus der Resource darstellen? Mir ist bekannt, dass ich bei einem ungebundenen DGV das CellFormatting Ereignis behandeln kann. Aber bei einem gebundenen DGV?
- Ich möchte ausgewählte Zeilen aus dem DGV löschen. Dabei sollen dann auch die dazugehörigen XML-Dateien und Einträge aus der BindingList gelöscht werden. Wie könnte ich das implementieren?
- Ich habe ein Master-Detail DGV über BindingSources gekoppelt. Leider wird meine Actors Collection im Detail-DGV nicht richtig dargestellt. Statt der Namen erhalte ich die Stringlänge angezeigt (s. Bild). Ich habe keinen blassen Schimmer warum.
Für Testzwecke füge ich auch eine XML-Datei (DVD001.xml) bei:
<?xml version="1.0" encoding="utf-8"?> <dvd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <title>Try card</title> <desc>Bla Bla 1</desc> <age_rating>16</age_rating> <actors> <actor>Bruce Factor</actor> <actor>Hint Willis</actor> </actors> </dvd>
Ist jemand bereit, sich o.g. Fragen anzunehmen?
EDIT: Die Klasse XmlFile(of T) ist in diesem Thread zu finden.
Schöne Grüße,
LittleBlueBird
- Bearbeitet LittleBlueBird Dienstag, 17. Juli 2012 20:55 Link zur XmlFile Klasse hinzugefügt
Antworten
-
Hallo LittleBlueBird,
irgendwie ist mir dieser Thread dauernd "durchgeflutscht"...
Da Du das meiste bereits selbst gefunden hast, nur zur Anzeige von Grafiken.Im Prinzip kannst Du den gleichen Weg wie beim Dateinamen einschlagen.
Damit die Grafik zur aktuellen Alterseingabe passt solltest Du die INotifyPropertyChanged Schnittstelle implementieren.
<XmlRoot(ElementName:="dvd")> Public Class Dvd Implements System.ComponentModel.INotifyPropertyChanged ' Für Änderungsbenachrichtigungen Private _title As String Private _description As String Private _ageRating As Integer ' Grafiken aus den Ressourcen Private Shared Fsk6Image As Image = My.Resources.Fsk6 Private Shared Fsk12Image As Image = My.Resources.Fsk12 Private Shared Fsk16Image As Image = My.Resources.Fsk16 Private Shared Fsk18Image As Image = My.Resources.Fsk18 <XmlElement(ElementName:="title")> <DisplayName("Titel")> Public Property Title() As String Get Return Me._title End Get Set(value As String) If Me._title <> value Then Me._title = value OnPropertyChanged("Title") End If End Set End Property <XmlElement(ElementName:="desc")> <DisplayName("Beschreibung")> Public Property Description() As String Get Return Me._description End Get Set(value As String) If Me._description <> value Then Me._description = value OnPropertyChanged("Description") End If End Set End Property <XmlElement(ElementName:="age_rating")> <DisplayName("Altersfreigabe")> Public Property AgeRating() As Integer Get Return Me._ageRating End Get Set(value As Integer) If Me._ageRating <> value Then Me._ageRating = value OnPropertyChanged("AgeRating") OnPropertyChanged("AgeRatingImage") ' Bild aktualisieren End If End Set End Property <XmlIgnore()> <DisplayName("FSK")> Public ReadOnly Property FskImage As Image Get Select Case True Case _ageRating <= 6 Return Fsk6Image Case _ageRating <= 12 Return Fsk12Image Case _ageRating <= 16 Return Fsk16Image Case Else Return Fsk18Image End Select End Get End Property Protected Overridable Sub OnPropertyChanged(propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged End Class
Ich habe die Actors mal weggelassen, da hier (zunächst) nicht von Interesse.
Für die Grafiken verwende ich gemeinsame Ressourcen (nicht unbedingt vollständig).
Über die Benachrichtigung beim Ändern des Alters wird dann die Grafik synchron gehalten.Als Nebenfeature habe ich das DisplayNameAttribute eingefügt, damit die Überschriften in Deutsch sind.
Gruß Elmar
- Als Antwort markiert LittleBlueBird Freitag, 20. Juli 2012 18:47
-
Hallo,
IRaiseItemChangedEvents funktioniert nur mit INotifyPropertyChanged zusammen,
denn sie bewirkt, dass die BindingList sich in das Ereignis einklinkt.zu 1.)
Wenn Du für die Eigenschaft das Attribut Browsable(false) verwendest, so wird sie nicht angezeigt -
das gilt dann aber für alle Bindungen.
Willst Du die Eigenschaft nur im DataGridView ausblenden, solltest Du die Spalten bearbeiten.zu 2.)
Das kann man schon machen in dem sich eine lokalisierbare Version von DisplayName erstellt
und die DisplayName Eigenschaft überschreibt, so dass die Daten aus einer Ressource kommen:<AttributeUsage(AttributeTargets.[Class] Or AttributeTargets.Method Or AttributeTargets.[Property] Or AttributeTargets.[Event])> Public Class SRDisplayNameAttribute Inherits DisplayNameAttribute Public Sub New(displayName As String) MyBase.New(displayName) End Sub Private _replaced As Boolean = False Public Overrides ReadOnly Property DisplayName As String Get If Not _replaced Then _replaced = True ' Aus einer Resource Datei ersetzen ' DisplayNameValue enthält anfangs die Original-Zeichenfolge, danach den Ersatz MyBase.DisplayNameValue = strings.ResourceManager.GetString(MyBase.DisplayName) End If Return MyBase.DisplayName End Get End Property End Class
(gleiches macht im übrigen Microsoft bei der Lokalisierung für Eigenschaften in VS).
zu 3.) Verwende das DataGridView.CellToolTipTextNeeded-Ereignis
Was die Eigenschaftsnamen angeht: Da habe ich einfach phantasiert und die deutsche FSK herangezogen,
(und für die Bildchen schnell gemalte Zahlen - sehen scheußlich aus ;-)
Wie Du richtig erkannt hast, muss der Text für OnPropertyChanged zur Eigenschaft passen.
Da mir das verspätet eingefallen ist, habe ich die Umbenennung übersehen.Gruß Elmar
- Bearbeitet Elmar Boye Freitag, 20. Juli 2012 20:06
- Als Antwort markiert LittleBlueBird Freitag, 20. Juli 2012 21:18
Alle Antworten
-
Hallo,
auf Frage 3 habe ich selbst eine Antwort gefunden. Ich habe die Eigenschaft Actors der Klasse Dvd modifiziert,
<System.Xml.Serialization.XmlArray(ElementName:="actors")> _ <System.Xml.Serialization.XmlArrayItem(ElementName:="actor", Type:=GetType(Actor))> _ Public Property Actors() As Collection(Of Actor)
wobei die Klasse Actor wie folgt aussieht:
Public Class Actor <System.Xml.Serialization.XmlTextAttribute(DataType:="string")> _ Public Property Name As String End Class
Dann klappt es auch mit dem Detail-DGV.
Schöne Grüße,
LittleBlueBird
- Bearbeitet LittleBlueBird Mittwoch, 18. Juli 2012 16:40
-
Hallo,
auf Frage 2 habe ich auch eine Antwort gefunden. Ich füge der Klasse Dvd eine weitere Eigenschaft FileName hinzu und kennzeichne diese mit XmlIgnore wie folgt:
<System.Xml.Serialization.XmlIgnore()> _ Public Property FileName As String
Damit wird die Eigenschaft beim Serialisieren ignoriert, ich habe aber die Möglichkeit, in der Klasse DvdCollection beim Einlesen der Xml-Dateien die Eingenschaft zu setzen:
Dim dvd As Dvd = Nothing Dim fileName as String = String.Empty For Each file As FileInfo In fileQuery fileName = file.FullName dvd = XmlFile(Of Dvd).Load(fileName) dvd.FileName = fileName Me.Add(dvd) Next
Im Formular muss ich noch in der Methode Form1_Load folgende Zeile einfügen:
Me.DataGridView1.Columns.Remove("FileName")
Eine Lösung für Frage 1 habe ich bis dato nicht gefunden. Vielleicht kann mir doch jemand einen Hinweis geben.
Schöne Grüße,
LittleBlueBird
-
Hallo,
ich habe bzgl. Frage 1 nach wie vor keine konkrete Lösung. Den Wert durch das Bild zu ersetzen bringt ja nichts, da ich in eine TextBox-Spalte kein Bild einfügen kann. Nach langer Suche und Überlegung bin ich auf folgende Möglichkeiten gekommen:
- Ich füge eine zusätzliche ungebundene Image-Spalte ein, die in Abhängigkeit der AgeRating-Spalte die entsprechenden Bilder zeigt. Allerdings habe ich dann zwei Spalten (AgeRating und die Bild-Spalte). Die AgeRating-Spalte kann ich aber nicht entfernen, da ich sonst die Bild-Spalte nicht generieren kann. Was nun?
- Ich erstelle eine neue Klasse DvdImage, die sich von der Klasse Dvd lediglich bzgl. der Eigenschaft AgeRating unterscheidet. Diese würde dann nicht vom Typ Integer sondern vom Typ System.Drawing.Bitmap sein. In der DvdCollection Klasse würde ich dann nach
dem Deserialisieren das Mapping von Dvd zu DvdImage machen. Die Dvd-Objekte muss ich aber immer mitführen (eigentlich eine Verschwendung von Speicher), da ich sie für das Serialisieren benötige.
Hat einer der Profis unter Euch eine bessere Idee?
Ich habe zudem noch eine Vereinfachung für Form1 gefunden. Statt das Ereignis SelectionChanged des DGV zu behandeln, binde ich die RichtTextBox an die Spalte "Description" von BindingSource1:
Me.RichTextBox1.DataBindings.Add(New Binding("Text", Me.BindingSource1, "Description", True))
Da ich in meiner letzten Antwort die Routine zum Löschen ausgewählter Zeilen und der entsprechenden Dateien nicht aufgeführt habe, folgt sie jetzt:
Private Sub DeleteToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles DeleteToolStripMenuItem.Click For Each selectedRow As DataGridViewRow In Me.DataGridView1.SelectedRows File.Delete(CType(selectedRow.DataBoundItem, Dvd).FileName) dvdCollection.RemoveAt(selectedRow.Index) Next End Sub
Schönen Gruß,
LittleBlueBird
- Bearbeitet LittleBlueBird Donnerstag, 19. Juli 2012 18:18 Ergänzung hinzugefügt
-
Hallo LittleBlueBird,
irgendwie ist mir dieser Thread dauernd "durchgeflutscht"...
Da Du das meiste bereits selbst gefunden hast, nur zur Anzeige von Grafiken.Im Prinzip kannst Du den gleichen Weg wie beim Dateinamen einschlagen.
Damit die Grafik zur aktuellen Alterseingabe passt solltest Du die INotifyPropertyChanged Schnittstelle implementieren.
<XmlRoot(ElementName:="dvd")> Public Class Dvd Implements System.ComponentModel.INotifyPropertyChanged ' Für Änderungsbenachrichtigungen Private _title As String Private _description As String Private _ageRating As Integer ' Grafiken aus den Ressourcen Private Shared Fsk6Image As Image = My.Resources.Fsk6 Private Shared Fsk12Image As Image = My.Resources.Fsk12 Private Shared Fsk16Image As Image = My.Resources.Fsk16 Private Shared Fsk18Image As Image = My.Resources.Fsk18 <XmlElement(ElementName:="title")> <DisplayName("Titel")> Public Property Title() As String Get Return Me._title End Get Set(value As String) If Me._title <> value Then Me._title = value OnPropertyChanged("Title") End If End Set End Property <XmlElement(ElementName:="desc")> <DisplayName("Beschreibung")> Public Property Description() As String Get Return Me._description End Get Set(value As String) If Me._description <> value Then Me._description = value OnPropertyChanged("Description") End If End Set End Property <XmlElement(ElementName:="age_rating")> <DisplayName("Altersfreigabe")> Public Property AgeRating() As Integer Get Return Me._ageRating End Get Set(value As Integer) If Me._ageRating <> value Then Me._ageRating = value OnPropertyChanged("AgeRating") OnPropertyChanged("AgeRatingImage") ' Bild aktualisieren End If End Set End Property <XmlIgnore()> <DisplayName("FSK")> Public ReadOnly Property FskImage As Image Get Select Case True Case _ageRating <= 6 Return Fsk6Image Case _ageRating <= 12 Return Fsk12Image Case _ageRating <= 16 Return Fsk16Image Case Else Return Fsk18Image End Select End Get End Property Protected Overridable Sub OnPropertyChanged(propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged End Class
Ich habe die Actors mal weggelassen, da hier (zunächst) nicht von Interesse.
Für die Grafiken verwende ich gemeinsame Ressourcen (nicht unbedingt vollständig).
Über die Benachrichtigung beim Ändern des Alters wird dann die Grafik synchron gehalten.Als Nebenfeature habe ich das DisplayNameAttribute eingefügt, damit die Überschriften in Deutsch sind.
Gruß Elmar
- Als Antwort markiert LittleBlueBird Freitag, 20. Juli 2012 18:47
-
Hallo Elmar,
ich bedanke mich ganz herzlich für Dein Feedback. Deine Lösung ist super elegant. Darauf wäre ich nicht gekommen, wobei sie einleuchtend ist.
Hinsichtlich der Schnittstelle INotifyPropertyChanged dachte ich, dass sie nicht notwendig sein würde, wenn die Klasse DvdCollection von der BindingList(Of Dvd) abgeleitet wird, weil diese ja die Schnittstelle IRaiseItemChangedEvents implementiert.
Ich habe noch zusätzliche Fragen:
- Ist es möglich, die Eigenschaft Description in der Dvd Klasse mit einem Attribut zu kennzeichnen, damit sie im DGV nicht dargestellt wird? Ich habe es zwar mit BindableAttribute(False) versucht, hat aber nichts gebracht. Außerdem ist die Eigenschaft Description an RichTextBox1 gebunden, also kann ich BindableAttribute schon gar nicht verwenden. Andere Attribute habe ich in der Hilfe nicht gefunden.
- Ich habe versucht, mittels DisplayNameAttribute den Spaltenkopf des DGV zu lokalisieren. Das funktioniert aber nicht, da die DisplayNameAttribute Klasse eine Konstante als Argument erwartet. Eine String-Resource ist jedoch eine Variable. Wie läßt sich das lösen?
- Wie komme ich an die ToolTips der einzelnen Zellen bei einem gebundenen DGV? Ich würde gerne bei der Bild-Spalte die Zahlen auch als ToolTip darstellen.
Ich habe noch eine kleine Anmerkung zu Deinem Code: Die Eigenschaft FskImage müsste in AgeRatingImage umbenannt werden, oder aber die Methode OnPropertyChanged müsste "FskImage" als Argument erhalten, sonst klappt es mit der Benachrichtigung für diese Eigenschaft nicht.
Schönen Gruß,
LittleBlueBird
- Bearbeitet LittleBlueBird Freitag, 20. Juli 2012 18:45 Korrektur
-
Hallo,
IRaiseItemChangedEvents funktioniert nur mit INotifyPropertyChanged zusammen,
denn sie bewirkt, dass die BindingList sich in das Ereignis einklinkt.zu 1.)
Wenn Du für die Eigenschaft das Attribut Browsable(false) verwendest, so wird sie nicht angezeigt -
das gilt dann aber für alle Bindungen.
Willst Du die Eigenschaft nur im DataGridView ausblenden, solltest Du die Spalten bearbeiten.zu 2.)
Das kann man schon machen in dem sich eine lokalisierbare Version von DisplayName erstellt
und die DisplayName Eigenschaft überschreibt, so dass die Daten aus einer Ressource kommen:<AttributeUsage(AttributeTargets.[Class] Or AttributeTargets.Method Or AttributeTargets.[Property] Or AttributeTargets.[Event])> Public Class SRDisplayNameAttribute Inherits DisplayNameAttribute Public Sub New(displayName As String) MyBase.New(displayName) End Sub Private _replaced As Boolean = False Public Overrides ReadOnly Property DisplayName As String Get If Not _replaced Then _replaced = True ' Aus einer Resource Datei ersetzen ' DisplayNameValue enthält anfangs die Original-Zeichenfolge, danach den Ersatz MyBase.DisplayNameValue = strings.ResourceManager.GetString(MyBase.DisplayName) End If Return MyBase.DisplayName End Get End Property End Class
(gleiches macht im übrigen Microsoft bei der Lokalisierung für Eigenschaften in VS).
zu 3.) Verwende das DataGridView.CellToolTipTextNeeded-Ereignis
Was die Eigenschaftsnamen angeht: Da habe ich einfach phantasiert und die deutsche FSK herangezogen,
(und für die Bildchen schnell gemalte Zahlen - sehen scheußlich aus ;-)
Wie Du richtig erkannt hast, muss der Text für OnPropertyChanged zur Eigenschaft passen.
Da mir das verspätet eingefallen ist, habe ich die Umbenennung übersehen.Gruß Elmar
- Bearbeitet Elmar Boye Freitag, 20. Juli 2012 20:06
- Als Antwort markiert LittleBlueBird Freitag, 20. Juli 2012 21:18
-
Hallo Elmar,
ich kann Dir nicht oft genug danken für die Hilfestellung, die Du mir und natürlich auch der Allgemeinheit im Forum leistest.
zu 1) Ja, ich wollte die Eigenschaft "Description" nur im DGV ausblenden, da ich sie an die RichTextBox1 gebunden habe.
zu 2) Wenn ich das richtig verstanden habe, ist "strings" nur der Platzhalter für die Resourcendatei, die die Übersetzungen enthält. In meinem Fall ist das "My.Resources".
zu 3) Ok, ich habe gehofft, man könnte das bei der Bindung irgendwie angeben.
Meine Bildchen stammen von der FSK. Ich habe sie mit Paint.NET auf 16px verkleinert. ;-)
Schöne Grüße,
LittleBlueBird
-
Hallo LittleBlueBird,
zu 1) die automatische Generierung des DataGridView ist zwar für schnelle Ergebnisse brauchbar,
für ein "richtiges" Layout empfiehlt es sich, die Spalten manuell zu konfigurieren.
Ergo: Lasse unnötige Attribute weg und stelle es passend ein.zu 2) Ja, Strings ist eine Ressourcendatei (resx).
My.Resources tut es dafür auch, solange die Geschichte überschaubar bleibt.zu 3) Ein Ereignis ist hier das universellste, das für alle Inhalte passt.
Da hier nur einige wenige Möglichkeiten vorkommen, sollte es überschaubar bleiben.Gruß Elmar