WatchList mit DataGridView erstellen

  • C#
  • .NET (FX) 4.5–4.8

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

    WatchList mit DataGridView erstellen

    Hallo,
    ich habe Daten die aus einem NetworkStream ausgelesen werden (in einem anderen Thread). Jetzt würde ich gerne eine Liste haben, in der der Nutzer eine Zieladresse, Datenlänge, Darstellungstyp etc auswählen kann, und die Daten dann angezeigt bekommt. Diese Anzeige müsste ich dann ca alle 0.5s oder jede Sekunde aktualisieren. Erstmal habe ich mir Folgendes gedacht:
    Spoiler anzeigen

    C#-Quellcode

    1. public enum DisplayType
    2. {
    3. DisplayHex,
    4. DisplayDec,
    5. DisplayDecSigned,
    6. DisplayFloat
    7. }
    8. public enum DataAddress
    9. {
    10. None,
    11. DB1,
    12. DB2,
    13. DB3,
    14. DB44 // etc
    15. }
    16. public class Variable
    17. {
    18. private const string invalid = "N/A";
    19. private byte[] buffer = new byte[32];
    20. public DataAddress Address { get; set; }
    21. public uint Offset { get; set; }
    22. public uint DataSize { get; set; }
    23. public byte Bit { get; set; }
    24. public DisplayType DisplayType { get; set; }
    25. public string DisplayedValue
    26. {
    27. get
    28. {
    29. if (Address == DataAddress.None)
    30. return invalid;
    31. if (DataSize == 0 || Offset + DataSize > 32 || DataSize > 4 || DataSize == 3)
    32. return invalid;
    33. switch(DisplayType)
    34. {
    35. case DisplayType.DisplayHex:
    36. {
    37. var num = BitConverter.ToUInt32(buffer, (int)Offset);
    38. return num.ToString("X" + DataSize * 2);
    39. }
    40. case DisplayType.DisplayDec:
    41. return GetDecRepresentation();
    42. case DisplayType.DisplayDecSigned:
    43. return GetDecSRepresentation();
    44. case DisplayType.DisplayFloat:
    45. {
    46. if (DataSize != 4)
    47. return invalid;
    48. return BitConverter.ToSingle(buffer, (int)Offset).ToString();
    49. }
    50. }
    51. return invalid;
    52. }
    53. }
    54. private unsafe void ReadData()
    55. { // Zeugs zum Daten kopieren
    56. }
    57. private string GetDecRepresentation()
    58. {
    59. }
    60. private string GetDecSRepresentation()
    61. {
    62. }
    63. }

    Ich hab jetzt eine ​BindingList<Variable>. Jetzt zu den Fragen:

    Wie binde ich die Liste so ans DGV, dass ich eine Darstellung wie im Bild erhalte?
    Wie binde ich so, dass der Nutzer Items zur Liste hinzufügen und löschen kann?
    Wie aktualisiere ich die Anzeige der Daten?

    Ich hab leider nicht oft mit dem DGV gearbeitet, vielleicht habt ihr ja auch ein paar Links für die Basics.

    Grüße
    Bilder
    • dgv.png

      4,64 kB, 635×160, 111 mal angesehen
    Ich hab mal etwas herumprobiert. Grundsätzlich klappt das mit dem DataSet. Wenn ich aber einfach die BindingList als DataSource angebe komm ich zum gleichen Ergebnis (ohne extra ein DataSet anzulegen). Damit die Enums zu einer ComboboxColumn werden muss ich die Spalten händisch hinzufügen.

    C#-Quellcode

    1. BindingList<Variable> data = new BindingList<Variable>();
    2. public WatchList()
    3. {
    4. InitializeComponent();
    5. dgMain.AutoGenerateColumns = false;
    6. for (int i = 0; i < 5; ++i)
    7. data.Add(new Variable());
    8. dgMain.DataSource = data;
    9. dgMain.Columns.Add(CreateComboColumn(typeof(DataAddress), "Address", "Adresse"));
    10. dgMain.Columns.Add(new DataGridViewTextBoxColumn() { Name = "Offset", DataPropertyName = "Offset" });
    11. dgMain.Columns.Add(CreateComboColumn(typeof(DataSize), "Size", "Länge"));
    12. dgMain.Columns.Add(CreateComboColumn(typeof(BitNumber), "Bit", "Bit"));
    13. dgMain.Columns.Add(CreateComboColumn(typeof(DisplayType), "DisplayType", "Anzeige"));
    14. dgMain.Columns.Add(new DataGridViewTextBoxColumn() { Name = "Wert", DataPropertyName = "DisplayedValue", ReadOnly = true});
    15. }
    16. public DataGridViewComboBoxColumn CreateComboColumn(Type enumType, string name, string displayName)
    17. {
    18. var column = new DataGridViewComboBoxColumn();
    19. column.DataSource = Enum.GetValues(enumType);
    20. column.DataPropertyName = name;
    21. column.Name = displayName;
    22. return column;
    23. }

    Das kanner wahrscheinlich nicht automatisch oder?
    Gibt es vielleicht eine Möglichkeit für die Enum Values alternative Namen anzeigen zu lassen? Bspw. bei enum Test { Test2 } anstatt "Test2", "2ter Test" anzuzeigen. Ich weiß, das ich bei Properties das DisplayName Attribut nutzen kann. Geht halt bei Enums nicht.
    Komisch, dass Dataset dich nicht überzeugen kann.
    Weil was du da grade machst ist das Rad neu erfinden.
    Am Ende kommt ein Sammelsurium von klassen heraus, die insgesamt auch nix anners machen, als ein gut gestyltes typisiertes Dataset machen täte.
    Nur dass ein Dataset eben doch noch wesentlich leistungsfähiger ist.
    Insbesondere kannst du das komplette Code-Gehampel mit dem DGV unterlassen, weil bei typDataset würde das DGV automatisch geeignete Spalten annehmen, die man b.Bed nur noch im Form-Designer modifizieren bräuchte.

    Aber jedem dasseine.

    Für was ähnliches hab ich ein eigenes Attribut erfunden, und bestimmt, dass man es auch Enums und Enum-Membern zufügen kann. Darüber löse ich bei uns letztendlich das Problem, mehrsprachige Enum-Betextungen bereitzustellen.
    CodeSample aber grad nicht parat.
    Aber prinzipiell war von Attribute zu erben, und die Bestimmung, wo das neue Attribut anwendbar, wurde selbst per Attribut gesteuert, nämlich [AttributeUsage]

    Ach Frage noch: Das Hinzufügen von Datensätzen durch Einträge in die unterste Zeile des DGVs - geht das bei deiner BindingList-Konstruktion?
    (Wenn ich mich recht erinnere, war das nämlich nur durch einige Zusatz-Umständlichkeiten möglich.)
    Also dieses Gehampel (manuell Spalten hinzufügen) mach ich eigentlich nur damit er für die Enums ne ComboboxColumn nimmt. Wenn AutoGenerateColumns auf true ist und ich setze die BindingList als DataSource, dann packt er mir auch alle Spalten automatisch rein (nur für die Enums nimmt er ne TextboxColumn). Bei dem DataSet kann ich anscheinend gar keine eigenen Typen angeben.

    Wie sag ich dem DGV denn dass es das Attribut anzeigen soll anstatt .ToString() zu benutzen?

    Jo das Hinzufügen klappt. Die Liste ist ja gebunden. Andersherum wenn ich BindingList.Clear() mache ist das DGV auch leer. Also eigentlich so wie es sein soll.

    Gonger96 schrieb:

    Bei dem DataSet kann ich anscheinend gar keine eigenen Typen angeben.
    Stimmt - Man hat einen Satz Primitiver Typen, und setzt die nach Bedarf zusammen.
    Ein Enum würde man durch eine eigene kleine Tabelle ersetzen, in die man eine feste Anzahl Datensätze generiert - dassis tatsächlich umständlich.
    Na, vlt. auch nicht, weil dabei entfällt das Problem, die "Enum-Member" zu betexten - eine "Enum-Tabelle" hätte dafür einfach eine Spalte.

    Zum Enum im DGV: Bei uns hat eine Datensatz-Klasse, wenn sie ein Enum-Feld aufweist, auch immer eine zugeordnete Readonly Display-Property, die aus dem Enum den Text extrahiert.
    Gute Idee, für eine TextboxColumn ists ganz easy. Bei einer ComboboxColumn etwas mühselig, aber es klappt. Ich hab mal ein Projekt angehängt.
    Dateien
    • DGVExample.zip

      (12,25 kB, 83 mal heruntergeladen, zuletzt: )