Forms auf Y Achse verschieben - was könnte ich besser machen ?



  • Moin zusammen ...

    Eine Hauptform auf welcher verschiedene Unterforms (Panels) verschoben werden sollen - via DragnDrop - und nur auf der Y Achse. Nach langem hin und her bin ich meinem Ziel schon nahe. Nur ein kleines Problem will einfach nicht gelöst werden.

    Hier erst mal der Code:

    private: System::Void panelklasse_MouseMove(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) 
    			 {
    			 mousemoveevent (e);
    			 }
    
    void Sidebar::panelklasse::mousemoveevent (System::Windows::Forms::MouseEventArgs^ e)
    	{
    	if (movemodus != 1) return;
    
    	if (e->Button == ::MouseButtons::Left)
    		 {
    		 Point mousePos = Control::MousePosition;
    		 mousePos.Offset(mouseposition.X, mouseposition.Y);
    
    		 // <-- Panel über die grenzen der Sidebar hinaus ?
    		 if (mousePos.Y < 0) mousePos.Y = 0;
    		 else if (mousePos.Y > form1instanz->Size.Height - this->Size.Height) mousePos.Y = form1instanz->Size.Height - this->Size.Height;
    
    		 checknewposition (mousePos);
    
    		 this->Location = System::Drawing::Point (this->Location.X, mousePos.Y);
    		 }
    	}
    
    void Sidebar::panelklasse::checknewposition (Point aktuellemousePos)
    	{	
    	Point neueformposition;
    
    	Form^ form;
    
    	for (int x = 0; x < offeneplugins; x++)																							
    		{				
    		form = safe_cast<Form^>(geladeneplugins[x]);
    
    		if ( (aktuellemousePos.Y > form->Location.Y) && (aktuellemousePos.Y < form->Location.Y + form->Size.Height) )						
    			{
    			neueformposition = System::Drawing::Point (form->Location.X, aktuellemousePos.Y + this->Size.Height); 
    
    			if (neueformposition.Y < 0) neueformposition.Y = 0;
    			else if (neueformposition.Y > form1instanz->Size.Height - form->Size.Height) neueformposition.Y = form1instanz->Size.Height - form->Size.Height;
    
    			form->Location = neueformposition;			
    			break;
    			}	
    
    		if ( (aktuellemousePos.Y + this->Size.Height > form->Location.Y) && (aktuellemousePos.Y + this->Size.Height < form->Location.Y + form->Size.Height) )
    			{
    			neueformposition = System::Drawing::Point (form->Location.X, aktuellemousePos.Y + this->Size.Height); 
    
    			if (neueformposition.Y < 0) neueformposition.Y = 0;
    			else if (neueformposition.Y > form1instanz->Size.Height - form->Size.Height) neueformposition.Y = form1instanz->Size.Height - form->Size.Height;
    
    			form->Location = neueformposition;			
    			break;
    			}
    		}	
    	}
    

    Wenn nun eine Form verschoben werden soll, wird in der Funktion CHECKNEWPOSITION überprüft, ob die Koordinaten schon von einem anderen Panel belegt sind. Ist dies der Fall, soll das Panel - welches überlagert worden wäre - unter die aktuelle Maus Position springen. Soweit klappt das auch - wenn ich das Panel auf der Y Achse nach Oben ziehe. Ziehe ich das Panel nach unten, wird das ggf. darunterliegende jedoch mit verschoben - haftet also an.

    Ausserdem erscheint beim ziehen einer Form diese immer für einen kurzen Augenblick doppelt - einmal wie gewünscht an der aktuellen Position, und einmal eine Position darunter.

    Bin auf eure Kritik und Tips gespannt.



  • Kritik habe ich keine nur Tips:

    • Die aktuelle Position der Maus würde ich über e->Location oder e->Y abfragen.
    • Zur Sicherheit würde ich alle Plugins überprüfen und zwar auf ->Visible und als Control und nicht als Form.
    • Anstatt der schwierig zu managende offenePlugins geladenePlugins würde ich eine generische Liste emfehlen und eine for each Schleife.
    • Außerdem scheint es mit so, dass du das Singleton-Pattern benutzt um von einem Panel eine Referenz auf die Form zu erhalten, da würde ich über form->Parent darauf zugreifen.
    • Rectangle->Contains oder ->IntersectsWith erspart dir viel Code in CheckPosition.
    • Wegen des Doppeltsehens kannst du vielleicht anstatt ->Location oder ->Top zu ändern ->SetBounds(Core)(..., ..., 0, 0, BoudsSpecific.Location) aufzufrufen.

    Aber sonst ist der Code schon gut.



  • Das hat mich fast erschlagen. Da ist noch viel neues bei:

    Plug In Array ist umgewandelt in eine List vom Typ Object und wird nun mit einer for each Schleife durchlaufen.

    Alle darin enthaltenden PlugIns werden zu Controls gecastet und auf Visible geprüft.

    Zum Singleton-Pattern: War mir bis jetzt neu - der Ausdruck. Dazu folgendes:

    Die einzelnen Panel Forms werden von einer Basisklasse abgeleitet. Im FormLoad Event dieser Basisklasse wird jetzt das Sender Object zur Liste hinzugefügt, welche ich später in der Funktion checknewposition zu Controls caste.

    Allerdings weiß ich nichts mit

    Rectangle->Contains oder ->IntersectsWith erspart dir viel Code in CheckPosition.

    anzufangen. Habe es so versucht:

    System::Drawing::Rectangle ^THISrect, CHECKrect;
    
    	THISrect = this->ClientRectangle;	
    
    	Control^ control;
    
    	for each (Object^ obj in panelliste) 
    		{
    		control = safe_cast<Control^>(obj);
    		if (control->Visible == false || control == this) continue;	
    
    		CHECKrect = control->ClientRectangle;
    
    		if ( (aktuellemousePos.Y > control->Location.Y) && (aktuellemousePos.Y < control->Location.Y + control->Size.Height) )
    			{
    

    Aber das erscheint mir nicht richtig - oder ich weiß es nicht besser :). Dadurch erhalte ich doch nicht die Größe des Panels - sondern der Controls darin - oder?

    Vielleicht zeigtst mir das mal an ein wenig Code ?



    • Sind die Plugins alle gleichgroß? Willst du soetwas machen eine Docking-IDE?
    • Die erste if-Abfrage im Anfangspost:
      if (control->Bounds->Contains(aktuellemousePos))
      Die zweite-Abfrage im Anfangspost:
      if (Rectangle(aktuellemousePos, this->Size)->IntersectsWith(control->Bounds))
      (aus dem Kopf)
    • Nimm anstatt CientRectangle Bounds
      ClientRectangle ist der Bereich den du mit Controls füllen kannst.
      (Außerdem ist ClientRectangle relativ zu Bounds->Location)
      Bounds ist der Bereich der com Steuerelement eingenommen wird.
      Durch die Padding-Eigenschaft wird ClientRectangle im Bounds ausgerichtet.

    Bei normalen Panels spielt das keine Rolle, die kein Padding haben. Forms aber aber den "Mit der Muas zieh"-Bereich.

    • Nein, das Singleton-Pattern war absolut nicht als Ratschlag gemeint. Leider wirß ich zu wenig über deine Architektur, sonst könnte ich dir besser helfen.
      Warum leitest du nicht die "Panel Forms" von Panel oder UserControl ab?
      Wie erhälst du die Plugins an sich welchen Tap haben diese?


  • shaun1981 schrieb:

    Plug In Array ist umgewandelt in eine List vom Typ Object und wird nun mit einer for each Schleife durchlaufen.

    Alle darin enthaltenden PlugIns werden zu Controls gecastet und auf Visible geprüft.

    verwende mal lieber Generics

    using namespace system:Collections::Generic;
    
    // ...
    
    List<Control^> ^panelliste = gcnew List<Control^>();
    
    // ...
    
    	for each (Control^ control in panelliste) 
    		{
    		if (control->Visible == false || control == this) continue;
    

    hand, mogel



  • Ich komme meinem Ziel dank eurer Hilfe schon näher. Hier mal der momentane Status:

    Point Sidebar::panelklasse::checknewposition (Point aktuellemousePos)
    	{	
    	Point neuecontrolposition, altecontrolposition;	
    	System::Drawing::Rectangle ^controlrectangle, ^thisrectangle;
    
    	for each (Control^ control in panelliste) // <-- alle geöffneten Panels durchlaufen
    		{
    
    		if (control->Visible == false || control == this) continue;
    
    		thisrectangle	 = gcnew System::Drawing::Rectangle (this->Location, this->Size);
    		controlrectangle = gcnew System::Drawing::Rectangle (control->Location, control->Size);
    
    		if (thisrectangle->IntersectsWith (control->Bounds)) // <-- Sobald das bewegte Panel in den Bereich eines anderen eintritt
    			{			
    			aktuellemousePos.Y = control->Location.Y;
    
    			// <-- mouserichtung ist true bei Abwärts, false bei Aufwärts ***  <-- control erhällt Location des bewegten Panels
    			if (mouserichtung == true)  neuecontrolposition = System::Drawing::Point (this->Location.X, this->Location.Y - controlrectangle->Height); 		
    			if (mouserichtung == false) neuecontrolposition = System::Drawing::Point (this->Location.X, this->Location.Y + controlrectangle->Height);
    
    			control->Location = neuecontrolposition;
    
    			// Rekrusiv überprüfen ob weitere Panels bedeckt ?
    			REKchecknewposition (control);
    			}		
    		}
    	return aktuellemousePos;
    	}
    

    Hier wäre es doch sinnvoll, nochmal alle Panels durchzugehen, um zu überprüfen ob evtl. weitere bedekt wurden. Dazu versuche ich folgendes:

    void Sidebar::panelklasse::REKchecknewposition (Control^ control)
    	{
    	Point neuecontrolposition, altecontrolposition;
    	System::Drawing::Rectangle ^checkrectangle, ^thisrectangle;
    
    	for each (Control^ checkcontrol in panelliste)
    		{
    		if (checkcontrol->Visible == false || checkcontrol == control) continue;
    
    		thisrectangle	 = gcnew System::Drawing::Rectangle (control->Location, control->Size);
    		checkrectangle	 = gcnew System::Drawing::Rectangle (checkcontrol->Location, checkcontrol->Size);
    
    		if (thisrectangle->IntersectsWith (control->Bounds)) 
    			{			
    			control->Location.Y = checkcontrol->Location.Y;			
    
    			if (mouserichtung == true)  neuecontrolposition = System::Drawing::Point (control->Location.X, control->Location.Y - checkrectangle->Height); 		
    			if (mouserichtung == false) neuecontrolposition = System::Drawing::Point (control->Location.X, control->Location.Y + checkrectangle->Height);
    
    			checkcontrol->Location = neuecontrolposition;
                            REKcheckposition (checkcontrol);			
    			}		
    		}
    	}
    

    Ohne den Rekrusiven durchlauf fuktioniert das alles auch sehr gut - ohne Flackern - solange ich nur wenige Panels ( momentan 3) geöffnet habe- und diese noch einen gewissen abstand zwischeneinander haben.

    Bewege ich hier die Panels zu schnell - kommt es vor das sie verschwinden - oder sich eben doch teilweise überlagern. Ich nehme mal an das diese dann hinter dem AKTIVEN kleben.



  • Ich habe mir mal ein Test Panel erstellt, in welchem lediglich die in der List vorkommenden Panels mit den dazugehörigen Locations aufgelistet werden. Und wie ich bereits vermutet hatte, liegen manche Panels einfach übereinander - und weisen die selbe Y Koordinate auf.

    Das passiert aber nur, wenn ich - wie gesagt - ein Panel zu schnell bewege. Und dann sollte doch noch überprüft werden, ob die neue Controlposition nicht auch schon belegt ist. Rekrusion bietet sich hier ja an, nur weiß ich nicht wie ich sie anwenden soll, da ich damit noch nicht gearbeitet habe.

    Eigentlich sollte es doch ganz einfach sein verdammt. Wenn das bewegte Panel ein anderes schneidet, soll das geschnittene die alte Position des bewegten einnehmen. Dann dürfte das ganze doch nicht passieren.

    Langsam qualmt mir der Kopf.



  • Das heißt, die Funktionen arbeiten zu langsam, denke ich. Ansonsten kann ich mir das nicht erklären, das die Panels gelegentlich auf die Y Koordinate -312000 springen.

    Vielleich sollte ein neuer Ansatz her.



  • Habe das nun folgend gelöst:

    void Sidebar::panelklasse::mousemoveevent (System::Windows::Forms::KeyEventArgs^  e)
    	{
    	if (movemodus != 1 || this->Location.Y < 0 || this->Location.Y > form1instanz->Size.Height) return;
    
    	System::Drawing::Point^ neueposition;
    	Control^ tempcontrol;
    	int index = 0; 	
    
    	for each (Control^ control in panelliste) // <-- Alle Panels durchlaufen
    		{
    		if (control != this) // <-- Wenn Control nicht die aufrufende ist Index erhöhen und weitermachen
    			{
    			index++;
    			continue;				
    			}		
    
    			// <-- neueposition erhällt Location des darüber / darunter liegenden Panels			
    			if (e->KeyCode == Keys::Up)
    				{
    				if (index - 1 < 0) return;
    				neueposition = panelliste[index - 1]->Location;
    				}
    
    			else if (e->KeyCode == Keys::Down)
    				{
    				if (index + 1 > panelliste.Count) return;
    				neueposition = panelliste[index + 1]->Location;
    				}
    
    			else return;
    
    			// <-- neue Position für aufrufendes Panel in Listen eintragen  
    			panelliste[index]->Location = System::Drawing::Point (neueposition);
    
    			// <-- neue Position für verschobenes Panel
    			if		(e->KeyCode == Keys::Up)
    				{
    				// Index in Liste austauschen
    				tempcontrol = panelliste[index - 1];
    
    				panelliste[index - 1] = panelliste[index];
    				panelliste[index] = tempcontrol;
    				panelliste[index]->Location = System::Drawing::Point (this->Location.X, this->Location.Y + panelliste[index - 1]->Size.Height);
    				// MousePosition auf neue Position verschieben
    				SetCursorPos (this->Location.X + 10, this->Location.Y + 10);
    				break;
    				}
    
    			else if (e->KeyCode == Keys::Down)	
    				{	
    				// Index in Liste austauschen
    				tempcontrol = panelliste[index + 1];
    
    				panelliste[index + 1] = panelliste[index];
    				panelliste[index] = tempcontrol;
    				panelliste[index]->Location = System::Drawing::Point (this->Location.X, this->Location.Y - panelliste[index + 1]->Size.Height);
    				// MousePosition auf neue Position verschieben
    				SetCursorPos (this->Location.X + 10, this->Location.Y + 10);
    				break;
    				}						
    
    		}	// Ende FOR EACH
    
    	} // Ende FUNKTION
    

    Das Panel, welches verschoben werden soll - wird nun optisch sichtbar gemacht und kann mit den Pfeiltasten bewegt werden. Das klappt so. Nur ein kleines neues Problem hat sich aufgetan.

    "Selectiert" wird das Panel über ein MouseHover oder Click Event. Bei den meisten Panels funktioniert dies auch. Nur habe ich auch Panels, welche bspw. eine DataGridView beeinhalten. Ist dieses Panel zum verschieben ausgewählt, und ich betätige die Pfeil Tasten zum Bewegen, passiert nichts.

    Das scheint irgendwie am Eingabefocus zu liegen - da das erste Element im DGV dann Selectiert wird. Einer ne Idee wie ich das verhindern könnte?


Log in to reply