Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Ideensuche: grafisches Objekt ziehen, aber nur an bestimmten Positionen einrasten lassen
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

Ideensuche: grafisches Objekt ziehen, aber nur an bestimmten Positionen einrasten lassen

beantworten | zitieren | melden

Hi,
kurze Erklärung zum Bild:

Ich habe ein Panel beerbt und noch ein bisschen darauf rumgezeichnet, damit es so gelb-rot aussieht, wie im Bild.
Dieses Panel soll auf einem Raster, das ich in eine GroupBox gezeichnet habe mittels drag drop platziert werden können.
Anforderungen:
    Es soll erreicht werden, das das Panel nur mit den darauf gezeichneten Kreisen auf den Bohrungen einrastet, die auf der GroupBox gezeichnet sind.

    Ich muss die Spur ermitteln, auf der der gelbe Bereich des Panels liegt. Wenn also das Panel mit dem roten Bereich auf Spur 26 und mit dem gelben Bereich auf Spur 27 liegt, muss ich irgendwie die 27 angezeigt bekommen.

    Das Panel bleibt beim verschieben sichtbar.

    Es dürfen sich nicht mehrere Panels überlappen.



Momentan fehlt mir ein bisschen die Idee, wie das zu bewerkstelligen ist. Ich hab schon versucht, die GroupBox durch ein GataGridView zu ersetzen und statt dem Panel die Spalten gefärbt, ist allerdings nicht besonders gut geglückt. Daher dieser versuch.
Für Ideen und Anregungen bin ich immer offen.
Attachments
private Nachricht | Beiträge des Benutzers
Gelöschter Benutzer

beantworten | zitieren | melden

besser ist hierfür ein eigenens control. anleitung findest du hier:

[Tutorial] Gezeichnete Objekte mit der Maus verschieben
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

du meinst, ein eigenes Control anstatt der GroupBox oder was. Mein Problem ist glaub ich eher, wie ich die Rasterung und die Bereiche sauber realisiere.
Das mit dem sichtbaren Verschieben des Panels könnte wahrscheinlich so wie in dem Tut beschrieben funktionieren, werd mal ausprobieren, wie performant das ist.
Für das Überschneiden hab ich auch noch keine richtige Idee, hier mein Ansatz:
Man müsste prüfen, ob die Randpixel des zu verschiebenden Panels im ClientRectangle des anderen Panels liegen oder so, keine Ahnung, ob das was ausmacht, ob das verschobene Panel vor oder hinter dem anderen liegt.

Die Spuren könnte man ja auch mit separaten Panels machen, wenn ich das Verschieben nicht mit DragDrop machen würde, dann könnte man vielleicht abfragen, über welchem Control der gelbe Bereich des Panels liegt.
private Nachricht | Beiträge des Benutzers
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5.299

beantworten | zitieren | melden

Hi!

Könnte man schon in einem DGV darstellen, aber horizontal.
Dazu täte sich son Dataset eignen:
Attachments
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5.299

beantworten | zitieren | melden

Das DGV würde dann die Bohrungen anzeigen, und welcher Spur die zugehören, und man könnte iwas da drüber draggen, und müsste im DragOver immer checken, ob ein Droppen zulässig ist, und entsprechend die Gridzellen colorieren.
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2.760
Herkunft: München

beantworten | zitieren | melden

@ErfinderDesRades: Wenn du dir den Screenshot genauer anschaust dann wirst du wohl hoffentlich mit mir übereinstimmen das ein Grid dafür eher ungeeignet ist.

@buyden:
Ich würde dir auch eher zum selberzeichnen raten, ansonsten ist so ein Panel ja schnell verschiebbar gemacht (OnMouseDown, MouseMove, MouseUp überschreiben und mehr oder weniger fertig)

Hmm... jetzt hab ichs dann doch sebst gebaut (Im MouseUp wird eingerastet):
[EDIT] Jetzt noch ein bisschen schöner...


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
   public partial class Form1 : Form
   {
      Rectangle panel = new Rectangle();

      public Form1()
      {
         InitializeComponent();

         panel.Y = 20;
         panel.Width = 170;
         panel.Height = 100;

         DoubleBuffered = true;
      }


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

         e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

         e.Graphics.DrawLine(Pens.Black, new Point(0, 20), new Point(Width, 20));
         e.Graphics.DrawLine(Pens.Black, new Point(0, 120), new Point(Width, 120));

         int slotNumber = 1;

         for (int i = 0; i < Width; i++)
         {
            if (i > 0 && i % 50 == 0 && i < 600)
            {
               if (((i != panel.X + panel.Width / 2) &&
                   !panel.Contains(new Point(i, 50))) ||
                   mouseDown)
               {
                  e.Graphics.DrawLine(Pens.DarkGray, new Point(i, 20), new Point(i, 120));

                  e.Graphics.DrawEllipse(Pens.Gray, new Rectangle(i - 5, 25, 10, 10));
                  e.Graphics.DrawEllipse(Pens.Gray, new Rectangle(i - 5, 105, 10, 10));
               }

               e.Graphics.DrawLine(Pens.Gray, new Point(i, 120), new Point(i, 130));

               SizeF slotNumerSize = e.Graphics.MeasureString(slotNumber.ToString(), SystemFonts.DefaultFont);
               e.Graphics.DrawString(slotNumber.ToString(), SystemFonts.DefaultFont, Brushes.Black, new Point(i - (int)slotNumerSize.Width / 2, 130));

               slotNumber++;
            }
         }

         e.Graphics.DrawRectangle(Pens.Red, panel);

         using (SolidBrush panelFill = new SolidBrush(Color.FromArgb(100, Color.Gray)))
         {
            e.Graphics.FillRectangle(panelFill, panel);
         }

         if (!snappedIn)
         {
            e.Graphics.DrawEllipse(Pens.DarkGray, new Rectangle(panel.X + (panel.Width / 2) - 5, panel.Y + 5, 10, 10));
            e.Graphics.DrawEllipse(Pens.DarkGray, new Rectangle(panel.X + (panel.Width / 2) - 5, panel.Bottom - 15, 10, 10));
         }
         else
         {
            e.Graphics.FillEllipse(Brushes.DarkGray, new Rectangle(panel.X + (panel.Width / 2) - 5, panel.Y + 5, 10, 10));
            e.Graphics.FillEllipse(Brushes.DarkGray, new Rectangle(panel.X + (panel.Width / 2) - 5, panel.Bottom - 15, 10, 10));
         }
      }

      bool mouseDown = false;
      bool snappedIn = false;

      Point mouseLocation;
      Point oldMouseLocation;

      protected override void OnMouseDown(MouseEventArgs e)
      {
         base.OnMouseDown(e);

         if (panel.Contains(e.Location))
            mouseDown = true;
         mouseLocation = e.Location;
         oldMouseLocation = e.Location;

         Invalidate();
      }

      protected override void OnMouseMove(MouseEventArgs e)
      {
         base.OnMouseMove(e);

         if (!mouseDown)
            return;

         snappedIn = false;

         mouseLocation = e.Location;
         panel.X += mouseLocation.X - oldMouseLocation.X;
         oldMouseLocation = e.Location;

         Invalidate();
      }

      protected override void OnMouseUp(MouseEventArgs e)
      {
         base.OnMouseUp(e);

         mouseDown = false;

         //
         // Snap the panel
         //
         int bestMatchX = int.MaxValue;
         int bestDistance = int.MaxValue;
         for (int i = 0; i < Width; i++)
         {
            if (i > 0 && i % 50 == 0 && i < 600)
            {
               int screwThreadX = i;
               int currentDistance = Math.Abs((panel.X + (panel.Width / 2)) - i);
               if (currentDistance ≤ bestDistance)
               {
                  bestDistance = currentDistance;
                  bestMatchX = i;
               }
               else
               {
                  break;
               }
            }
         }

         snappedIn = true;

         panel.X = bestMatchX - (panel.Width / 2);

         Invalidate();
      }
   }
}

