Laden...

DataGridView: Child-Control des DGVs ruckelt beim Scrollen

Erstellt von inva vor 15 Jahren Letzter Beitrag vor 14 Jahren 4.609 Views
I
inva Themenstarter:in
37 Beiträge seit 2007
vor 15 Jahren
DataGridView: Child-Control des DGVs ruckelt beim Scrollen

Hallo Liebe Community,
Kurz die Vorgeschichte:
Ich habe ein DataGridView im Hintergrund. Hier werden, wer hätte es gedacht, Daten angezeigt. Nun habe ich über dem DataGridView ein 2. controll eingeblendet, welches als das DataGridView iim Hintergrund als Parent hat. Das 2. controll hat eine "fixe" position im DataGridView, es soll sich also beim scrollen mit hoch oder runter bewegen.
Das funktioniert auch alles soweit.
Nun habe ich aber folgendes Problem. Beim Scrollen zieht nun das 2. controll nach. Es bewegt sich also nicht flüßig wie das unterliegende DataGridView sondern bewegt sich recht hackend/ruckelig und zieht einfach nach.
Die Ursache ist wohl folgende: Beide controlls werden zu unterschiedlichen Zeiten gezeichnet, was eben zur Folge hat, dass das 2. controll später gezeichnet wird, wodurch dieser "nach ziehen" Effekt entsteht.
Eine Idee wäre, das DataGridView-Controll ab zu leiten um ein eigenes Controll zu erstellen und dessen OnPaint() methode zu überschreiben um in dieser beide Controlls zu zeichnen. Nun weiß ich aber nicht so recht wie ich das anstellen soll.

Ist meine die Idee überhaupt erfolg versprechend oder ist der Ansatz schon falsch?
prinzipiell quält mich hier zu auch noch die Frage, ob es überhaupt möglich ist 2 controlls gleichzeitig zu zeichnen bzw. zumindest, gleichzeitig auf dem Bildschirm zu aktualisieren, sodass es für den Benutzer gleichzeitig aussieht.

Grüße,
inva

Aller Wahrscheinlichkeit nach wird das Weltall von einem Rudel Irrer regiert.

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo inva,

um das zu synchronisieren ist in der Tat die beste Möglichkeit Dein 2. Control selbst zu zeichnen und das am Besten innerhalb eines abgeleiteten und überschriebenen OnPaint() Deines DGV.

Also nochmal verständlicher:

  • Dein zu zeichnendes Control so gestalten, dass es auch von außen zum Zeichnen aufgerufen werden kann (z.B. mittels einer Methode Paint(), die auch die gleichen Parameter wie das OnPaint entgegennimmt und dann vom DGV einfach weitergegeben wird)
  • Du musst das DGV ableiten und die Methode OnPaint überschreiben
  • Innerhalb der überschriebenen OnPaint kannst Du dann die Parameter weiterreichen und o.g. Methode für Dein Control aufrufen (das zeichnet sich dann selbst)
  • Abschließend rufst Du das Original-OnPaint mittels "base.OnPaint()" auf

Das sollte dann zum Ergebnis kommen, dass beide Controls (Dein DGV und Dein Control) synchron gezeichnet werden. Ist aber ungetestet.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

I
inva Themenstarter:in
37 Beiträge seit 2007
vor 15 Jahren

Hallo Norman-Timo,
Danke für deinen Post! Deine Idee klingt logisch und gut.
Aber ich bin leider nicht allzu fit in GDI Geschichten.
Daher frage ich mich, wie ich das "selbst zeichnen" im Code umsetze.
Muss ich mir dafür das Graphics object des 2. controlls besorgen und rufe dessen .Draw...() auf ?!

Wie ich die zu überschreibene OnPaint() zu realisieren habe ist mir klar, denke ich.
Aber um Missverständnisse zu vermeiden, ein kurzes Snippet:

OnPaint() vom DGV:


protected override void OnPaint(PaintEventArgs e)
{
     // call Paint from second control           
     myControll.Paint(e);

     base.OnPaint(e);
}

