Gå til innhold

Programmere grafikk som klikkbare objekter


Anbefalte innlegg

Skrevet

Hei!

 

Jeg skal snart i gang med et prosjekt som skal bruke en del grafikk. Og i den forbindelse hadde det vært kjekt om deler av grafikken er klikkbar som objekter. Med det mener jeg: Tegn en vegg fra punkt A til punkt B. Klikkes punkt A, kan du flytte dette. Tilsvarende for punkt B. Klikker du midt på "veggen" (dvs streken) kan du flytte denne.

 

Er det noen som har gjort noe tilsvarende, og kan gi noen tips?

Videoannonse
Annonse
Skrevet (endret)

Du må bruke en array som holder orden på antall vegger og trykkpunktene til veggene.

Innholde i arrayen kan være x og y til veggen.

Også har du en egen funksjon som regner ut hvor trykkpunktene er.

Det vil si ved punkt a eller ved punkt b.

 

Om du velger å regne ut hvor trykkpunktene er samtidig som du lager veggene å ligge dette inn i arrayen med x og y ELLER regner dette ut ved clickevent kan du velge selv!

Hver linje i arrayen må også inneholde lengde å bredde på veggen.

 

Så kan du kalle funksjoner for dette:

 

#MovePointA

#MovePointB

#MoveObject

 

Du kan også ha et "object" som er heter "selectedObject", da blir det litt lettere med flytting av objektet.

Når du holder på med grafikk har du vel en konstant loop, så når du skal flytte et punkt til et objekt setter du bare musens punkter hele tiden til det punkte du klikket på, så ved andre klikk lagrer du de nye verdiene.

 

Når du klikker på selve formen må du loope igjennom arrayen og bruke en >/</if opplegg til å finne frem til hvilken vegg bite du holder musa over.

Når du finner objektet, sletter du det fra arrayen og ligger det inn i et temp object for "ObjectSelected". Også kan du oppdatere arrayen når du er ferdig å flytte på det, eller et punkt.

Den delen blir ikke så lett å implementere!

 

Det stemmer at jeg har laget noe slikt før, men da baserte hele proskjektet seg på at formen var delt inn i et "virtuellt" rutenett.

Det er nok ikke helt det samme som du er ute etter, en mer fleksibel løsning er nok det du er ute etter.

 

Kanskje du kan "arve" et annet objekt sitt egenskaper?

F.eks knapper?

 

Jeg er ikke helt inne på hvordan arv fungerer i .Net, men i og med at formen vet hvilken knapper som trykkes på så burde det være mulig å trekke noe ut av det.

 

Det er sikkert noen som har mer erfaring med dette en meg!

Noen?

 

Edit:

Formen har jo et klikkevent?

Som returnerer en pointer til objectet som ble klikket?

(Tror jeg)

Endret av chills
Skrevet (endret)

using System;
using System.Drawing;

public enum SizeMode
{
 None = 0,
 West = 1,
 North = 2,
 East = 4,
 South = 8,
 NorthWest = West | North,
 NorthEast = East | North,
 SouthWest = West | South,
 SouthEast = East | South,
 Move = 16
}

public interface IGObject
{
 Rectangle Rectangle { get; set; }
 void Resize(SizeMode grip_handle, Point increment);
 void Draw(Graphics g);
}

public class GObject : IGObject
{
 protected Rectangle m_rect;
 public virtual void Draw(Graphics g)
 {
   ' Vi bruker g.TranslateTransform(m_rect.Left, m_rect.Top)
   ' For å gjøre tegning enklere, så (0, 0) vil være posisjonen til objektet
   ' på grafikk objektet
   Rectangle new_rect = new Rectangle(0, 0, m_rect.Width, m_rect.Height);
 
   g.FillRectangle(Brushes.Yellow, m_rect)
   g.DrawRectangle(Pens.Black, m_rect);

 }
 