Da sollte zumindest ansatzweise so weit alles drin sein was du brauchen wirst um die Überlappungen usw. zu realisieren...
private Nachricht | Beiträge des Benutzers
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

@jaensen
gefällt mir sehr gut, deine Lösung, wobei mir noch nicht so ganz einleuchtet was du tust.
private Nachricht | Beiträge des Benutzers
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

Ich hab jetzt mal versucht, das ganze so umzusetzen.
Problem 1:
Ich kann nicht mit einem Panel arbeiten wie geplant, da sonst die MouseMove-Geschichte nicht funktioniert.
Problem 2:
Wenn ich direkt auf mein Form Rectangles zeichne, kann ich das ja nur im Paint-Event tun, allerdings ist mir nicht so ganz klar, wie ich dann dynamisch Panels hinzufügen soll.
private Nachricht | Beiträge des Benutzers
Gelöschter Benutzer

beantworten | zitieren | melden

@Problem 1:
deswegen ja der vorschlag von jaensen und mir.

@Problem 2:
musst du ja nciht. das panel ist bei dir nur eine grafik in einem viereck. ob du das nun selber zeichnest oder aber durch ein panelcontrol zeichnen lässt ist absolut egal.
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo buyden,
Zitat
Wenn ich direkt auf mein Form Rectangles zeichne, kann ich das ja nur im Paint-Event tun, allerdings ist mir nicht so ganz klar, wie ich dann dynamisch Panels hinzufügen soll.
im Paint-Event zu zeichnen ist richtig. Das verhindert ja aber nicht, dass man an beibiegen Stellen mit Invalidate das Zeichnen selbst anstoßen kann.

herbivore
private Nachricht | Beiträge des Benutzers
norman_timo
myCSharp.de - Member

Avatar #avatar-1775.jpeg


Dabei seit:
Beiträge: 4.506
Herkunft: Wald-Michelbach (Odw)

beantworten | zitieren | melden

Hallo zusammen,
Zitat
Das verhindert ja aber nicht, dass man an beibiegen Stellen mit Invalidate das Zeichnen selbst anstoßen kann.

Einschränkung:
Es gibt ganz lustige Effekte, wenn man im Paint selbst ein Invalidate hervorruft, das sollte mit logischen Mitteln verhindert werden.

Grüße
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
private Nachricht | Beiträge des Benutzers
Gelöschter Benutzer

beantworten | zitieren | melden

im paint ist es auch niemals notwendig invalidate aufzurufen. das paint soll nur die grafische repräsentation eines bestimmten zustandes sein und nciht diesen zustand selbst durch änderung am zustand herbeiführen. daher sind z.b. positionsänderungen innerhalb des paint, von grafisch repräsentierten objekten zu vermeiden.
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5.299

beantworten | zitieren | melden

Jaensens Sample kann nur der allererste Einstieg sein, der zeigt, dasses prinzipiell möglich ist.
Du mußt dir natürlich DrawObject-Klassen schreiben, die selbst "wissen", wo und wie sie sich zu zeichnen haben.
Und natürlich nicht direkt aufs Form selbst malen, sondern ein Extra-Control entwickeln, dem man Daten angibt, und die's dann prinzipiell so visualisiert, wie Jaensen gezeigt hat.
"Canvas" nenne ich so Controls. Kannst mal Gezieltes OwnerDrawing angucken.

Wobei ich nicht ganz überzeugt bin, obs nicht evtl. doch einfacher ist, statt OwnerDrawing from Scratch ein DGV zu mißbrauchen, und da DataRows herumzudraggen.
Jednfalls dafür habichwas, wo man das Drop-Target schön markieren kann, also die Position, wo das "Panel" beim Droppen einrasten würde. (nicht ganz) alles erschlagendes DragnDrop
Das zu visualisieren scheint mir nämlich wichtiger, als das Panel jederzeit schön zu malen.

Iwie eine Daten-Repräsentation des Vorganges brauchst du jedenfalls.
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

Danke erstmal für die vielen Hinweise.
Die Ganze Sache selber zu zeichnen gefällt mir ehrlich gesagt besser als das DGV.
Also ich bin noch am Basteln, aber so richtig vorwärts komm ich noch nicht.
Momentan sieht meine BauteilPanel-Klasse so aus:


using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace BauteilPanelTest
{
    class BauteilPanel:Panel
    {   
        #region Properties          
        
        /// <summary>
        /// Die Breite des Bauteils in Punkten
        /// </summary>
        public int BauteilBreite { get; set; }

        /// <summary>
        /// Die Position des Bauteils in Punkten
        /// </summary>
        public int BauteilPos { get; set; }

        /// <summary>
        /// Die Artikelnummer des Bauteils
        /// </summary>
        public string IdentNr { get; set; }                                                   

        /// <summary>
        /// Gibt den Feederplatz an, an dem sich das Bauteil befindet
        /// </summary>
        public int Feederplatz { get; set; }

        /// <summary>
        /// Gibt die Bohrung an, auf der die Stangenaufnahme montiert wird
        /// </summary>
        public int Bohrung { get; set; }
        #endregion  
          
        #region Lokale Variablen
         
        private Brush transparentRedBrush = 
            new SolidBrush(Color.FromArgb(200, Color.Red));
        private Brush transperentYellowBrush = 
            new SolidBrush(Color.FromArgb(200, Color.Yellow));
        
        private GraphicsPath gp = new GraphicsPath();
        private Matrix matrix = new Matrix();
        private Point textOrigin;
       
        #endregion
        /// <summary>
        /// Konstruktor der BauteilPanel-Klasse
        /// </summary>
        /// <param name="identnr">Bauteil-Identnummer</param>
        /// <param name="aufnahme">Breite der Stangenaufnahme Spalten - 1 Spalte = 1/2 Bohrung</param>
        /// <param name="bauteil">Breite des Bauteils in Spalten</param>
        /// <param name="pos">Position des Bauteils in Spalten auf der Aufnahme</param>
        /// <param name="raster">Umrechnungsfaktor von Spalten auf Punkte</param>
        /// <param name="height">Höhe des BauteilPanels in Punkten</param>
        public BauteilPanel(string identnr, int aufnahme, int bauteil, int pos, 
            int raster, int height)
        {                              
            this.IdentNr = identnr;              
            this.BauteilBreite = bauteil * raster;
            this.BauteilPos = pos * raster;
            this.Height = height;
            this.BorderStyle = BorderStyle.FixedSingle;
            this.Size = new Size(aufnahme*raster, this.Height);
            textOrigin = new Point(this.BauteilPos + this.BauteilBreite / 2 - 15, 
                this.Height / 2 + (IdentNr.Length * 15)/2);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.BackColor = Color.FromArgb(200, Color.Red);

            //GraphicsPath erstellen und Text schreiben
            gp.AddString(this.IdentNr, FontFamily.GenericMonospace, (int)FontStyle.Bold,
                24F, textOrigin, StringFormat.GenericDefault);
            matrix.RotateAt(270, textOrigin); 
            gp.Transform(matrix);   
        }

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

            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            
            //BauteilRechteck zeichnen                         
            //Rechteck wird 2Pt kleiner gezeichnet, da der Rand auf jeder Seite 1Pt 
            //breit ist.  
            e.Graphics.FillRectangle(transperentYellowBrush,
                this.BauteilPos - 1, 0, this.BauteilBreite, this.Height - 2);

            //GraphicsPath für den Text zeichnen                   
            e.Graphics.FillPath(Brushes.Black, gp);                 
           
            //Bohrungen zeichnen
            e.Graphics.DrawEllipse(Pens.Black, this.ClientSize.Width / 2 - 5, 
                10, 10, 10);
            e.Graphics.DrawEllipse(Pens.Black, this.ClientSize.Width / 2 - 5, 
                this.ClientSize.Height - 20, 10, 10);
        }

        public virtual bool Contains(Point pt)
        {
            if (pt.X ≥ this.Location.X && pt.X ≤ this.Location.X + this.Width &&
                pt.Y ≥ this.Location.Y && pt.Y ≤ this.Location.Y + this.Height)
            {
                return true;
            }
            else return false;                
        }        
    }
}

