Laden...

DataGridView neuen Eintrag farblich hervorheben

Erstellt von caldicot vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.217 Views
C
caldicot Themenstarter:in
51 Beiträge seit 2010
vor 13 Jahren
DataGridView neuen Eintrag farblich hervorheben

Hi,

ich habe ein Problem.
Ich möchte gerne, dass eine neue Zeile eines DataGridViews für x Sekunden farblich hervorgehoben wird.
Ich weiss nicht so recht wie ich das anstellen kann.

Das DataGridView hat ja ein RowsAdded Event.
Das würde sich ja hier anbieten?

Für jeden neuen Eintrag benötige ich einen eigenen Timer der z.B. nach 5s tickt, weil ja jede neue Zeile individuell behandelt werden soll?!
Wie kann ich jetzt beim Tick des Timers genau auf diese eine Zeile zugreifen?
Die neuste Zeile ist es ja möglicherweise nicht, weil während dieser 5s schon wieder eine Neue dazu gekommen ist.
Kann ich das den EventArgs mitgeben?
Oder übergibt man sowas z.B. in das Timer.Tag Attribut?
Die DataGridViewRowsAddedEventArgs haben ja die Eigenschaft RowIndex.
Wobei, wenn eine neue Zeile hinzugefügt wird, der RowIndex ja verfäscht wird?

Ich habe mehrere DataGridViews und möchte bei jedem die neuen Zeilen hervorheben.
Woher weiss ich in der Tick Methode des Timers, welches DGV Auslöser war?
Oder muss ich für 3 DGVs alles 3x bauen?

Oder wäre es besser ein neu Zeichnen des DGV mittels Timer alle x Sekunden auszulösen und in dem Cell Painting Event die Zeitdifferenz prüfen und dann ggf. farblich hervorheben.

Ich hoffe ihr versteht mein Problem.

Danke für Eure Hilfe
caldi

5.299 Beiträge seit 2008
vor 13 Jahren

Der Ansatz mit dem _CellPaint-Event scheint mir der richtige, nur die Frage, wie weiß die Zelle, wie alt sie ist - da hat man mehrere Optionen.

Ich binde meine Daten ja immer über eine BindingSource ans DGV, da würde sich das _ListChanged der BindingSource anbieten.

Frage ist auch, ob deine Daten einen Timestamp besitzen, den man verwenden kann, um zu gucken, welche Farbe fällig ist.

Natürlich kann man auch per Timer alle halbe Sekunde alle DGVs mit DGV.Invalidate() zum Neuzeichnen anstoßen - aber das könnte flackern.

IMO ist das schlankeste, wenn im BindingSource_ListChanged bei ListChangeType.Add das Item in ein Dictionary<T, DateTime> gesteckt wird.

Dann läuft ein Timer (für alle) die Dictionary-Einträge durch, und wirft die zu alten wieder raus. Beim Rauswerfen könnte man auch BindingSource.ResetBinding(Item) aufrufen, damit das Grid genau diesen Datensatz neu zeichnet (nämlich dann wieder in der Standard-Farbe).

Im PaintEvent wird dann geguckt, ob der zu zeichnende Wert im Dictionary auftaucht, und dementsprechend Farbe.

Der frühe Apfel fängt den Wurm.

C
caldicot Themenstarter:in
51 Beiträge seit 2010
vor 13 Jahren

Hallo,

danke für Deine Antwort.
Die Idee finde ich gut. Leider klappt das bei mir noch nicht so ganz:

Den ListChangeType.ItemAdd erhalte ich nie. Nur Reset.
Meine Server Applikation benachrichtigt alle Clients, wenn ein neuer Eintrag vorhanden ist.
Die Clients holen sich dann diesen in Form einer DataTable (mit nur 1 Eintrag; dem Neusten) und mergen das Table dann in das lokale DataSet.