 void Resize(SizeMode grip_handle, Point incr)
 {
   if( (grip_handle == SizeMode.Move) )
   {
     m_rect.Offset(incr);
   }
   else if(grip_handle == SizeMode.None)
   {
     return;
   }
   else
   {
     if( (grip_handle & SizeMode.North) == SizeMode.North)
     {
       m_rect = new Rectangle(m_rect.Left, m_rect.Top - incr.Y, m_rect.Width, m_rect.Size.Height + incr.Y);
     }
     if( (grip_handle & SizeMode.South) == SizeMode.South)
     {
       m_rect = new Rectangle(m_rect.Left, m_rect.Top + incr.Y, m_rect.Width, m_rect.Size.Height - incr.Y);
     }
     if( (grip_handle & SizeMode.North) == SizeMode.West)
     {
       m_rect = new Rectangle(m_rect.Left - incr.X, m_rect.Top, m_rect.Width + incr.X, m_rect.Size.Height);
     }
     if( (grip_handle & SizeMode.East) == SizeMode.East)
     {
       m_rect = new Rectangle(m_rect.Left + incr.X, m_rect.Top, m_rect.Width - incr.X, m_rect.Size.Height);
     }
   }
 }
}

public IGObject GetObjectAtPoint(System.Collections.ObjectModel.Collection<IGObject> col, Point p, Point ScrollbarOffset)
{
 p = new Point(p.X - ScrollbarOffset.X, p.Y - ScrollbarOffset.Y);
 foreach(IGObject obj in col)
 {
   if(obj.Rectangle.Contains(p)
     return obj;
 }
}

 

her er et eksempel

Inherit GObject og override Draw funksjonen for å tegne noe annet

 

Og slik tegner man alle:

 

public void Draw(Graphics g, Collection<IGObject> col, Point scrollbaroffset)
{
 System.Drawing.Drawing2D.Matrix mat;
 g.TranslateTransform(scrollbaroffset);
 mat = (System.Drawing.Drawing2D.Matrix)g.Transform.Clone();
 foreach(IGObject obj in col)
 {
   g.TranslateTransform(obj.Rectangle.Left, obj.Rectangle.Top);
   obj.Draw(g);
   g.Transform = (System.Drawing.Drawing2D.Matrix)mat.Clone();
 }
}

Endret av GeirGrusom
Skrevet

Takk for mye fancy kode, Geir. :)

Kan du nå forklare hvordan den brukes? ;)

 

 

Ved

Public Class GObject
   Inherits IGObject

får jeg at "Classes can inherit only from other classes".

Skrevet (endret)

Ikke double buffer hvis du ikke trenger, det tar mer ram, og er mye tregere.

Hold all tegning inne i OnPaint eller Paint event, og alt ser glimrende fantastisk ut, aldri bruk Control.CreateGraphics() (hvis du ikke trenger en hDC da, i såfall må du huske å kalle ReleaseDC() før du avslutter)

Endret av GeirGrusom
Skrevet (endret)

Litt fikling og tukling med koden, kom jeg frem til denne klassen:

Public Class Line
   Inherits GObject
   Protected m_pen As Pen
   Public Property Color() As Color
       Get
           Return m_pen.Color
       End Get
       Set(ByVal value As Color)
           m_pen.Color = value
       End Set
   End Property
   Public Overrides Sub Draw(ByVal g As System.Drawing.Graphics)
       g.DrawLine(m_pen, 0, 0, m_rect.Width, m_rect.Height)
   End Sub
   Public Overrides Sub New()
       m_pen = Pens.Black
   End Sub
End Class

 

Ekstremt simpelt! Men akkurat det jeg trenger! ;)

 

 

 

Men her er det noe som er litt merkelig. Ta en linje som vist over. Dra høyresiden mot venstre til den er på andre siden av "venstresiden".

 

Linjen blir fremdeles tegnet, men er plutselig ikke klikkbar! Tipper det har noe med SeletionHelper -> Create å gjøre, men om du har noen gode tips før jeg herjer rundt, så tar jeg gladelig i mot dem! :)