Die Contains-Methode liefert momentan allerdings nur true, wenn ich auf den Rand des Panels komme, was ich nicht so ganz verstehe.
Das andere Control, auf dem gezeichnet werden soll fehlt noch.
private Nachricht | Beiträge des Benutzers
Gelöschter Benutzer

beantworten | zitieren | melden

überprüfe die koordinaten, die du in die methode schiebst. mach am besten einen bedingten breakpoint.

noch etwas wichtiges:
Dispose implementieren und verwenden
GDI+ & OutOfMemoryException
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5.299

beantworten | zitieren | melden

Hi!

Deine Bauteile dürfen nicht von Panel oder sonst nem control erben.
Das müssen DrawObjects sein, vergleichbar denen aus meinem Sample.

Auch jaensen dragt kein Panel, sondern nur ein Rectangle, wasser halt panel genannt hat.

Wie gesagt: du brauchst ein Canvas-Control - die Zeichenfläche, und diese verarbeitet mausevents.
Und hält eine Liste von Bauteil-Objekten, was bei mir DrawObjects sind - Objekte, die "wissen", wie und wo sich malen.

Im OnPaint der Zeichenfläche müssen alle Bauteile aufgefordert werden, sich zu zeichenen, und bekommen dazu das Graphics übergeben.

Zum Draggen muß die Canvas bestimmen, welches DrawObject selektiert ist, und dessen Location ändern. Ausserdem muß Canvas.Invalidate(Rectangle) aufgerufen werden, einmal für die alte Position des DrawObjects, und einmal für die neue.
Oder einfach Canvas.Invalidate(), wodurch die ganze Canvas neuzeichnet (schlechtere Performance).
Aber diese Infos sind dir ja alle schon gegeben.

bei jaensen ist das noch alles in einer Klasse zusammen gelassen, aber bei mehreren Bauteilen muß man die Verantwortlichkeiten besser auseinander-strukturieren oderso.

Das mit dem Contains wird kein Problem lösen, solange der Ansatz so falsch ist.

Kennst du übrigens die Methode Rectangle.Contains()?
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2.760
Herkunft: München

beantworten | zitieren | melden

Oder nimm einfach Rectangle.Contains(Point)
private Nachricht | Beiträge des Benutzers
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

Soweit jetzt der Fortschritt,

Die BauteilPanel-Klasse:


