Splitter rastern/einrasten lassen



  • Hallo,

    auf einem Panel habe ich mehrere RichEdits aufgereiht mit jeweils einem Splitter dazwischen. Die RichEdits symbolisieren einzelne Module, die Breite (der RichEdits) eine bestimmte Zeitdauer. Diese Zeitdauer ist per Splitter veränderbar.

    Für meine Zwecke ist es nun erforderlich, die Zeitdauer zu rastern, z.B. 4 Sek.-Raster, 5-Sek.-Raster etc.

    In den TSplitter-Ereignissen OnCanResize oder OnMove kann ich Splitter->Left nicht wie gewünscht setzen bzw. ohne das gewünschte Ergebnis. Auch gibt es leider kein Ereignis MouseUp oder OnExit, wo Splitter->Left setzen könnte.

    Hat jemand eine Idee?!

    Gruß
    Leo



  • Klingt nach dem falschen Werkzeug. Warum nimmst du da RichEdit-Controls?

    Du kannst vermutlich eine neue Klasse einführen, die von TSplitter ableitet und die Verschiebe-Events granuliert. Aber ich würde eher über selbstgezeichnete Controls nachdenken, wenn es nicht einen sehr guten Grund für die RichEdit-Felder gibt.



  • audacia schrieb:

    Klingt nach dem falschen Werkzeug. Warum nimmst du da RichEdit-Controls?

    Die bieten gute Darstellungsmöglichkeiten (Schrift, Farbe etc.)

    audacia schrieb:

    Du kannst vermutlich eine neue Klasse einführen, die von TSplitter ableitet und die Verschiebe-Events granuliert. Aber ich würde eher über selbstgezeichnete Controls nachdenken, wenn es nicht einen sehr guten Grund für die RichEdit-Felder gibt.

    Kannst Du genauer sagen, wie das mit den 'selbstgezeichneten Controls' gemeint ist?

    Gruß
    Leo



  • Ich will's dir ja auch nicht ausreden, ohne die Details zu kennen. Vielleicht hast du einen Screenshot, damit man sich besser vorstellen kann, wie du die benutzt?

    Mit selbstgezeichneten Controls meine ich, daß du selbst eine Komponente erstellst, die das Zeichnen des Zeitplans auf der Grundlage einer deinen Bedürftnissen entsprechenden Datenstruktur übernimmt und die Maussteuerung selbst implementiert. Dann bist du völlig frei darin, wie du das Einrasten implementierst.



  • audacia schrieb:

    Ich will's dir ja auch nicht ausreden, ohne die Details zu kennen. Vielleicht hast du einen Screenshot, damit man sich besser vorstellen kann, wie du die benutzt?

    Hier findest Du den bisherigen Stand der Dinge:
    http://www.eoleo.de/cpp/RichEdit_Splitter_Beispiel.swf.html

    Gruß
    Leo



  • Ist schon ein bisschen länger her, seit der Thread eröffnet wurde.
    Aber hiermit zeige ich dir eine Möglichkeit auf, in der sich dein Vorhaben realisieren lässt.

    void __fastcall TForm1::Splitter1CanResize(TObject *Sender, int &NewSize, bool &Accept)
    {
    	if (abs(NewSize - Splitter1->Top) <= 20)
    		Accept = false;
    	else
    		NewSize = (NewSize < Splitter1->Top) ? Splitter1->Top - 40 : Splitter1->Top + 40;
    }
    //---------------------------------------------------------------------------
    

    In diesem Beispiel wurde der Splitter Horizontal und nicht wie bei dir Vertikal verwendet.
    Du musst nur das Event CanResize abfangen und dann eine kleine Abfrage einbauen.

    Als Erstes prüfst du, ob deine Schwelle für die erlaubte Verschiebung erreicht wurde.
    Ist dies nicht der Fall, so Akzeptierst du die Änderungen nicht und wenn alles in Ordnung ist, setzt du die neue Position mit deinem gewünschten Intervall (Hier 40)

    Im untenstehenden Link findest du das kleine Beispiel mit dem kompilierten Programm.
    !! Es funktioniert auch, wenn du den Splitter auf LiveUpdate eingestellt hast.

    Ich hoffe ich konnte dir helfen.
    Splitter-Beispiel



  • Hi Deforation,

    super! Und das mit sowenig Code, ich probier's gleich aus ..

    (ne gute Stunde später)

    ... funktioniert bei mir leider nur mit 'einem' Splitter, dessen oberer Nachbar auf ...->Top gleich 0 gesetzt ist. Aber schon beim zweiten Splitter funktioniert die NewSize-Logik nicht mehr. Der Wert steigt schon bei minimaler Mausbewegung in die Zehntausende.

    Das liegt nach meinem Verständnis daran, dass der 2. und alle folgenden Splitter keinen verlässlichen Bezugspunkt (Top = 0) haben, zudem der NewSize-Wert in Bezug gesetzt werden kann.

    Wenn dagegen auf das OnMouseDown und OnMouseUp-Ereignis zugreifbar wäre, könnte bei MouseDown die Position gemerkt und bei MouseUp aus gemerkter und aktueller der gewünschte Rasterpunkt ermittelt werden.

    Hier wäre ich für ein winzigkleines Anschauungsbeispiel überaus dankbar!

    Viele Grüße
    Leo



  • Hallo Leo Freitag

    Bei deinem Beispiel sind die Splitter ja Vertikal ausgerichtet.
    Funktioniert es auch nicht, wenn du den anstelle des Top-Wertes den Left-Wert nimmst, da sich ja dieser mit der Verschiebung ändert?

    UPDATE:
    So habe es jetzt mal selbst getestet und muss sagen, dass es wirklich nicht funktioniert.

    Das Problem liegt darin, dass in NewSize die Grösse des linken TPanel-Objekts steht und in der Left Eigenschaft des Splitters dessen effektive Position von links aus (somit also dem Formularrand).

    Ich habe jetzt nochmals was erarbeitet und nachfolgendes funktioniert für alle fälle, auch wenn mehrere Splitter nebeneinander sind und mehrere Panels abtrennen.

    void __fastcall TForm1::Splitter2CanResize(TObject *Sender, int &NewSize, bool &Accept)
    {
    	//Position des Cursors ermitteln (Koordinaten innerhalb des Monitors)
    	POINT ptMousePos = Mouse->CursorPos;
    
    	//Position in Bezug auf die Clientanwendung umrechnen lassen
    	::ScreenToClient(dynamic_cast<TSplitter*>(Sender)->Parent->Handle, &ptMousePos);
    
    	//Prüfung mittels Intervallschritten von 40 Pixel
    	if (abs(ptMousePos.x - dynamic_cast<TSplitter*>(Sender)->Left) <= 40)
    		Accept = false;
    }
    //---------------------------------------------------------------------------
    

    Dein Tipp mit der Mausposition war sehr hilfreich und lässt sich äußerst elegant lösen.
    Den zweiten Teil der Abrage kann man auch gleich weg lassen, war sowieso unnötig.

    Bin auf eine Rückmeldung ob es nun funktioniert gespannt.

    Hier noch der Link zum neuen <Splitter_Test Version 2>



  • Hi Deforation,

    danke für Deine Energie. Ja, die Rasterung wirkt jetzt auf alle Splitter, prima!

    Ich lasse mir die wesentlichen Positions- und Größeninformationen in einer ListBox (ListBox_Debug) anzeigen. Oh je, hier zeigt sich, dass die Rasterung leider sehr ungenau ist. Bei Hin- und Herschieben eines Splitters werden die Werte sehr schnell ungenau. (Habe Deinen Code um das Edit 'txt_Raster' und 'ListBox_Debug' ergänzt):

    int iRaster = StrToInt(txt_Raster->Text);
        //Position des Cursors ermitteln (Koordinaten innerhalb des Monitors)
        POINT ptMousePos = Mouse->CursorPos;
    
        //Position in Bezug auf die Clientanwendung umrechnen lassen
        ::ScreenToClient(dynamic_cast<TSplitter*>(Sender)->Parent->Handle, &ptMousePos);
    
        //Prüfung mittels Intervallschritten von iRaster (40) Pixel
        if(abs(ptMousePos.x - dynamic_cast<TSplitter*>(Sender)->Left) <= iRaster){
            Accept = false;
        }else{
            ListBox_Debug->Items->Add("*");
            ListBox_Debug->Items->Add("abs: " + IntToStr(abs(ptMousePos.x - dynamic_cast<TSplitter*>(Sender)->Left)));
            ListBox_Debug->Items->Add("Sp.-Left: " + IntToStr(dynamic_cast<TSplitter*>(Sender)->Left));
            ListBox_Debug->Items->Add("NewSize: " + IntToStr(NewSize));
            ListBox_Debug->ItemIndex = ListBox_Debug->Count-1;
        }
    

    Ich denke, es lässt sich nur solide realisieren, wenn beim 'Betreten' und 'Verlassen' des Splitters ein fester Bezugswert ermittelt und zwischengespeichert wird. Also entweder OnEnter/OnExit oder OnMouseDown/OnMouseUp.

    Gruß
    Leo



  • Ich habe mal deine Debug-Box implementiert.
    Leider kann ich dein Problem nachvollziehen. Ich kann derzeit nicht verstehen, weshalb die Grössen beim Hin- und Herschieben so variieren.

    Mal schauen, vielleicht fällt mir ausser der Möglichkeit, alle Werte zwischen zu speichern noch etwas anderes ein.



  • Problem gelöst!
    Nicht in jedem Punkt elegant, aber es funktioniert.

    Ich bin dazu auf die ursprüngliche Idee zurückgekommen und habe von TSplitter eine Komponente TLeoSplitter abgeleitet und diese um die Ereignisse OnMouseDown und OnMouseUp erweitert. Benötigt habe ich schließlich nur OnMouseUp.

    Das Ableiten war schon ein großes, mühsames Suchspiel - ich lerne besser am konkreten Beispiel als an allgemeinen Beschreibungen. Wie oft habe ich mir einen Jemand aus Fleisch und Blut gewünscht, den ich mit Fragen hätte löchern können. Schwer zu finden, aber ich werd' die Suche nicht aufgeben.

    Aber schließlich war sie fertig, meine erste Komponente! Sieht im Nachhinein recht banal aus, aber der Weg dahin war's überhaupt nicht.
    Geholfen haben mir dabei hauptlächlich:
    - das Tutorial Komponentenentwicklung http://bcb-tutorial.c-plusplus.net/komponentenentwicklung/komponenten1.html
    - http://w3processing.com/index.php?subMenuItemId=235
    - sowie als Anschauungsbeispiel die Unit extctrls.pas/extctrls.hpp

    Quellcode und Beispielprogramm für die abgeleitete Komponente TLeoSplitter und die Demoanwendung LeoSplitter-Demo gibt's hier (besonders für die, die besser am Beispiel lernen 😉 ):
    http://www.eoleo.de/cpp/LeoSplitter-Demo.zip
    http://www.eoleo.de/cpp/LeoSplitter-Komponente.zip

    Anregungen und Kritik? Gerne.
    Gruß
    Leo



  • Wow

    Vielen Dank, dafür dass du dies auch noch zur Verfügung stellt.
    Werde mir deine Lösung sobald ich Zeit finde mal anschauen.

    ("\(°.°)/")


Log in to reply