Property List(Of T): Bei Änderung neu zeichnen

  • VB.NET
  • .NET (FX) 4.0

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von tom87.

    Property List(Of T): Bei Änderung neu zeichnen

    Hallo :) ,

    Ich habe mir neulich ein kleines UserControl gemacht, welches die Aufgabe hat ein Tortendiagramm zu zeichnen.
    Nun gibt es dort eine Property, die die Aufgabe hat alle Abschnitte des Diagramms in einer Liste zusammen zu fassen:

    VB.NET-Quellcode

    1. Private mItems As New List(Of cPieChartItem)
    2. <EditorBrowsable(EditorBrowsableState.Always), Browsable(True), Bindable(True), _
    3. DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
    4. Public Property Items As List(Of cPieChartItem) 'Besagte Property
    5. Get
    6. Return mItems 'Liste zurückgeben
    7. End Get
    8. Set(ByVal Value As List(Of cPieChartItem))
    9. If mItems IsNot Value Then 'Überprüfen ob nicht die selbe Liste gesetzt wurde, um unnötiges aktualisieren zu verhindern
    10. mItems = Value 'Neue Liste setzten
    11. Me.Invalidate() 'Neu zeichnen
    12. End If
    13. End Set
    14. End Property


    Wenn ich jetzt die Auflistung (UserControl.Items.Add()) z.B. durch den Designer ändere, zeichnet sich das Control nicht neu. Scheinbar hat das "Me.Invalidate()" im Set-Block keinerlei Auswirkungen, wenn es sich um eine Liste handelt, denn nicht die Liste selbst wird komplett ausgetauscht, sondern nur ihre Auflistung.

    Leider stellt das System.Collections.Generic.List(Of T)-Objekt keine Events bereit, die ich nutzen könnte, sofern die Auflistung geändert wurde.
    Mehr (einfache) Lösungen fallen mir auf die Schnelle nicht ein.

    IDE: Visual Studio Express 2013 (Windows Desktop) auf Windows 7 E. x64
    .Net Framework: 4.0
    (Falles weitere Informationen gebraucht werden, bitte bescheid geben)


    Hier noch der Sourcecode für die Klasse cPieChartItem:

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Drawing
    3. Public Class cPieChartItem
    4. Public Property Color As Color = Drawing.Color.Red 'Farbe des Kreissegments
    5. Public Property Text As String = "cPieChartItem" 'Text/Beschreibung für die Legende
    6. Public Property Value As Single = 90 'Wert
    7. Public Sub New(ByVal _Color As Color, ByVal _Text As String, ByVal _Value As Single) 'Sub New
    8. Color = _Color
    9. Text = _Text
    10. Value = _Value
    11. End Sub
    12. Public Sub New() 'Sub New für den Designer
    13. End Sub
    14. End Class


    Ich habe bereits im Internet gesucht und keine Lösung gefunden, welche mir geholfen hat. Vielleicht hat jemand von euch hilfreiche Links oder weiß selbst eine Art auf die man die Problematik sauber lösen könnte ;)
    Ich bin mir auch bewusst, dass es bereits solche Controls gibt, dennoch bitte ich auf die Fragestellung einzugehen :)

    Ich bedanke mich schon einmal im Voraus, freue mich auf eure Antworten und wünsche allen einen schönen Restsonntag :)

    MfG

    tom87 schrieb:

    durch den Designer ändere
    Was willst Du im Designer angezeigt haben?
    Was willst Du zur Runtime angezeigt haben?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Vielen Dank für die Antwort!

    Vollzitat des direkten Vorposts an dieser Stelle entfernt ~VaporiZed

    Zur Runtime & im Designer soll er das Control IMMER neu zeichnen, sobald sich bei der Property "Items" die Auflistung verändert hat.

    MfG

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

    Hi
    lass eine Klasse von System.Collections.ObjectModel.Collection(Of T) erben und überschreibe InsertItem, RemoveItem, SetItem und ClearItems. Anschließend fügst du einen Konstruktor hinzu, der das Control bereitstellt und teilst diesem über assemblyweite (Friend) Methoden mit, wann eine solche Änderung vollzogen wurde (z.B. ein SubmitItemInserted, SubmitItemRemoved, SubmitItemSet, SubmitClearing, SubmitCleared). Zusätzlich teilst du hierbei jeweils benötigte Informationen, wie Indices oder Werte (bei Removed auch alte, InsertItem, usw. sind die Methoden, die genau das machen, d.h. du kannst vorher noch herausfinden, welches das alte Element bei RemoveItem und SetItem war - damit kannst du das Maximum neu berechnen, ohne alle Elemente durchzugehen). Anschließend führst du noch Methoden OnItemInserted, OnItemRemoved, OnItemSet, usw. ein, die standardmäßig ein Neuberechnen des Maximums vornehmen, sofern es an alle Elemente angepasst werden soll und invalidierst das Control.
    Statt der List(Of T) bietest du dann eine Instanz genau dieser Klasse an. Die Instanz kann nicht getauscht werden, sondern wird im Konstruktor zugewiesen.

    Gruß
    ~blaze~

    tom87 schrieb:

    soll er das Control IMMER neu zeichnen
    auch wenn es ggf. nicht sinnvoll ist?
    Beim Chart-Control werden Dummy-Daten mit den gegebenen Properties dargestellt. Willst Du so was machen?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Vielen Dank für Eure Antworten!

    Ich habe es so umgesetzt wie blaze es vorgeschlagen hat, auch wenn ich nicht alles 100%ig verstanden habe, was er gesagt hat, so bin ich doch zu einem funktionierendem Ergebnis gekommen; so wie ich es gewollt hatte!
    Zudem wäre ich jemanden sehr verbunden, der sich mit seinem geschulten Auge noch einmal kurz die Zeit nimmt, um sich meinen Quellcode anzuschauen und auf seine Qualität zu untersuchen, funktionieren tut es jedoch, wie gesagt ;).

    Ich hab's so gemacht:
    Spoiler anzeigen

    im cPieChart selbst (Ausschnitt):

    VB.NET-Quellcode

    1. '[...]
    2. Private WithEvents mItems As New cPieChartList 'Das Item selbst
    3. <EditorBrowsable(EditorBrowsableState.Always), Browsable(True), Bindable(True), _
    4. DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
    5. Public Property Items As cPieChartList 'Liste für die Kreissegmente
    6. Get
    7. Return mItems 'Liste zurückgeben
    8. End Get
    9. Set(ByVal Value As cPieChartList)
    10. If mItems IsNot Value Then
    11. mItems = Value 'Liste setzten
    12. Me.Invalidate() 'Neu zeichnen
    13. End If
    14. End Set
    15. End Property
    16. 'Hier entsprechende Events...
    17. Private Sub mItems_ItemChanged(sender As Object, e As cPieChartItemChangedEventArgs) Handles mItems.ItemChanged
    18. Me.Invalidate() 'Neu zeichnen, wenn die Items irgendwie geändert wurden
    19. End Sub
    20. Private Sub mItems_ItemsCleared(sender As Object, e As EventArgs) Handles mItems.ItemsCleared
    21. Me.Invalidate() 'Neu zeichnen, wenn ALLE Items entfernt wurden
    22. End Sub
    23. '[...]


    Die cPieChartList die von dem System.Collections.ObjectModel.Collection(Of T) erbt (Hier nochmal ein großes Danke! Da wäre ich nie drauf gekommen!):

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class cPieChartList
    3. Inherits System.Collections.ObjectModel.Collection(Of cPieChartItem) 'Klasse erben
    4. Friend Event ItemChanged(ByVal sender As Object, ByVal e As cPieChartItemChangedEventArgs) 'Meine kleinen...
    5. Friend Event ItemsCleared(ByVal sender As Object, ByVal e As EventArgs) '... beiden Events
    6. Public Enum ItemOperation 'Eventkram...
    7. Insert = 1
    8. Remove = 2
    9. _Set = 3
    10. ItemProperty = 4
    11. End Enum
    12. Private Sub OnPropertyChanged(ByVal sender As Object, ByVal e As EventArgs) 'Sub der handelt wenn sich die Propertys eines Items ändern
    13. RaiseEvent ItemChanged(Me, New cPieChartItemChangedEventArgs(CType(sender, cPieChartItem), -1, ItemOperation.ItemProperty)) 'Event: Item geändert
    14. End Sub
    15. Protected Overrides Sub InsertItem(index As Integer, item As cPieChartItem) 'Neues Item...
    16. AddHandler item.PropertyChanged, AddressOf OnPropertyChanged 'Eventhandeler für Item-Changed hinzufügen
    17. MyBase.InsertItem(index, item) 'Item hinzufügen
    18. RaiseEvent ItemChanged(Me, New cPieChartItemChangedEventArgs(item, index, ItemOperation.Insert)) 'Event: Item hinzugefügt
    19. End Sub
    20. Protected Overrides Sub RemoveItem(index As Integer) 'Item entfernen...
    21. RemoveHandler Me.Items(index).PropertyChanged, AddressOf OnPropertyChanged 'Eventhandler für Item-Changed entfernen
    22. MyBase.RemoveItem(index) 'Item entfernen
    23. RaiseEvent ItemChanged(Me, New cPieChartItemChangedEventArgs(Nothing, index, ItemOperation.Remove)) 'Event: Item entfernt
    24. End Sub
    25. Protected Overrides Sub SetItem(index As Integer, item As cPieChartItem) 'Item setzten...
    26. If Items.Contains(item) = False Then 'Wenn das Item noch nicht in der Auflistung ist:
    27. AddHandler item.PropertyChanged, AddressOf OnPropertyChanged 'Eventhandler für Item-Changed hinzufügen
    28. End If
    29. MyBase.SetItem(index, item) 'Item setzten
    30. RaiseEvent ItemChanged(Me, New cPieChartItemChangedEventArgs(item, index, ItemOperation._Set)) 'Event: Item gesetzt
    31. End Sub
    32. Protected Overrides Sub ClearItems()
    33. For Each i In Items 'Alle Items durchgehen
    34. RemoveHandler i.PropertyChanged, AddressOf OnPropertyChanged '... und die Events entfernen
    35. Next
    36. MyBase.ClearItems() 'Items entfernen
    37. RaiseEvent ItemsCleared(Me, New EventArgs) 'Event: Alle Items entfernt
    38. End Sub
    39. Public Sub New() 'Sub New
    40. End Sub
    41. End Class


    und das cPieChartItem:

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Drawing
    3. Public Class cPieChartItem
    4. Private mColor As Color = Drawing.Color.Red 'Farbe
    5. Private mText As String = "cPieChartItem" 'Text des Kreisstückes für die Legende
    6. Private mValue As Single = 90 'Wert
    7. Friend Event PropertyChanged(ByVal sender As Object, ByVal e As EventArgs) 'Event welches ausgelöst wird, wenn eine Property verändert wird
    8. Public Property Color As Color
    9. Get
    10. Return mColor 'Farbe zurückgeben
    11. End Get
    12. Set(ByVal Value As Color)
    13. If mColor <> Value Then
    14. mColor = Value 'Farbe setzten &
    15. RaiseEvent PropertyChanged(Me, New EventArgs) '...Event
    16. End If
    17. End Set
    18. End Property
    19. Public Property Text As String
    20. Get
    21. Return mText 'Text zurückgeben
    22. End Get
    23. Set(ByVal Value As String)
    24. If mText <> Value Then
    25. mText = Value 'Text setzten &
    26. RaiseEvent PropertyChanged(Me, New EventArgs) '...Event
    27. End If
    28. End Set
    29. End Property
    30. Public Property Value As Single
    31. Get
    32. Return mValue 'Wert zurückgeben
    33. End Get
    34. Set(ByVal Value As Single)
    35. If mValue <> Value Then
    36. mValue = Value 'Wert setzten &
    37. RaiseEvent PropertyChanged(Me, New EventArgs) '...Event
    38. End If
    39. End Set
    40. End Property
    41. Public Sub New(ByVal _Color As Color, ByVal _Text As String, ByVal _Value As Single) 'Sub New
    42. Color = _Color
    43. Text = _Text
    44. Value = _Value
    45. End Sub
    46. Public Sub New() 'Sub New ohne Args
    47. End Sub
    48. End Class


    Sowie die cPieChartItemEventArgs:

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class cPieChartItemChangedEventArgs
    3. Inherits EventArgs
    4. Private mItem As cPieChartItem
    5. Private mIndex As Integer
    6. Private mOperation As cPieChartList.ItemOperation
    7. Public ReadOnly Property Items As cPieChartItem
    8. Get
    9. Return mItem
    10. End Get
    11. End Property
    12. Public ReadOnly Property Index As Integer
    13. Get
    14. Return mIndex
    15. End Get
    16. End Property
    17. Public ReadOnly Property Operation As cPieChartList.ItemOperation
    18. Get
    19. Return mOperation
    20. End Get
    21. End Property
    22. Public Sub New(ByVal _Item As cPieChartItem, ByVal _Index As Integer, ByVal _Operation As cPieChartList.ItemOperation)
    23. mItem = _Item
    24. mIndex = _Index
    25. mOperation = _Operation
    26. End Sub
    27. End Class




    Nochmal Vielen Dank an Blaze!
    Bilder
    • piechart.PNG

      35,34 kB, 773×300, 80 mal angesehen
    @tom87 Das mit dem AddHandler versteh ich nicht ganz. Wieso machst du das?
    So klappt es doch schon.
    Nebenbei sollte der Klassename mit einem Großbuchstaben beginnen. (Macht man in .NET halt so :) )

    Code

    VB.NET-Quellcode

    1. Public Class cPieChartList
    2. Inherits System.Collections.ObjectModel.Collection(Of cPieChartItem) 'Klasse erben
    3. Friend Event ItemChanged(ByVal sender As Object, ByVal e As cPieChartItemChangedEventArgs) 'Meine kleinen...
    4. Friend Event ItemsCleared(ByVal sender As Object, ByVal e As EventArgs) '... beiden Events
    5. Public Enum ItemOperation
    6. Insert = 1
    7. Remove = 2
    8. _Set = 3
    9. ItemProperty = 4
    10. End Enum
    11. Protected Overrides Sub InsertItem(index As Integer, item As cPieChartItem) 'Neues Item...
    12. MyBase.InsertItem(index, item) 'Item hinzufügen
    13. RaiseEvent ItemChanged(Me, New cPieChartItemChangedEventArgs(item, index, ItemOperation.Insert)) 'Event: Item hinzugefügt
    14. End Sub
    15. Protected Overrides Sub RemoveItem(index As Integer) 'Item entfernen...
    16. MyBase.RemoveItem(index) 'Item entfernen
    17. RaiseEvent ItemChanged(Me, New cPieChartItemChangedEventArgs(Nothing, index, ItemOperation.Remove)) 'Event: Item entfernt
    18. End Sub
    19. Protected Overrides Sub SetItem(index As Integer, item As cPieChartItem) 'Item setzten...
    20. MyBase.SetItem(index, item) 'Item setzten
    21. RaiseEvent ItemChanged(Me, New cPieChartItemChangedEventArgs(item, index, ItemOperation._Set)) 'Event: Item gesetzt
    22. End Sub
    23. Protected Overrides Sub ClearItems()
    24. MyBase.ClearItems() 'Items entfernen
    25. RaiseEvent ItemsCleared(Me, New EventArgs) 'Event: Alle Items entfernt
    26. End Sub
    27. End Class