using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace BauteilPanelTest
{
    class BauteilPanel
    {   
        #region Properties          
        
        public int Height { get; set; }

        public int Width { get; set; }

        public Size Size { get; set; }

        public Point Location { get; set; }

        /// <summary>
        /// Die Breite des Bauteils in Punkten
        /// </summary>
        public int BauteilBreite { get; set; }

        /// <summary>
        /// Die Position des Bauteils in Punkten
        /// </summary>
        public int BauteilPos { get; set; }

        /// <summary>
        /// Die Artikelnummer des Bauteils
        /// </summary>
        public string IdentNr { get; set; }                                                   

        /// <summary>
        /// Gibt den Feederplatz an, an dem sich das Bauteil befindet
        /// </summary>
        public int Feederplatz { get; set; }

        /// <summary>
        /// Gibt die Bohrung an, auf der die Stangenaufnahme montiert wird
        /// </summary>
        public int Bohrung { get; set; }
        #endregion  
          
        #region Lokale Variablen
         
        private Brush transparentRedBrush = 
            new SolidBrush(Color.FromArgb(200, Color.Red));
        private Brush transperentYellowBrush = 
            new SolidBrush(Color.FromArgb(200, Color.Yellow));
        private Pen hitTestPen = new Pen(Brushes.Black, 10);       
        
        private GraphicsPath baseRectPath = new GraphicsPath();
        private GraphicsPath bauteilRectPath = new GraphicsPath();
        private GraphicsPath bohrungPath = new GraphicsPath();
        private GraphicsPath stringPath = new GraphicsPath();
        private Matrix rotateMatrix = new Matrix();
        private Matrix moveMatrix = new Matrix();
        private Point textOrigin;
        private Rectangle rectPanel, rectBauteil, bohrung1, bohrung2;     
   
        
       
        #endregion
        /// <summary>
        /// Konstruktor der BauteilPanel-Klasse
        /// </summary>
        /// <param name="identnr">Bauteil-Identnummer</param>
        /// <param name="aufnahme">Breite der Stangenaufnahme Spalten - 1 Spalte = 1/2 Bohrung</param>
        /// <param name="bauteil">Breite des Bauteils in Spalten</param>
        /// <param name="pos">Position des Bauteils in Spalten auf der Aufnahme</param>
        /// <param name="raster">Umrechnungsfaktor von Spalten auf Punkte</param>
        /// <param name="height">Höhe des BauteilPanels in Punkten</param>
        public BauteilPanel(string identnr, int aufnahme, int bauteil, int pos, 
            int raster, int height)
        {                              
            this.IdentNr = identnr;              
            this.BauteilBreite = bauteil * raster;
            this.BauteilPos = pos * raster;
            this.Height = height;
            this.Width = aufnahme * raster;
            this.Size = new Size(aufnahme * raster, this.Height);
            this.Location = new Point(0, 0);

            rectPanel = new Rectangle(
                this.Location,
                this.Size);
            baseRectPath.AddRectangle(rectPanel);

            rectBauteil = new Rectangle(
                this.Location.X + this.BauteilPos,
                this.Location.Y,
                this.BauteilBreite, this.Height);
            bauteilRectPath.AddRectangle(rectBauteil);

            bohrung1 = new Rectangle(
                this.Location.X + this.Width / 2 - 5,
                this.Location.Y + 10,
                10, 10);
            bohrungPath.AddEllipse(bohrung1);

            bohrung2 = new Rectangle(
                this.Location.X + this.Width / 2 - 5,
                this.Location.Y + this.Height - 20,
                10, 10);
            bohrungPath.AddEllipse(bohrung2);

            textOrigin = new Point(
                this.Location.X + this.BauteilPos + this.BauteilBreite / 2 - 12,
                this.Location.Y + this.Height / 2 + (IdentNr.Length * 15) / 2);

            //GraphicsPath erstellen und Text schreiben
            stringPath.AddString(this.IdentNr, FontFamily.GenericMonospace, (int)FontStyle.Bold,
                24F, textOrigin, StringFormat.GenericDefault);
            rotateMatrix.RotateAt(270, textOrigin);
            stringPath.Transform(rotateMatrix);
            
            //bauteilRectPath.AddPath(stringPath, true);
            //baseRectPath.AddPath(bauteilRectPath, true);
            //baseRectPath.AddPath(bohrungPath, true);
           

        }

        public void Draw(Graphics p)
        {      
            p.FillPath(transparentRedBrush, baseRectPath);
            p.DrawPath(Pens.Black, baseRectPath);
            p.FillPath(transperentYellowBrush, bauteilRectPath);
            p.FillPath(Brushes.Black, stringPath);
            p.DrawPath(Pens.Black, bohrungPath);                 
        }

        public void Move(int deltaX, int deltaY)
        {
            Matrix moveMatrix = new Matrix();
            moveMatrix.Translate(deltaX, deltaY);
            baseRectPath.Transform(moveMatrix);
            baseRectPath.Transform(moveMatrix);
            stringPath.Transform(moveMatrix);
            bohrungPath.Transform(moveMatrix);
        }

        public bool Contains(Point pt)
        {
            if (rectPanel.Contains(pt))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

Die Zeichenfläche:


using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Collections.Generic;

namespace BauteilPanelTest
{
    class ZuordnungPanel   :Panel
    {               
        List<BauteilPanel> panelList = new List<BauteilPanel>();                 
        private Point lastMouseLocation;
        private BauteilPanel movingBt;

        public ZuordnungPanel()
        {
            //hier müssen noch die GraphicsPaths für das Raster erzeugt werden.
        }

        public void Add(BauteilPanel bt)
        {
            panelList.Add(bt);
            Invalidate();  
        }     

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

            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            foreach (BauteilPanel bp in panelList)
            {
                bp.Draw(e.Graphics);
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (movingBt != null)
            {
                movingBt.Move(e.X - lastMouseLocation.X, 0);
                lastMouseLocation = e.Location;
                Invalidate();        
            }    
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {          
            base.OnMouseDown(e);
            for (int i = this.panelList.Count - 1; i ≥ 0; i--)
            {
                BauteilPanel bt = panelList[i];
                if (bt.Contains(e.Location))
                {
                    movingBt = bt;
                    break;
                }
            }
            lastMouseLocation = e.Location;                               
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);                
            movingBt = null;
        }
    }
}

und das Form:


using System;
using System.Drawing;
using System.Windows.Forms;

namespace BauteilPanelTest
{         
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();                     
        }

        private void button1_Click(object sender, EventArgs e)
        {                        
            BauteilPanel bt2 = new BauteilPanel("UUZ02803142", 4, 4, 0, 15, 300);
            bt2.Location = new Point(150, 0);   
            ZuordnungPanel zp = new ZuordnungPanel();
            zp.ClientSize = new Size(500, 303);
            zp.BorderStyle = BorderStyle.FixedSingle;
            zp.Add(bt2);
            this.Controls.Add(zp);                                              
        }                  
    }       
}

Soweit so gut.
momentanes Problem ist, dass mein Bauteilpanel ja aus mehreren Paths besteht.
Ich habe versucht, alle zugehörigen Paths mit der gleichen Matrix zu verschieben, allerdings verschieben sich dadurch die Paths untereinander.
Wenn ich versuche, das Panel mit Path.AddPath zusammen zu setzen, wird die Erscheinung des Panels so verändert, dass der Sinn eigentlich nicht mehr gegeben ist aber trotzdem nicht alles verschoben.
private Nachricht | Beiträge des Benutzers
Gelöschter Benutzer

beantworten | zitieren | melden

zeig mal ienen screenshot wie das ergebniss ausehen soll (von so einem bauteil) und schau dir das mal an:
GDI+ & OutOfMemoryException
Dispose implementieren und verwenden


du hast da einige grafische objekte, die du manuell wieder aufräumen musst.
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2.760
Herkunft: München

beantworten | zitieren | melden

Ich würde versuchen das Bauteil mittels Komposition zusammenzusetzen. Also Verschachtelte Bauteile die wiederum aus Bauteilen bestehen.
private Nachricht | Beiträge des Benutzers
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5.299

beantworten | zitieren | melden

Hi!


        public void Move(int deltaX, int deltaY)
        {
            Matrix moveMatrix = new Matrix();
            moveMatrix.Translate(deltaX, deltaY);
            baseRectPath.Transform(moveMatrix);
            baseRectPath.Transform(moveMatrix);
            stringPath.Transform(moveMatrix);
            bohrungPath.Transform(moveMatrix);
        }
Ist das doppelte Verschieben von baseRectPath beabsichtigt?

Ich bevorzuge übrigens, bei meinen Verschiebereien, mit Template-Pathes zu arbeiten, und absoluten Positionen.
Also die Zeichnungen werden in Templates angelegt, und bleiben unverändert.
Zum Bewegen wird der Template-GraphicsPath in einen Output-GraphicsPath umkopiert, und dann so transformiert dasser die richtige Drehung, Position, Größe annimmt, und dann (später) gezeichnet.

so ein Move mit delta-Werten scheint mir schlecht zu verwalten, man muß die ja ständig aufsummieren, um die absolute Position zu kriegen.
Bzw., wenns der Maus folgen soll, muß man aus den Mauskoordinaten ständig diese übelicherweise sehr kleinen Delta-Werte rausfitzeln, da tätich auch Rundungs-Probleme befürchtigen.
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2.760
Herkunft: München

beantworten | zitieren | melden

So in etwa:


using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
   public abstract class GraphicElement
   {      
      #region Private variables / Public properties

      public virtual RectangleF Bounds { get; set; }

      public virtual string Name { get; set; }

      public Color Color
      {
         get { return _color; }
         set
         {
            _color = value;
            if (BorderPen != null)
               BorderPen.Dispose();
            BorderPen = new Pen(_color);
         }
      }
      private Color _color;
      protected Pen BorderPen { get; set; }


      public Color FillColor
      {
         get { return _fillColor; }
         set
         {
            _fillColor = value;
            if (FillBrush != null)
               FillBrush.Dispose();
            FillBrush = new SolidBrush(_fillColor);
         }
      }
      private Color _fillColor;
      protected Brush FillBrush { get; set; }

      #endregion

      #region Constructor

      public GraphicElement()
      {
         Color = Color.Black;
         FillColor = Color.FromArgb(75, Color.White);
      }

      #endregion

      #region Public abstract methods

      public abstract void Paint(Graphics g);

      #endregion

      #region Public virtual methods

      public virtual bool Hits(PointF p)
      {
         return Bounds.Contains(p);
      }

      public virtual void Move(float deltaX, float deltaY)
      {
         RectangleF newRect = Bounds;

         newRect.X += deltaX;
         newRect.Y += deltaY;

         Bounds = newRect;
      }

      #endregion

      #region Internal event invoking methods

      internal void InvokeMouseDown(MouseEventArgs e)
      {
         OnMouseDown(e);
      }

      internal void InvokeMouseMove(MouseEventArgs e)
      {
         OnMouseMove(e);
      }

      internal void InvokeMouseUp(MouseEventArgs e)
      {
         OnMouseUp(e);
      }

      #endregion

      #region Protected virtual methods

      protected virtual void OnMouseDown(MouseEventArgs e)
      {
         MouseEventHandler handler = MouseDown;
         if (handler != null)
            handler(this, e);
      }

      protected virtual void OnMouseMove(MouseEventArgs e)
      {
         MouseEventHandler handler = MouseMove;
         if (handler != null)
            handler(this, e);
      }

      protected virtual void OnMouseUp(MouseEventArgs e)
      {
         MouseEventHandler handler = MouseUp;
         if (handler != null)
            handler(this, e);
      }

      #endregion

      #region Public events

      public event MouseEventHandler MouseDown;

      public event MouseEventHandler MouseMove;

      public event MouseEventHandler MouseUp;

      #endregion
   }

   #region GraphicElementCollection<T>

   public class GraphicElementCollection<T> : KeyedCollection<string, T> where T : GraphicElement
   {
      #region Protected overridden methods

      protected override string GetKeyForItem(T item)
      {
         return item.Name;
      }

      protected override void InsertItem(int index, T item)
      {
         base.InsertItem(index, item);
      }

      protected override void RemoveItem(int index)
      {
         base.RemoveItem(index);
      }

      #endregion

      #region Protected virtual methods

      protected virtual void OnCollectionChanged(EventArgs e)
      {
         EventHandler handler = CollectionChanged;
         if (handler != null)
            handler(this, e);
      }

      #endregion

      #region Public events

      public event EventHandler CollectionChanged;

      #endregion
   }

   #endregion

   #region Part

   public class Part : GraphicElement
   {
      public PartCollection Parts
      {
         get { return _parts; }
      }
      private PartCollection _parts = new PartCollection();

      //public DrillholeCollection Drillholes
      //{
      //   get { return _Drillholes; }
      //}
      //private DrillholeCollection _Drillholes = new DrillholeCollection();

      public Part Parent { get; set; }

      public override void Paint(Graphics g)
      {
         GraphicsState state = g.Save();
         if (Parent != null)
         {
            using (Matrix m = g.Transform)
            {
               m.Translate(Parent.Bounds.X, Parent.Bounds.Y);
               g.Transform = m;
            }
         }

         g.FillRectangle(FillBrush, Bounds);
         g.DrawRectangle(BorderPen, Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height);

         GraphicsState state2 = g.Save();

         using (Matrix m = g.Transform)
         {
            m.Translate(Bounds.X, Bounds.Y);
            g.Transform = m;
         }

         //foreach (Drillhole Drillhole in Drillholes)
         //{
         //   PointF DrillholeCenter = new PointF(Drillhole.Bounds.X + (Drillhole.Bounds.Width / 2),
         //                                    Drillhole.Bounds.Y + (Drillhole.Bounds.Height / 2));

         //   if (Control.MouseButtons == MouseButtons.Left)
         //   {
         //      g.DrawLine(Pens.LightBlue, new PointF(DrillholeCenter.X - 800, DrillholeCenter.Y), new PointF(DrillholeCenter.X + 800, DrillholeCenter.Y));
         //      g.DrawLine(Pens.LightBlue, new PointF(DrillholeCenter.X, DrillholeCenter.Y - 800), new PointF(DrillholeCenter.X, DrillholeCenter.Y + 800));
         //   }

         //   Drillhole.Paint(g);
         //}

         g.Restore(state2);

         foreach (Part part in Parts)
         {
            part.Paint(g);
         }

         g.Restore(state);
      }

      public override bool Hits(PointF p)
      {
         float x = 0;
         float y = 0;

         Part parent = this;
         do
         {
            parent = parent.Parent;
            if (parent != null)
            {
               x += parent.Bounds.X;
               y += parent.Bounds.Y;
            }

         } while (parent != null);

         RectangleF rect = Bounds;
         rect.X += x;
         rect.Y += y;

         return rect.Contains(p);
      }

      protected override void OnMouseDown(MouseEventArgs e)
      {
         foreach (Part part in _parts)
         {
            if (part.Hits(e.Location))
               part.InvokeMouseDown(e);
         }

         base.OnMouseDown(e);
      }

      protected override void OnMouseMove(MouseEventArgs e)
      {
         foreach (Part part in _parts)
         {
            if (part.Hits(e.Location))
            {
               part.InvokeMouseMove(e);
            }
         }

         base.OnMouseMove(e);
      }

      protected override void OnMouseUp(MouseEventArgs e)
      {
         foreach (Part part in _parts)
         {
            if (part.Hits(e.Location))
               part.InvokeMouseUp(e);
         }

         base.OnMouseUp(e);
      }
   }

   #endregion

   #region Part collection

   public class PartCollection : GraphicElementCollection<Part>
   {
      protected override void InsertItem(int index, Part item)
      {
         base.InsertItem(index, item);
      }

      protected override void RemoveItem(int index)
      {
         base.RemoveItem(index);
      }
   }

   #endregion

   #region GraphicElementBehavior

   public abstract class GraphicElementBehavior : IDisposable
   {
      #region Private variables

      private List<GraphicElement> _targets = new List<GraphicElement>();

      #endregion

      #region Constructor

      public GraphicElementBehavior()
      {

      }

      #endregion

      #region Public virtual methods

      public virtual void Add(GraphicElement element)
      {
         _targets.Add(element);

         element.MouseDown += new MouseEventHandler(Element_MouseDown);
         element.MouseMove += new MouseEventHandler(Element_MouseMove);
         element.MouseUp += new MouseEventHandler(Element_MouseUp);
      }

      public virtual void Remove(GraphicElement element)
      {
         element.MouseDown -= new MouseEventHandler(Element_MouseDown);
         element.MouseMove -= new MouseEventHandler(Element_MouseMove);
         element.MouseUp -= new MouseEventHandler(Element_MouseUp);

         _targets.Remove(element);
      }

      #endregion

      #region Protected virtual methods


      protected virtual void Element_MouseUp(object sender, MouseEventArgs e)
      {

      }

      protected virtual void Element_MouseMove(object sender, MouseEventArgs e)
      {

      }

      protected virtual void Element_MouseDown(object sender, MouseEventArgs e)
      {

      }

      #endregion

      #region IDisposable Members

      public void Dispose()
      {
         if (_targets == null || _targets.Count == 0)
            return;

         for (int i = _targets.Count - 1; i ≥ 0; i--)
         {
            GraphicElement element = _targets[i];
            Remove(element);
         }
      }

      #endregion
   }

   #endregion

   #region Part behavior

   public class PartBehavior : GraphicElementBehavior
   {
      bool _mouseDown;

      Point _mouseLocation;
      Point _oldLocation;

      protected override void Element_MouseDown(object sender, MouseEventArgs e)
      {
         base.Element_MouseDown(sender, e);

         _mouseDown = true;
         _mouseLocation = e.Location;
         _oldLocation = e.Location;
      }

      protected override void Element_MouseMove(object sender, MouseEventArgs e)
      {
         base.Element_MouseMove(sender, e);

         if (!_mouseDown)
            return;

         Part senderPart = (Part)sender;

         _mouseLocation = e.Location;
         senderPart.Move(_mouseLocation.X - _oldLocation.X, _mouseLocation.Y - _oldLocation.Y);
         _oldLocation = _mouseLocation;

         //_basePlate.Invalidate();
      }

      protected override void Element_MouseUp(object sender, MouseEventArgs e)
      {
         base.Element_MouseUp(sender, e);

         Part senderPart = (Part)sender;

         _mouseDown = false;
         //_basePlate.Invalidate();
      }
   }

   public class Test
   {
      public Part BuildNestedPart()
      {
         Part p = new Part();
         p.Bounds = new Rectangle(10, 10, 150, 150);

         Part pInner1 = new Part();
         pInner1.Bounds = new Rectangle(10, 10, 50, 50);
         pInner1.Parent = p;

         Part pInnerInner = new Part();
         pInnerInner.Bounds = new Rectangle(10, 10, 30, 30);
         pInnerInner.Parent = pInner1;

         pInner1.Parts.Add(pInnerInner);

         Part pInner2 = new Part();
         pInner2.Bounds = new Rectangle(10, 70, 50, 50);
         pInner2.Parent = p;

         Part pInner3 = new Part();
         pInner3.Bounds = new Rectangle(70, 10, 50, 50);
         pInner3.Parent = p;

         Part pInner4 = new Part();
         pInner4.Bounds = new Rectangle(70, 70, 50, 50);
         pInner4.Parent = p;

         p.Parts.Add(pInner1);
         p.Parts.Add(pInner2);
         p.Parts.Add(pInner3);
         p.Parts.Add(pInner4);

         return p;
      }
   }

   #endregion
}