... wie müßte nun die Paint() im 2. controll aussehen?! Mir fehlen hierfür leider jegliche Vorstellungen, da ich mich bisher nicht/wenig mit Oberflächen und der Paint-"Struktur" von Windows beschäftigt habe.

Aller Wahrscheinlichkeit nach wird das Weltall von einem Rudel Irrer regiert.

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo inva,

Du hast in den Ereignisparametern doch schon das relevante Graphics-Objekt, schau Dir doch mal den Parameter genauer an:
MSDN - PaintEventArgs

Vor allem das "e.Graphics" 😉

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

I
inva Themenstarter:in
37 Beiträge seit 2007
vor 15 Jahren

Hey Norman-Timo,
Ich habe das schonmal so ausprobiert, aber, dann habe ich doch nur das Graphics object von DGV, in dem ist doch das 2. controll gar nicht enthalten.
Also, wenn ich es wie folgt mache.

OnPaint() des abgeleiteten DGVs überschreiben
PaintMe() des 2. Controlls aufrufen mit PaintEventArgs von OnPaint() als Parameter
OnPaint() des 2. Controlls aufrufen mit PaintEventArgs aus PaintMe() [also PaintEventArgs aus DGV]
Dann habe ich trotzdem den "schlieren-Effekt" und die stelle an der das Controll ist ist grau.

anbei mein Code


// 1. Controll (DGV)
class CMainDataGridView : DataGridView
    {
        private CDetailsDataGridView refToDetailsGrid;
        public CDetailsDataGridView DetailsGrid
        {
            set { refToDetailsGrid = value; }
            get { return refToDetailsGrid; }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            // call Paint from details DGV
            refToDetailsGrid.PaintMe(e);

            // at last, call the base OnPaint()
            base.OnPaint(e);
        }
    }

// 2. controll (auch DGV)
class CDetailsDataGridView : DataGridView
    {
        private PaintEventArgs Helper;
        public void PaintMe(PaintEventArgs e)
        {
            Helper = e;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(Helper);
        }
    }

Aller Wahrscheinlichkeit nach wird das Weltall von einem Rudel Irrer regiert.

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo inva,

ich glaube ich habe mich noch nicht 100%ig klar ausgedrückt (sorry dafür).

Meine Intension wäre in Deinem Fall, dass Du Dein 2. Control nicht als vollwertiges Control (also nicht von System.Windows.Forms.Control abgeleitet) entwickelst, sondern als "simple" Klasse, die halt eine "PaintMe()" Methode anbietet.

Dein abgeleitetes DGV hält dann als Member Variable Dein 2. Control (eine simple Referenz auf die Klasseninstanz). Bei einem OnPaint des DGV veranlässt Du dann eine Zeichnung des modifizierten DGV, indem Du Dich nur um Dein Control kümmerst, den Rest des DGV wird die Original-Basis vornehmen.

Also ist das eine Erweiterung des normalen DGV. Um es dann noch komfortabler zu machen kannst Du für den Designer noch gewisse Eigenschaften erweitern, so dass das neue DGV auch Eigenschaften des 2. Controls unterstützt, oder Du lässt die Designerfähigkeit außen vor und Du machst alles programmatisch.

Hoffe das war jetzt etwas präziser, so dass Du auch nachvollziehen kannst wie ich es meine.

Grüße
Norman-Timo

P.S.: Hab mal schnell eine VS2005 Solutions mit einem Beispiel angehängt

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo inva,

wollte noch einmal nachfragen, ob Dir mein Ansatz geholfen hat?

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

I
inva Themenstarter:in
37 Beiträge seit 2007
vor 15 Jahren

Guten Morgen Norman-Timo!

Also, deine Idee habe ich soweit verstanden. Aber nun ist mein 2. controll ja "blöderweise" ein 'echtes' controll und ich möchte ja auch mehr als einfach nur ein Rechteck malen, sondern auch den Inhalt des 2. controlls anzeigen.
Wie in meinem Beispiel gezeigt ist das 2. Controll auch ein DataGridView, ebenfalls ein eigenes abgeleitetes.

