9 svar
78 visningar
Andy500 behöver inte mer hjälp
Andy500 7
Postad: 28 apr 13:12

Aktivera dubbelbuffering för tabpage i C# (skoluppgift)

Hej!

Har en inlämningsuppgift (programmering 1) och vet inte riktigt vad jag ska göra. Har testat och experimenterat runt lite med att bland annat flytta runt raderna, men har ändå inte fått till det. Har även sökt runt lite andra forum utan att bli klokare.

Inlämningsuppgiften går ut på att skapa ett "skjutspel" med en måltavla samt ett sikte. Därefter ska det gå att skjuta på måltavlan och räkna poäng med mera, men det kommer jag till så småningom.

Det som jag har problem med är att få bort flimmret. Siktet ska röra sig efter muspekaren och då "målas" ju måltavlan och siktet upp igen från början, vilket skapar väldigt mycket flimmer på skärmen, något som jag vill få bort. Tidigare när måltavlan och siktet ritats upp i form1 så har jag löst detta med raden "this.DoubleBuffered = true;". Dock verkar detta inte funka när måltavlan och siktet ritas upp på en tabpage. Vet inte riktigt hur jag ska göra.

Bifogar all min kod här nedan (klassen för måltavla och sikte samt "form-koden"). Får ursäkta den röriga strukturen som uppstått när jag testat och experimenterat runt.

Tacksam för all form av hjälp! 

Form.cs:

public partial class Form1 : Form
{
     Måltavla mål;
     Sikte sikte;             
     public Form1()
     {
         InitializeComponent();
 
         //Skapa ett sikt-objekt med namnet sikte och ge det positionen 0, 0 och storleken 20
         sikte = new Sikte(200, 200, 20);
 
         //Aktivera dubbelbuffering
         this.DoubleBuffered = true;
     }

     private void Form1_Load(object sender, EventArgs e)
     {
         //Sätt höjd och bredd till skärmstorleksvärden
         int höjd = Screen.PrimaryScreen.Bounds.Height;
         int bredd = Screen.PrimaryScreen.Bounds.Width;
 
         //Sätt storleken på form1 till skärmstorleken
         this.Size = new Size(bredd, höjd);
 
         //Placera måltavlan i mitten av skärmen
         mål = new Måltavla(ClientSize.Width / 2, ClientSize.Height / 2, 100);           
 
         //Fyll tabcontrol i form1
         this.tabControl1.Dock = DockStyle.Fill;
 
         //Uppdatera kontrollen med det senaste resultatet.
         tabPage1.Paint += tabPage1_Paint;           
         //Maximera fönstret
         this.WindowState = FormWindowState.Maximized;
     }   
     private void tabPage1_Paint(object sender, PaintEventArgs e)
     {              
         mål.Rita(e.Graphics);
         sikte.Rita(e.Graphics);           
     }
 
     private void tabPage1_MouseMove(object sender, MouseEventArgs e)
     {         
         sikte.X = e.X;
         sikte.Y = e.Y;
         tabPage1.Invalidate();
     }
}

Sikte.cs:

internal class Sikte
{
     //Medlemsdata
     private int x = 0;
     private int y = 0;
     private int radie = 0;
 
     //Konstruktor
     public Sikte(int x, int y, int r)
     {
         this.X = x;
         this.Y = y;
         this.Radie = r;
     }
 
     //Egenskaper
     public int X
     {
         get
         {
             return x;
         }
 
         set
         {
             x = value;
         }
     }
 
     public int Y
     {
         get
         {
             return y;
         }
 
         set
         {
             y = value;
         }
     }
 
     public int Radie
     {
         get
         {
             return radie;
         }
 
         set
         {
             if (value > 0)
             {
                 radie = value;
             }
             else
             {
                 radie = -value;
             }
         }
     }
 
     public Point Position
     {
         get
         {
             return new Point(x, y);
         }
         set
         {
             x = value.X;
             y = value.Y;
         }
     }
 
     public void Flytta(int dx, int dy)
     {
         x = x + dx;
         y = y + dy;
     }
 