Ganz unten ist eine Testklasse die ein verschachteltes Part-objekt baut.
Um das Part zu zeichnen brauchst du natürlich noch ein Control mit überschriebener OnPaint-Methode.
Es gibt auch ein PartBehavior dem du die Parts hinzufügen musst um es bewegen zu können. In diesem ist das Invalidate des Controls um die Änderungen sichtbar zu machen noch auskommentiert, das musst du durch dein Control ersetzen...

[EDIT]
Ein Testcontrol:


   public class TestControl : Control
   {
      public PartCollection Parts
      {
         get { return _parts; }
      }
      private PartCollection _parts = new PartCollection();

      public PartBehavior Behavior { get; set; }

      public TestControl()
         :base()
      {
         _parts.CollectionChanged += new EventHandler(_parts_CollectionChanged);
         Behavior = new PartBehavior();
         DoubleBuffered = true;
      }

      void _parts_CollectionChanged(object sender, EventArgs e)
      {
         Invalidate();
      }

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

         foreach (Part part in _parts)
         {
            part.Paint(e.Graphics);
         }
      }

      protected override void OnMouseDown(MouseEventArgs e)
      {
         base.OnMouseDown(e);

         foreach (Part part in _parts)
         {
            if (part.Hits(e.Location))
               part.InvokeMouseDown(e);
         }
      }

      protected override void OnMouseMove(MouseEventArgs e)
      {
         base.OnMouseMove(e);

         foreach (Part part in _parts)
         {
            if (part.Hits(e.Location))
               part.InvokeMouseMove(e);
         }
         Invalidate();
      }

      protected override void OnMouseUp(MouseEventArgs e)
      {
         base.OnMouseUp(e);

         foreach (Part part in _parts)
         {
            if (part.Hits(e.Location))
               part.InvokeMouseUp(e);
         }
         Invalidate();
      }
   }