Dein Code-Beispiel brachte mich auf folgende Idee:

  • ich überschreibe die OnPaint des 2. controlls und merke mir dessen graphics objekt
  • das gemerkte graphics objekt und zeichne es im überschriebenen OnPaint() meines DataGridViews
    -> will ich das jedoch machen, bekomme ich eine OutOfMemory Exception beim scrollen (wenn also die position des 2. controlls verändert werden soll)

hier der Code (OnPaint() des DGVs):


protected override void OnPaint(PaintEventArgs e)
{
    // let the base do its paint
    base.OnPaint(e);
    // now draw the details grid over the main grid
    if (refToDetailsGrid.PaintEventArgs != null)
    {
        refToDetailsGrid.PaintEventArgs.Graphics.DrawRectangle(new Pen(Color.Black, 1), refToDetailsGrid.PaintEventArgs.ClipRectangle);
    }
}

Also ich kann mir schon denken, dass es dort kracht, aber ich weiß nicht was ich dagegen tuen soll. Vorallem, zeichnet mir DrawRectangle doch auch nur ein Rechteck und nicht die ganze Grafik. Da aber mein 2. DGV (2. controll) auch child-controlls hat (Scrollbar, rows ...) bin ich doch zwingend an die base.OnPaint() des 2. DGVs angewiesen, damit dies korrekt gezeichnet wird, oder?
Falls dies so richtig ist, bedeutet das doch, dass es nicht möglich ist beide controlls gleichzeitig zu zeichnen?!

Aber noch eine Alternative dazu, wobei die bisher nur als Idee existiert aber die Umsetzung noch fehlt bzw. das "wie" ichs umsetzen kann.
Undzwar: ich kann doch die controlls stink normal via OnPaint() zeichnen lassen unterdrücke aber den Refresh zum Bildschirm solange bis beide OnPaint's abgearbeitet sind, refreshe den Bildschirm und für den Nutzer sieht es zumindest so aus, als werden sie gleichzeitig gezeichnet. Oder ist dies so nicht realisierbar?

Grüße und ein großes Dankeschön für deine bisherige Mühe!

inva

Aller Wahrscheinlichkeit nach wird das Weltall von einem Rudel Irrer regiert.

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo inva,

ich komme heute nicht mehr dazu mich mit diesem Thema zu beschäftigen, da mich das aber auch interessiert bin ich trotzdem bemüht dem nachzugehen.

Falls ich es aber dennoch vergessen sollte, erinnere mich doch bitte daran, hier tätig zu werden (ab morgen Abend dann 😉

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo inva,

ich galube Dein Vorhaben droht zu scheitern. Ich habe wirklich einiges ausprobiert. Ich habe mich an die Windows-Messages wegen dem Scrollen gehängt und auch dort dann an das innere DGV ein "Invalidate" (auch auf Basis von Windows Messages) geschickt, und trotzdem ruckelt es bzw. zieht nach.

Ich glaube es bleibt Dir nichts anderes übrig als das Verhalten zu akzeptieren, oder ein komplett eigenes Control zu entwickeln, das alles auf einmal zeichnen kann. Also ein spezielles DGV nachprogrammieren.

Mir fällt leider keine Idee mehr ein. Ein Ansatzpunkt wäre gewesen, wenn das 2. Control direkt gezeichnet werden kann, so wie ich im Testprojekt mit dem Rahmen simuliert habe. Aber da das auch ein schon "fertiges" Control ist kaufst Du Dir eben auch alle Control-Eigenschaften mit ein. Ein Synchronisieren ist irgendwie nicht vorgesehen.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

2.760 Beiträge seit 2006
vor 14 Jahren

Es könnte meiner Meinung nach doch möglich sein ein verschachteltes Control ohne schlieren in das DGV zu zeichnen wenn man dem verschachtelten Control vorher eine WM_PRINT-Nachricht (MSDN: WM_PRINT Message) schickt und alles in ein Bitmap zeichnet welches man dann im OnPaint des DGV zeichnen könnte. Damit sollte es gehen fast jedes beliebige Control da mit reinzupacken. (Ich denke Control.DrawToBitmap() sollte in den meisten Fällen auch schon helfen)

Um die Generik zu erhöhen und einen sauberen Weg zu wählen konnte man dafür eine eigene Column schreiben.