     //Metoder
     public void Rita(Graphics g)
     {
         int r = radie;
 
         g.DrawEllipse(Pens.Green, x - r, y - r, r * 2, r * 2);
 
         r = (int)(radie * 0.6);
 
         g.DrawEllipse(Pens.Green, x - r, y - r, r * 2, r * 2);
 
         r = (int)(radie * 0.4);
 
         g.DrawLine(Pens.Green, x - (3 * r), y, x - r, y);
 
         g.DrawLine(Pens.Green, x + (3 * r), y, x + r, y);
 
         g.DrawLine(Pens.Green, x, y - (3 * r), x, y - r);
 
         g.DrawLine(Pens.Green, x, y + (3 * r), x, y + r);
 
         r = (int)(radie * 0.1);
 
         g.DrawLine(Pens.Purple, x - r, y, x + r, y);
 
         g.DrawLine(Pens.Purple, x, y - r, x, y + r);
     }
}

Måltavla.cs:

internal class Måltavla
{
     //Medlemsdata
     private int x = 0;
     private int y = 0;
     private int radie = 0;
 
     //Konstruktor
     public Måltavla(int x, int y, int r)
     {
         this.X = x;
         this.Y = y;
         this.Radie = r;
     }
 
     //Egenskaper
     public int X
     {
         get
         {
             return x;
         }
 
         set
         {
             x = value;
         }
     }
 
     public int Y
     {
         get
         {
             return y;
         }
 
         set
         {
             y = value;
         }
     }
 
     public int Radie
     {
         get
         {
             return radie;
         }
 
         set
         {
             if (value > 0)
             {
                 radie = value;
             }
             else
             {
                 radie = -value;
             }
         }
     }
     public Point Position
     {
         get
         {
             return new Point(x, y);
         }
         set
         {
             x = value.X;
             y = value.Y;
         }
     }
 
     public void Flytta(int dx, int dy)
     {
         x = x + dx;
         y = y + dy;
     }
 
     //Metoder
     public void Rita(Graphics g)
     {
         int r = radie;
 
         g.DrawEllipse(Pens.Black, x - r, y - r, r * 2, r * 2);
 
         g.FillEllipse(Brushes.Black, x - r, y - r, r * 2, r * 2);
 
         r = (int)(radie * 0.833);
 
         g.FillEllipse(Brushes.White, x - r, y - r, r * 2, r * 2);
 
         r = (int)(radie * 0.666);
 
         g.FillEllipse(Brushes.Black, x - r, y - r, r * 2, r * 2);
 
         r = (int)(radie * 0.499);
 
         g.FillEllipse(Brushes.White, x - r, y - r, r * 2, r * 2);
 
         r = (int)(radie * 0.333);
 
         g.FillEllipse(Brushes.Black, x - r, y - r, r * 2, r * 2);
 
         r = (int)(radie * 0.166);
 
         g.FillEllipse(Brushes.Red, x - r, y - r, r * 2, r * 2);
 
     }
 
}

 

sictransit 1073 – Livehjälpare
Postad: 28 apr 14:10 Redigerad: 28 apr 14:12

DoubleBuffered = true är rätt väg att gå, om jag minns rätt. Nu var det länge sedan jag kodade grafik över huvud taget, men jag gjorde ett litet testskott. 