Folgenden Code einfach in die Form und mal ausprobieren:



         Test test = new Test();
         Part part = test.BuildNestedPart(testControl1);

         testControl1.Parts.Add(part);
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von jaensen am .
private Nachricht | Beiträge des Benutzers
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

@JAck30lena

Screenshot siehe erster Beitrag, das ganze halbtransparent, um das Raster der Zeichenfläche noch sehen zu können

@ErfinderDesRades
Zitat
Ist das doppelte Verschieben von baseRectPath beabsichtigt?

Natürlich nicht. Das erklärt natülich einiges.

Jetzt funktioniert das verschieben beim ersten Mal. Beim 2. Mal muss allerdings auf die ursprüngliche Position geklickt werden, um wieder verschieben zu können, nicht auf das Panel selbst.
private Nachricht | Beiträge des Benutzers
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

Soweit jetzt also der Fortschritt:

BauteilPanel-Klasse:


using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Collections.Generic;
using System;

namespace BauteilPanelTest
{
    class ZuordnungPanel   :Panel
    {               
        List<BauteilPanel> panelList = new List<BauteilPanel>();                 
        private Point lastMouseLocation;         
        private BauteilPanel movingBt;
        private GraphicsPath bohrungPath = new GraphicsPath();
        private GraphicsPath spur1Path = new GraphicsPath();
        private GraphicsPath spur2Path = new GraphicsPath();
        private GraphicsPath spur3Path = new GraphicsPath();
        private GraphicsPath spur4Path = new GraphicsPath();
        private int _raster, _spalten, _height, _ruestposition;
        private Font tahoma9 = new Font("Tahoma", 9F);
        private Font tahoma10B = new Font("Tahoma", 9F, FontStyle.Bold);
        private Label lbSpur1 = new Label();
        private Label lbSpur2 = new Label();
        private Label lbSpur3 = new Label();
        private Label lbSpur4 = new Label();
        private Label lbSpur = new Label();

        public ZuordnungPanel(int raster,int spalten, int height)
        {
            _raster = raster;
            _spalten = spalten;
            _height = height;
            _ruestposition = 3;
                                    
            this.Size = new Size(raster * spalten + 2, height + 2);                  
            for (int i = 0; i ≤ 8; i++)
            {
                bohrungPath.AddEllipse(_raster * 4 + i * (_raster * 2) - 5, 50, 10, 10);
                bohrungPath.AddEllipse(_raster * 4 + i * (_raster * 2) - 5, _height - 20, 10, 10);
                bohrungPath.AddString((i + 1).ToString(), FontFamily.GenericSansSerif, 
                    (int)FontStyle.Regular, 10F, 
                    new Point(_raster * 4 + i * (_raster * 2) - 6, 27), 
                    StringFormat.GenericDefault);                    
            }
            bohrungPath.AddString("Bohrung:", FontFamily.GenericSansSerif,
                (int)FontStyle.Regular, 8F,
                new Point(1, 28), StringFormat.GenericDefault);   
            
            //Spuren anlegen
            spur1Path.AddRectangle(new Rectangle(raster * 3, 0, raster * 2, 40));
            spur2Path.AddRectangle(new Rectangle(raster * 5, 0, raster * 4, 40));
            spur3Path.AddRectangle(new Rectangle(raster * 11, 0, raster * 4, 40));
            spur4Path.AddRectangle(new Rectangle(raster * 17, 0, raster * 2, 40));                

            lbSpur1.Location = new Point(raster * 3+1, 3);
            lbSpur2.Location = new Point(raster * 5+1, 3);
            lbSpur3.Location = new Point(raster * 11+1, 3);
            lbSpur4.Location = new Point(raster * 17+1, 3);
            lbSpur.Location = new Point(1, 6);

            lbSpur1.Size = new Size(raster * 2-2, 20);
            lbSpur2.Size = new Size(raster * 4-2, 20);
            lbSpur3.Size = new Size(raster * 4-2, 20);
            lbSpur4.Size = new Size(raster * 2-2, 20);
            lbSpur.Size = new Size(40, 20);
            
            lbSpur1.Font = tahoma10B;
            lbSpur2.Font = tahoma10B;
            lbSpur3.Font = tahoma10B;
            lbSpur4.Font = tahoma10B;
            lbSpur.Font = tahoma10B;

            lbSpur1.TextAlign = ContentAlignment.MiddleCenter;
            lbSpur2.TextAlign = ContentAlignment.MiddleCenter;
            lbSpur3.TextAlign = ContentAlignment.MiddleCenter;
            lbSpur4.TextAlign = ContentAlignment.MiddleCenter;

            SetRuestPosition(_ruestposition);

            lbSpur.Text = "Spur:";

            this.Controls.AddRange(new Control[] { lbSpur1, lbSpur2, lbSpur3, lbSpur4, lbSpur });  
        }

        /// <summary>
        /// Fügt der PanelList das übergebene BauteilPanel hinzu
        /// </summary>                   
        public void Add(BauteilPanel bt)
        {
            panelList.Add(bt);
            Invalidate();  
        }     

        /// <summary>
        /// Zeichnet das Zuordnungs-Panel und die in der Panellist aufgeführten BauteilPanels
        /// </summary>       
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            for (int i = 0; i ≤ _spalten-1; i++)
            {
                e.Graphics.DrawLine(Pens.Gray, (_raster * i) , 40, (_raster * i) , _height);
            }                      
            e.Graphics.FillPath(Brushes.Black, bohrungPath);
            
            e.Graphics.DrawRectangle(Pens.Gray, 0, 40, _raster*_spalten, _height - 40);

            e.Graphics.DrawPath(Pens.Gray, spur1Path);
            e.Graphics.DrawPath(Pens.Gray, spur2Path);
            e.Graphics.DrawPath(Pens.Gray, spur3Path);
            e.Graphics.DrawPath(Pens.Gray, spur4Path);
            
            foreach (BauteilPanel bp in panelList)
            {
                bp.Draw(e.Graphics);
            }
        }          

        protected override void OnMouseDown(MouseEventArgs e)
        {          
            base.OnMouseDown(e);
            for (int i = this.panelList.Count - 1; i ≥ 0; i--)
            {
                BauteilPanel bt = panelList[i];
                if (bt.Contains(e.Location))
                {
                    movingBt = bt;
                    break;
                }
            }
            lastMouseLocation = e.Location;                               
        }

         protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (movingBt != null)
            {
                movingBt.Move(e.X - lastMouseLocation.X, 0);
                lastMouseLocation = e.Location;                  
                Invalidate();                    
            }    
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);     
            SetFeederPosition(movingBt);            
            movingBt = null;       
        }

        /// <summary>
        /// Setzt die Rüstposition des Stangenförderers auf den übergebenen Wert
        /// und aktualisiert die Feederplätze
        /// </summary>                          
        public void SetRuestPosition(int ruestposition)
        {
            _ruestposition = ruestposition;
            lbSpur1.Text = (_ruestposition - 2).ToString();
            lbSpur2.Text = (_ruestposition - 1).ToString();
            lbSpur3.Text = _ruestposition.ToString();
            lbSpur4.Text = (_ruestposition + 1).ToString();              
        }

        /// <summary>
        /// Aktualisiert die Feederplatz-Eigenschaft des übergebenen BauteilPanels 
        /// </summary>             
        public void SetFeederPosition(BauteilPanel bt)
        {
            if (spur1Path.IsVisible(bt.SpurPunkt))            
            {                                            
                bt.Feederplatz = int.Parse(lbSpur1.Text);
            }
            if (spur2Path.IsVisible(bt.SpurPunkt))
            {
                bt.Feederplatz = int.Parse(lbSpur2.Text);
            }
            if (spur3Path.IsVisible(bt.SpurPunkt))
            {
                bt.Feederplatz = int.Parse(lbSpur3.Text);
            }
            if (spur4Path.IsVisible(bt.SpurPunkt))
            {
                bt.Feederplatz = int.Parse(lbSpur4.Text);
            }
        }
    }
}

ZuordnungPanel-Klasse:


using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace BauteilPanelTest
{
    class BauteilPanel
    {   
        #region Properties          
        
        public int Height { get; set; }

        public int Width { get; set; }

        public Size Size { get; set; }

        public Point Location { get; set; }

        /// <summary>
        /// Die Breite des Bauteils in Punkten
        /// </summary>
        public int BauteilBreite { get; set; }

        /// <summary>
        /// Die Position des Bauteils in Punkten
        /// </summary>
        public int BauteilPos { get; set; }

        /// <summary>
        /// Die Artikelnummer des Bauteils
        /// </summary>
        public string IdentNr { get; set; }                                                   

        /// <summary>
        /// Gibt den Feederplatz an, an dem sich das Bauteil befindet
        /// </summary>
        public int Feederplatz { get; set; }

        /// <summary>
        /// Gibt die Bohrung an, auf der die Stangenaufnahme montiert wird
        /// </summary>
        public int Bohrung { get; set; }

        public Point SpurPunkt { get; set; }             
           
        #endregion  
          
        #region Lokale Variablen
         
        private Brush transparentRedBrush = 
            new SolidBrush(Color.FromArgb(150, Color.Red));
        private Brush transperentYellowBrush = 
            new SolidBrush(Color.FromArgb(180, Color.Yellow));
        private Pen hitTestPen = new Pen(Brushes.Black, 10);       
        
        private GraphicsPath baseRectPath = new GraphicsPath();
        private GraphicsPath bauteilRectPath = new GraphicsPath();
        private GraphicsPath bohrungPath = new GraphicsPath();
        private GraphicsPath stringPath = new GraphicsPath();
        private Matrix rotateMatrix = new Matrix();
        private Matrix moveMatrix = new Matrix();
        private Point textOrigin;
        //private Point spurpunkt;
        private Rectangle rectPanel, rectBauteil, bohrung1, bohrung2;   
       
        #endregion
        /// <summary>
        /// Konstruktor der BauteilPanel-Klasse
        /// </summary>
        /// <param name="identnr">Bauteil-Identnummer</param>
        /// <param name="aufnahme">Breite der Stangenaufnahme Spalten - 1 Spalte = 1/2 Bohrung</param>
        /// <param name="bauteil">Breite des Bauteils in Spalten</param>
        /// <param name="pos">Position des Bauteils in Spalten auf der Aufnahme</param>
        /// <param name="raster">Umrechnungsfaktor von Spalten auf Punkte</param>
        /// <param name="height">Höhe des BauteilPanels in Punkten</param>
        public BauteilPanel(string identnr, int aufnahme, int bauteil, int pos, 
            int raster, int height)
        {           
            this.IdentNr = identnr;              
            this.BauteilBreite = bauteil * raster;
            this.BauteilPos = pos * raster;
            this.Height = height;
            this.Width = aufnahme * raster;
            this.Size = new Size(aufnahme * raster, this.Height);
            this.Location = new Point(0, 40);
            this.SpurPunkt = new Point(this.BauteilPos + this.BauteilBreite / 2, 10);

            rectPanel = new Rectangle(
                this.Location,
                this.Size);
            baseRectPath.AddRectangle(rectPanel);

            rectBauteil = new Rectangle(
                this.Location.X + this.BauteilPos,
                this.Location.Y,
                this.BauteilBreite, this.Height);
            bauteilRectPath.AddRectangle(rectBauteil);

            bohrung1 = new Rectangle(
                this.Location.X + this.Width / 2 - 5,
                this.Location.Y + 10,
                10, 10);
            bohrungPath.AddEllipse(bohrung1);

            bohrung2 = new Rectangle(
                this.Location.X + this.Width / 2 - 5,
                this.Location.Y + this.Height - 20,
                10, 10);
            bohrungPath.AddEllipse(bohrung2);

            textOrigin = new Point(
                this.Location.X + this.BauteilPos + this.BauteilBreite / 2 - 12,
                this.Location.Y + this.Height / 2 + (IdentNr.Length * 15) / 2);

            //GraphicsPath erstellen und Text schreiben
            stringPath.AddString(this.IdentNr, FontFamily.GenericMonospace, (int)FontStyle.Bold,
                24F, textOrigin, StringFormat.GenericDefault);
            rotateMatrix.RotateAt(270, textOrigin);
            stringPath.Transform(rotateMatrix);
        }

        public void Draw(Graphics p)
        {      
            p.FillPath(transparentRedBrush, baseRectPath);
            p.DrawPath(Pens.Black, baseRectPath);
            p.FillPath(transperentYellowBrush, bauteilRectPath);
            p.FillPath(Brushes.Black, stringPath);
            p.DrawPath(Pens.Black, bohrungPath);                 
        }

        /// <summary>
        /// Verschiebt die Position des BauteilPanels um die übergebenen Werte
        /// </summary>
        /// <param name="deltaX">Verschiebung in X-Richtung</param>
        /// <param name="deltaY">Verschiebung in Y-Richtung</param>
        public void Move(int deltaX, int deltaY)
        {
            Matrix moveMatrix = new Matrix();
            moveMatrix.Translate(deltaX, deltaY);             
            baseRectPath.Transform(moveMatrix);
            bauteilRectPath.Transform(moveMatrix);
            stringPath.Transform(moveMatrix);
            bohrungPath.Transform(moveMatrix);
            this.Location = new Point(this.Location.X + deltaX, this.Location.Y + deltaY);
            this.SpurPunkt = new Point(this.SpurPunkt.X + deltaX, this.SpurPunkt.Y);
        }

        public bool Contains(Point pt)
        {
            if (baseRectPath.IsVisible(pt))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

Ich kann jetzt also das Panel wunderbar verschieben, und die Spur auslesen, auf der es liegen bleibt.
Fehlt also nur noch das Einrasten, wobei ich dem Code von jaensen im 1. Beispiel nicht so ganz folgen kann, und das Verhindern, das die Panels übereinander liegen bleiben oder aus dem ZuordnunsPanel herausgeschoben werden können.
Letzteres sollte eigentlich mit einer Prüfung im Mover machbar sein denk ich.
private Nachricht | Beiträge des Benutzers
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2.760
Herkunft: München

beantworten | zitieren | melden

Zitat
wobei ich dem Code von jaensen im 1. Beispiel nicht so ganz folgen kann
Hmm.. dann versuche ich mich mal mit einer Erklärung in Textform.
Folgender Code ist bekanntlich für das einrasten zuständig:

         
         //
         // Snap the panel
         //
         int bestMatchX = int.MaxValue;
         int bestDistance = int.MaxValue;
         for (int i = 0; i < Width; i++)
         {
            if (i > 0 && i % 50 == 0 && i < 600)
            {
               int screwThreadX = i;
               int currentDistance = Math.Abs((panel.X + (panel.Width / 2)) - i);
               if (currentDistance ≤ bestDistance)
               {
                  bestDistance = currentDistance;
                  bestMatchX = i;
               }
               else
               {
                  break;
               }
            }
         }

         snappedIn = true;

         panel.X = bestMatchX - (panel.Width / 2);
Dieser code geht einen nicht gerade eleganten aber einfachen weg:
Es wird die gesamte Breite des Panels pixel für pixel durchlaufen

for (int i = 0; i < Width; i++)
         {
und geschaut ob an dem gerade durchwanderten Pixel eine Einrastmöglichkeit besteht


if (i > 0 && i % 50 == 0 && i < 600)
Dieser Teil kann z.B. durch eine eigene Methode ersetzt werden die z.B. auf einem eigenen Raster arbeitet (meines wäre in diesem Fall: jeder 50. Pixel ist eine Einrastposition).
Wenn eine potenzielle Einrastfunktion gefunden wurde so wird die Distanz des Bauteils zu dieser Position gemessen und der Betrag in einer Variable gespeichert.


               int currentDistance = Math.Abs((panel.X + (panel.Width / 2)) - i);
Wenn die so gefundene Entfernung geringer ist als eine vorher gemessene so wird die alte verworfen und die neue gespeichert.


               if (currentDistance ≤ bestDistance)
               {
                  bestDistance = currentDistance;
                  bestMatchX = i;
Durch die Laufvariable 'i' hat man dann die genaue X-Koordinate der Einrastposition. Wenn die schleife komplett durchlaufen wurde kennt man die kleinste distanz und deren X-Koordinate und kann das Bauteil dort positionieren.


         panel.X = bestMatchX - (panel.Width / 2);

[EDIT]Schreibfehler
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von jaensen am .
private Nachricht | Beiträge des Benutzers
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

Hab's jetzt das Einrasten doch allein hinbekommen.


public ZuordnungPanel(int raster,int spalten, int height)
        {
            _raster = raster;
            _spalten = spalten;
            _height = height;
            _ruestposition = 3;
                                    
            this.Size = new Size(raster * spalten + 2, height + 2);                  
            for (int i = 0; i ≤ 8; i++)
            {
                //obere Bohrung erzeugen
                bohrungPath.AddEllipse(_raster * 4 + i * (_raster * 2) - 5, 50, 10, 10);                
                //Bohrungsmittelpunkt in Array schreiben (X-Werte)
                bohrungen[i] = _raster * 4 + i * (_raster * 2);                 
                //untere Bohrung erzeugen
                bohrungPath.AddEllipse(_raster * 4 + i * (_raster * 2) - 5, _height - 20, 10, 10);
                //Bohrungsnummer erzeugen
                bohrungPath.AddString((i + 1).ToString(), FontFamily.GenericSansSerif, 
                    (int)FontStyle.Regular, 10F, 
                    new Point(_raster * 4 + i * (_raster * 2) - 6, 27), 
                    StringFormat.GenericDefault);                    
            }
            bohrungPath.AddString("Bohrung:", FontFamily.GenericSansSerif,
                (int)FontStyle.Regular, 8F,
                new Point(1, 28), StringFormat.GenericDefault); 

Ich schreibe im Konstruktor des ZuordnungPanels die X-Positionen der Bohrungen in ein Array.


int distance = 500;    
            if (movingBt != null)
            {
                foreach (int bohrung in bohrungen)
                {
                    //die nächstgelegene Bohrung suchen und den Abstand ermitteln
                    if (Math.Abs(distance) >
                        Math.Abs(bohrung - movingBt.Location.X - movingBt.Width / 2))
                    {
                        distance = bohrung - movingBt.Location.X - movingBt.Width / 2;
                    }
                }
                //BauteilPanel um den Abstand verschieben
                movingBt.Move(distance, 0);
                Invalidate();

Dann suche ich im MouseUp im Array nach der Bohrung mit der geringsten Distanz zum Bauteilmittelpunkt und verschiebe das Bauteil um diese Distanz.

Eigentlich ganz simpel aber man muss eben erstmal drauf kommen.
Damit erledigt sich auch das Problem, das die Bauteile aus dem Panel herausgeschoben werden können, da sie ja an den Bohrungen einrasten.

Bleibt also nur noch das Problem mit dem Überlappen und ein paar Schönheitskorrekturen (die Disposerei und das Flackern beim Verschieben).
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von buyden am .
private Nachricht | Beiträge des Benutzers
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2.760
Herkunft: München

beantworten | zitieren | melden

Zitat
...und das Flackern beim Verschieben
[Artikel] Flackernde Controls und flackerndes Zeichnen vermeiden

Das überlappen bekommst du bei rechteckigen Objekten sehr leicht durch eine Prüfung aller Ecken eines Objektes mit der Fläche des anderen Objektes raus.
[Pseudocode]
for (everyCorner of my Rectangle)
   if (otherRectangle.Contains(corner))
      //Überlappung
   else
      // Keine Überlappung
[/Pseudocode]
private Nachricht | Beiträge des Benutzers
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5.299

beantworten | zitieren | melden

Da gibts sogar Rectangle.IntersectsWith(Rectangle)
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
buyden
myCSharp.de - Member



Dabei seit:
Beiträge: 203

Themenstarter:

beantworten | zitieren | melden

Probleme behoben!

@ErfinderDesRades
Zitat
Da gibts sogar Rectangle.IntersectsWith(Rectangle)

is ne feine Sache.

Ich merk mir also beim MouseDown die Position des Panels, prüfe im MouseUp auf Überschneidung und setze im True-Fall das Bauteil an die ursprüngliche Position zurück.


//Auf überlappung prüfen und bei Bedarf das BauteilPanel an seine 
                //ursprüngliche Position zurückversetzen
                foreach (BauteilPanel bt in panelList)
                {
                    if (movingBt != bt &&
                        movingBt.PanelRectangle.IntersectsWith(bt.PanelRectangle))
                    {
                        movingBt.Move(lastBtLocation.X - movingBt.Location.X, 0);
                    }
                }

Und invalidated wird nur noch das Rectangle um das Panel rum.


protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (movingBt != null)
            {                   
                bounds = movingBt.PanelRectangle;
                //es soll ein etwas größerer Bereich neu gezeichnet werden als das Panel 
                //selbst, da sonst bei schnellen Bewegungen die Randbereiche verschwinden
                bounds.Width += 20;
                bounds.Location = new Point(bounds.Location.X - 10, bounds.Location.Y);               
                movingBt.Move(e.X - lastMouseLocation.X, 0);                                    
                lastMouseLocation = e.Location;
                Invalidate(bounds);                                                       
            }    
        }

Ich bin sehr zufrieden mit dem Ergebnis.

Danke erstmal an alle.

Für Hinweise bin ich trotzdem jederzeit dankbar.
Attachments
private Nachricht | Beiträge des Benutzers