Endret av moskus
Skrevet

Grunnen er at Rectangle ikke kan være negative i størrelse, jeg har ikke lagt inn noen sjekkrutiner på dette, men i .NET 1.1 fikk du exception hvis dette skjedde, men ikke nå lenger.

 

Dette er faktisk en liten kødden rutine, fordi du må begynne å lage rektanglen fra Right til Left, og/eller Bottom til Top, men det er ikke uovervinnelig.

 

Jeg har fikset problemet, jeg har lagt til 2 egenskaper på GObject, som er m_p1, og m_p2 som er (x1, y1)-(x2, y2)

og en funksjon som heter CreateRectangle, og byttet ut GObject.Resize

som følger:

 

legg til dette i SelectionHelper.vb

      ' Denne ligger i SelectionHelper hos meg
Public Shared Function CreateRectangle(ByVal p1 As Point, ByVal p2 As Point) As Rectangle
 Dim x As Integer, y As Integer
 Dim w As Integer, h As Integer

 w = p2.X - p1.X
 h = p2.Y - p1.Y
 x = p1.X
 y = p1.Y

 If (w < 0) Then
 	x += w
 	w = -w ' Bytt fortegn, vi vet det er negativt fra før
 End If

 If (h < 0) Then
 	y += h
 	h = -h ' Bytt fortegn, vi vet det er negativt fra før
 End If

 Return New Rectangle(x, y, w, h)
End Function

 

GObject

' Egenskaper
Protected m_p1 As Point
Protected m_p2 As Point

Public Property Rectangle() As System.Drawing.Rectangle Implements IGObject.Rectangle
 Get
 	Return SelectionHelper.CreateRectangle(m_p1, m_p2)
 End Get
 Set(ByVal value As System.Drawing.Rectangle)
 	m_rect = value
 	m_p1 = m_rect.Location
 	m_p2 = New Point(m_rect.Right, m_rect.Bottom)
 End Set
End Property
' GObject.Resize
Public Sub Resize(ByVal grip_handle As SizeMode, ByVal incr As Point) Implements IGObject.Resize
 Dim x1 As Integer, y1 As Integer
 Dim x2 As Integer, y2 As Integer

 If grip_handle = SizeMode.None Then
 	Exit Sub
 Else

 	x1 = m_p1.X
 	x2 = m_p2.X

 	y1 = m_p1.Y
 	y2 = m_p2.Y

 	If grip_handle = SizeMode.Move Then
   x1 += incr.X
   x2 += incr.X

   y1 += incr.Y
   y2 += incr.Y
 	Else
   If (grip_handle And SizeMode.North) = SizeMode.North Then y1 += incr.Y

   If (grip_handle And SizeMode.South) = SizeMode.South Then y2 += incr.Y

   If (grip_handle And SizeMode.East) = SizeMode.East Then x2 += incr.X

   If (grip_handle And SizeMode.West) = SizeMode.West Then x1 += incr.X

 	End If

 	m_p1 = New Point(x1, y1)
 	m_p2 = New Point(x2, y2)

 	m_rect = SelectionHelper.CreateRectangle(m_p1, m_p2)

 End If
End Sub

 

Det som skjer her nå er at jeg har lagt til m_p1 og m_p2 som er aboslutt verdier for størrelsen til objektet, men beholdt m_rect for at koden fortsatt skal fungere andre steder.

Og CreateRectangle lager et Rectangle objekt som alltid er positivt i størrelse.

 

Hyggelig å være til hjelp, moskus! :)

Skrevet

Takk igjen! Det der forstod jeg faktisk! :p

 

 

 

Hyggelig å være til hjelp, moskus! :)

Ja visst faen er du det! :)

 

Nå kan jeg gå tilbake til å "bare" bekymre meg om matematikken i programmet (foreløpig)... :hmm::blush:

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...