Min tanke var att det är ju formuläret som dubbelbuffrar, inte nödvändigtvis komponenterna på det. Istället för att rita på en TabPage, så provade jag att rita direkt på formuläret. Då fungerar det fint och flimrar inte det minsta. Här är en nedbantad version av din kod:

    public partial class Form1 : Form
    {
        Måltavla mål;
        Sikte sikte;

        public Form1()
        {
            InitializeComponent();

            sikte = new Sikte(200, 200, 20);

            this.DoubleBuffered = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {            
            int höjd = Screen.PrimaryScreen.Bounds.Height;
            int bredd = Screen.PrimaryScreen.Bounds.Width;
            
            this.Size = new Size(bredd, höjd);
            
            mål = new Måltavla(ClientSize.Width / 2, ClientSize.Height / 2, 100);
            
            MouseMove += Form1_MouseMove;
            
            this.WindowState = FormWindowState.Maximized;
        }

        private void Form1_MouseMove(object? sender, MouseEventArgs e)
        {
            sikte.X = e.X;
            sikte.Y = e.Y;

            Invalidate();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            mål.Rita(e.Graphics);
            sikte.Rita(e.Graphics);
        }
    }
Andy500 7
Postad: 28 apr 14:27

Ja, men precis! Tänkte också lite att det är formuläret som dubbelbuffrar och inte komponenterna som gör det. Att rita upp allt i formuläret och få det att dubbelbuffra är inga problem. Det är just att rita upp det i en tabpage och få det att dubbelbuffra.

Tror inte jag kan ta bort tabcontrol eftersom jag vill kunna ha en separat flik med "spelarinställningar" där man kan bestämma storlek på måltavlan, siktet, skotten, antal skott, spelläge m.m. Vet inte om det finns ett annat sätt att ha en eller flera separata flikar utan att behöva använda tabcontrol.

sictransit 1073 – Livehjälpare
Postad: 28 apr 15:07 Redigerad: 28 apr 15:11

Då får vi väl skapa vår egen control, så vi kommer åt att sätta doublebuffered=true.

Två ändringar:

  1. Deklarera en subklass till TabPage och slå på det i konstruktorn.
  2. Skapa inte nya instanser av TabPage utan av DoubleBufferedTabPage.

(Sedan skall alla klasser ha sin egen fil förstås. Ser att du har lite duplicerad kod i dina andra också.)

    partial class Form1
    {
        private void InitializeComponent()
        {
            tabControl1 = new TabControl();
            tabPage1 = new DoubleBufferedTabPage();
            tabPage2 = new DoubleBufferedTabPage();

... massa kod ...

            ResumeLayout(false);
        }       
    }

    public partial class DoubleBufferedTabPage: TabPage
    {
        public DoubleBufferedTabPage()
        {
            DoubleBuffered = true;
        }
    }
Andy500 7
Postad: 28 apr 16:39

Hänger inte riktigt med. Ska jag skapa en till klass?

sictransit 1073 – Livehjälpare
Postad: 28 apr 16:43 Redigerad: 28 apr 16:46

Japp!

Du skapar en till klass DoubleBufferedTabPage som ärver av TabPage.

    public partial class DoubleBufferedTabPage: TabPage
    {
        public DoubleBufferedTabPage()
        {
            DoubleBuffered = true;
        }
    }

Det enda den skall göra är att sätta DoubleBuffered = true i sin konstruktor.

Sedan i din befintliga metod InitializeComponent() har du redan nu kodrader som skapar nya instanser av TabPage. Den koden ändrar du så att man istället skapar nya DoubleBufferedTabPage.

        private void InitializeComponent()
        {
            tabControl1 = new TabControl();
            tabPage1 = new DoubleBufferedTabPage(); // Här stod det new TabPage() tidigare.
            tabPage2 = new DoubleBufferedTabPage();

Eftersom DoubleBufferedTabPage är en subklass till TabPage får man göra så och det påverkar inte övrig funktionalitet.

(Du kanske funderar på varför du inte bara kan sätta DoubleBuffered=true på din befintliga TabPage? Då får du läsa på om "protected" och vad det innebär.)

Andy500 7
Postad: 28 apr 17:04 Redigerad: 28 apr 17:09

Har nu skapat en klass med namnet DoubleBufferedTabPage och koden som du skrev.

Dock osäker var jag ska sätta in den här koden:

 private void InitializeComponent()
        {
            tabControl1 = new TabControl();
            tabPage1 = new DoubleBufferedTabPage(); // Här stod det new TabPage() tidigare.
            tabPage2 = new DoubleBufferedTabPage();

Ska jag lägga in den i Form1.Designer.cs eller i Form1.cs?

("Protected" och dess innebörd har jag faktiskt ingen koll på tyvärr. Inget som läraren gått igenom eller något som det står om i kursboken.)

sictransit 1073 – Livehjälpare
Postad: 28 apr 17:09 Redigerad: 28 apr 17:10

I din Form1.Designer.cs borde du redan ha kod som ser ut ungefär så här:,

 private void InitializeComponent()
        {
            tabControl1 = new TabControl();
            tabPage1 = new TabPage();
            tabPage2 = new TabPage();

Där byter du ut new TabPage() mot den nya klass du skapat, typ new DoubleBufferedTabPage().

Andy500 7
Postad: 28 apr 17:13

Hittade det! Allt verkar funka som det ska och utan flimmer. Tack så mycket för hjälpen!

DoubleBuffered är protected, istället för public, vilket betyder att man inte kommer åt den flaggan utifrån.

Man kan alltså inte sätta tabPage1.DoubleBuffered=true.

Däremot kommer man åt saker som är protected från subklasser. Därför skapar vi en sådan subklass och sätter flaggan. Det är allt vi gör. Vår subklass fungerar precis exakt som en TabPage, förutom att flaggan är satt.

Sedan använder vi den subklassen istället för TabPage och .NET märker aldrig skillnaden.

Svara
Close