Nachdem ich aber ohnehin ein Event habe, das mich über einen neuen Eintrag informiert und ich diesen dann abhole, kann ich den neuen Eintrag ja auch hier abfangen und in ein Dictionary schreiben.
Das klappt auch.
Mein Problem ist jetzt, dass ich bei der Timer Methode, das ja das Alter prüft und dann die zu alten Einträge aus dem Dictionary entfernt, nicht weiss, wie ich nur diesen einen Datensatz neuzeichnen lassen kann. Das würde mir nämlich gut gefallen, dass nicht dass ganze DGV gezeichnet werden muss, sondern eben nur die paar neuen Einträge.
Kann ich da irgendwie eine Beziehung herstellen von einer DataRow zu der entsprechenden Zeile?

Ich habe das so versucht:
Beim eintreten des neuen Eintrag-Events wird die DataRow in eine List<DataRow> geschrieben.
Der Timer tickt alle 500ms.

Die Timer Methode sieht so aus:


        private void neuerEintragTimer_Tick(object sender, EventArgs e)
        {
            foreach (DataRow dr in this.newStatus)
            {
                DateTime time = (DateTime)dr["Zeit"];
                if (time.AddSeconds(5) > DateTime.Now)
                {
                    this.newStatus.Remove(dr);
                    int index = this._localDataSet.Status.Rows.IndexOf(dr);
                    this.StatusBinding.ResetItem(index);
                }
            }
        }

Allerdings ist der index den ich erhalte immer um 1 größer als die Anzahl der BindingSource.
Das verstehe ich nicht ganz, weil der gefundene Index ziemlich klein sein sollte und die BindingSource gar nicht weniger Elemente enthalten kann als das DataSet.
Das DataSet.Status ist ja die DataSource dieser BindingSource.
Finde ich sehr merkwürdig?
Habe ich was falsch gemacht?

Danke
caldi

5.299 Beiträge seit 2008
vor 13 Jahren

Die Indicees der DataTable können durchaus anders sein, als die Indicees des DGV. Du solltest die Item aus der BindingSource nehmen, nicht aus dem Dataset.
ZB. sind auch gelöschte DataRows noch in der DataTable vorhanden.

Ich wundere micht, wieso du ListChangeType.Reset erhälst, aber nicht .Add. Tauschst du bei jedem Mergen die gesamte DataTable aus, oder wie kommt das zustande?

Der frühe Apfel fängt den Wurm.

C
caldicot Themenstarter:in
51 Beiträge seit 2010
vor 13 Jahren

Hi,

ich tausche eigtl. nicht das ganze DataTable aus. (oder mir ist es nicht bewusst)

hier die Methode, die den neusten Eintrag vom Server lädt:


        private void LoadLatestStatusFromServer()
        {
            // Wenn der Aufruf nicht aus dem GUI-Thread kommt ...
            if (InvokeRequired)
                // Aufruf an den GUI-Thread delegieren
                Invoke(new Action(LoadLatestStatusFromServer));
            else
            {
                // Statustabelle vom Server abrufen
                DataSet.StatusDataTable table = AppServer.Service.GetLatestStatus();

                // Vom Server abgerufene Statusdaten ins lokale DataSet laden
                this._localDataSet.Status.Merge(table);
            }
        }

Die Tabelle wird aus mit dieser Methode nie verändert. Daher finde ich es komisch, wenn die Indices nicht übereinstimmen?!

EDIT:
Also ich habe das jetzt so gelöst:
Bei dem Event, mit dem der Server den Client benachrichtigt, dass ein neues Element vorhanden ist, schreibe ich das neue Element in eine Liste (die DataRow).
In einem Timer rufe ich die List.RemoveAll() Methode auf.

Das Prädikat, das ich der RemoveAll() Methode übergebe prüft, ob die die Zeile älter als x Sekunden ist (ich speichere ein DateTime in der Zeile).
Dann suche ich die Zeile in der BindingSource anhand der Zeit und Resete dann die Zeile der BindingSource.
Dann wird eben True bzw. False zurückgegeben, damit es ein Prädikat ist.

In der Paint Cell Methode prüfe ich natürlich, ob das zu zeichnende Objekt in der Liste steht und wähle dann eine andere Farbe.

Ich hoffe, das ist keine übermäßig unsaubere Lösung 😃

Danke für Deine Hilfe, das hat mich auf die richtige Fährte gebracht ! 😃

Viele Grüße
caldi