PictureBox: wywołanie spoza klasy Form

0

Witam.

Mam następujący problem - jest sobie klasa public partial class Form1 : Form. Pomijając nieistotne elementy wygląda tak:

        private void Form1_Load(object sender, EventArgs e)
        {
            pictureBox2.Dock = DockStyle.Fill;
            pictureBox2.Paint += new PaintEventHandler(this.pictureBox2_Paint);
            this.Controls.Add(pictureBox2);
        }
 
        public void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Point[] pArray = new Point[3];
            pArray[0] = new Point(0,0);
            pArray[1] = new Point(255,255);
            pArray[2] = new Point(0,255);
            g.FillPolygon(Brushes.Black, pArray)
         }

W ten jakże prosty sposób mamy narysowany trójkącik. No tak, ale - problem w tym, że metoda ta jest wywołana przy załadowaniu formy (na początku programu), podczas gdy ja chcę załóżmy współrzędne tego trójkącika w oparciu o coś najpierw wyliczyć - i potem przekazać, aby taki a nie inny został narysowany.

Pojawiają się 2 problemy (być może związane z tym, że słabo ogarniam delegaty):

  1. Co mam zrobić/jaką (jak) metodę wywołać, aby móc "rozkaz" narysowania czegoś wydać z klasy zewnętrznej
  2. Jak przekazać odpowiednie parametry do tej metody, aby został narysowany taki trójkąt, a nie inny.

Z góry dzięki za wszelką pomoc.
Pozdrawiam

0

Zrób publiczną metodę, a współrzędne przekazuj do niej w parametrach metody. Wtedy będziesz mógł ją używać z zewnątrz. Żadne delegaty Ci tu nie są potrzebne.

0

Dzięki za odpowiedź :) No tak, to jest w sumie jasne. Z tym że kwestia tego, że:

  1. pictureBox2_Paint znajduje się z klasie public partial class Form1 : Form, i nawet jeżeli tak jak teraz jest publiczna, to z zewnątrz klasy jej nie wykonam, bo do tego musiałbym utworzyć obiekt klasy Form1, co nie ma racji bytu (jeżeli dobrze to rozumiem).

  2. Nawet przy założeniu, że takie rozwiązanie może jakoś zadziałać, mimo wszystko jeżeli np. przeciążyłbym tą metodę tak, aby pobierała jako parametry tablicę wierzchołków, to jest jeden problem - obiekt typu PaintEventArgs, który jest potrzebny aby stworzyć obiekt Graphics, którego metody wykonując zwyczajnie rysujemy. Pytanie brzmi: Co to tak właściwie jest, i co miałbym w ogóle przekazać jako ten argument, bo tak średnio rozumiem.

0

Ad 1) No to chyba jasne, że musisz mieć obiekt klasy Form1, skoro chcesz narysować na nim trójkąt. Nie można rysować trójkątów na czymś, czego nie ma (tzn. pewno matematycy potrafią, ale to raczej nie w C#). :)

Ad 2) Napisałeś metodę obsługującą zdarzenie PictureBox.Paint. Wywołuje się ona gdy kontrolka ta jest odrysowywana na ekranie, czyli np. przy otwieraniu okna Form1. Nie masz za bardzo na to wpływu.
Ja mówię, żebyś napisał sobie nową metodę, której przekażesz w parametrach np. współrzędne, i która narysuje trójkąt na tym PictureBoxie. Metody tej będziesz mógł używać z zewnątrz i będzie ona niezależna od zdarzeń okna/kontrolki.
Graphics to po prostu taki fajny obiekt, po którym można rysować. ;) Można go "zrobić" z każdej kontrolki, trzeba po prostu użyć metody CreateGraphics. Nie potrzebujesz do tego ani zdarzenia Paint ani PaintEventArgs, po prostu:

Graphics g = this.pictureBox2.CreateGraphics();

Inna rzecz - po co rysować w ten sposób na PictureBox? Czy nie lepiej utworzyć Bitmap, narysować na niej, a później dopiero wyświetlić ją w PictureBox? Chyba ładniej i schludniej.

0

Witam koledzy!
Podłączę się do tematu, bo problem bardzo zbliżony. Otóż mam do wykonania projekt testujący algorytmy wyszukiwania drogi. W założeniach projektu jeden punkt mówi o tym, że mapa jest przekazana w formie bitmapy (plik *.bmp) z naniesionymi przeszkodami i kosztami. Z przeszukiwaniem mapy sobie poradziłem, wszystko działa tak jak zakładałem, ale mam problem z prezentacją przebiegu wyszukiwania drogi. W metodzie wyszukującej drogę mam wywołanie metody UpdateMap(Point pos), która wygląda następująco:

public void UpdateMap(Point pos)
{
    if(pictureBoxMapa.InvokeRequire)
    {
        Invoke(UpdateMapDelegate(UpdateMap), new object[]{pos});
        return;
    }
    mapa = mapa.SetPixel(pos.X, pos.Y, Color.White);
    pictureBoxMapa.Image = mapa;
    pictureBoxMapa.refres();
}

Jeśli są jakieś błędy składniowe to przepraszam, bo jestem w pracy i pisze z pamięci, a cały kod mam w domu, ale błędu w składni raczej nie mam, bo kompiluje się bez problemów i przy debugowaniu tez nic nie widać. Chodzi o to, że nie aktualizuje mi mapy "w locie" podczas działania algorytmu. Ktoś może wie gdzie ukryty jest problem, lub zna estetyczniejsze rozwiązanie mojego problemu?

0

zdaje sie ze SetPixel nic nie zwraca (void SetPixel(...))
przy zalozeniu ze map jest typu Bitmap oraz ze jest podpiete jako Image do PictureBox
wystarczy:

public void UpdateMap(Point pos)
{
    if(pictureBoxMapa.InvokeRequire)
    {
        Invoke(UpdateMapDelegate(UpdateMap), new object[]{pos});
    }
    else
    {
        mapa.SetPixel(pos.X, pos.Y, Color.White);
        pictureBoxMapa.Refresh();
     }
}

zalezy jak masz reszte programu napisana, na pewno nie blokujesz glownego watku? na pewno algorytm wyznaczania drogi chodzi w osobnym watku?

0

Z SetPixel() masz rację, po prostu napisałem kod z pamięci i stąd ten kwiatek ;)
Algorytm wyszukiwania drogi leci w osobnym wątku.

Wczoraj też zauważyłem, że po pierwszym uruchomieniu algorytmy, na mapce nic nie narysowało, ale przy kolejnym uruchomieniu natychmiast na mapie wyświetliło ścieżkę wyszukiwania drogi.

Czy problemem może być bardzo częsta, zmiana pixela na bitmapie i próba jej wyświetlenia, żeby pokazać to "w locie"?

0

tak, ale w takim scenariuszu raczej zachowaloby sie to tak, ze od razu narysowaloby ci ta droge

co mozesz sprobowac zrobic, to w UpdateMap nie modyfikowac obrazka, tylko dodawac punkt do kolejki FIFO oraz odpalic timer, ktory bedzie sprawdzal czy ma cos w fifo, jesli tak, to pobiera jeden punkt i rysuje, oczywiscie musisz tez na zakonczenie algorytmu ustawic sobie jakas flage, dzieki ktorej kiedy kolejka bedzie pusta i flaga ustawiona zatrzymasz timer
da ci to zludzenie rownomiernej animacji rysowania sciezki

0

Potrzebowałbym jednak coś co umożliwiłoby zaznaczanie na mapie aktualnie badanego punktu w każdym kroku. Algorytmy wyszukiwania są sterowane parametrami i w pewnych warunkach może okazać się, że nie znajdzie drogi, ale jednak chciałbym, żeby zaznaczał punkty na bieżąco na mapie i można było zobaczyć jaki wpływ mają parametry na wybór kolejnych punktów.

Skoro picturebox jest zbyt wolny na takie coś to może lepiej byłoby wybrać directx? Z tym akurat nie miałem nigdy styczności i nie wiem czy jest tam możliwość utworzenia na formularzu kontrolki wyświetlającej grafikę 2d?

0

direct draw to czesc directX do grafiki 2D
zdaje sie ze pozniej zostal zastapiony czyms, teraz poleca sie (vista+) Direct2D

ale wg PictureBox powinien tu dawac rade, moze pokaz wiecej kodu, zeby mozna bylo ocenic co jest nie tak
mozesz tez sam ryskowac po kontrolce, na podstawie np. jakiejs tablicy

0

Dubel

0

No więc kod wygląda tak (tylko te najistotniejsze fragmenty):

         public List<Wierzcholek> ZnajdzDroge(Point start, Point goal)
         {
             czasRozpoczecia = proces.UserProcessorTime;
             Wierzcholek rodzic;
             bool znaleziono = false;

             kolejkaOpen.Clear();
             kolejkaClose.Clear();

             int[,] kierunek;
             if (przekatne)
                 kierunek = new int[8, 2] { { 0*siatka, -1*siatka }, { 1*siatka, 0*siatka }, { 0*siatka, 1*siatka }, { -1*siatka, 0*siatka }, { 1*siatka, -1*siatka }, { 1*siatka, 1*siatka }, { -1*siatka, 1*siatka }, { -1*siatka, -1*siatka } };
             else
                 kierunek = new int[4, 2] { { 0*siatka, -1*siatka }, { 1*siatka, 0*siatka }, { 0*siatka, 1*siatka }, { -1*siatka, 0*siatka } };

             rodzic.G = 0;
             rodzic.H = 1;
             rodzic.F = rodzic.G + rodzic.H;
             rodzic.WierzcholekDrogi = start;
             rodzic.Rodzic = rodzic.WierzcholekDrogi;
             kolejkaOpen.Push(rodzic);
             while (kolejkaOpen.Count > 0 && !stop)
             {
                 rodzic = kolejkaOpen.Pop();
                 if(rodzic.WierzcholekDrogi == goal)
                 {
                     kolejkaClose.Add(rodzic);
                     znaleziono = true;
                     break;
                 }
                 if (kolejkaClose.Count > (800 / siatka) * (600 / siatka))
                 {
                     stop = true;
                     return null;
                 }
                 for(int i=0; i<(przekatne ? 8 : 4); i++)
                 {
                     Wierzcholek nowyWierzcholek = new Wierzcholek();
                     nowyWierzcholek.WierzcholekDrogi.X = rodzic.WierzcholekDrogi.X + kierunek[i, 0];
                     nowyWierzcholek.WierzcholekDrogi.Y = rodzic.WierzcholekDrogi.Y + kierunek[i, 1];
                     if (nowyWierzcholek.WierzcholekDrogi.X < 0 || nowyWierzcholek.WierzcholekDrogi.Y < 0 || nowyWierzcholek.WierzcholekDrogi.X >= 800 || nowyWierzcholek.WierzcholekDrogi.Y >= 600 || mapa.GetPixel(nowyWierzcholek.WierzcholekDrogi.X, nowyWierzcholek.WierzcholekDrogi.Y).Name == "ff000000")
                         continue;

                     int noweG=0;
                     switch (mapa.GetPixel(nowyWierzcholek.WierzcholekDrogi.X, nowyWierzcholek.WierzcholekDrogi.Y).Name)
                     {
                         case "ffffffff":
                             noweG = rodzic.G + 1;
                             break;
                         case "ff22b14c":
                             noweG = rodzic.G + int.Parse(oknoGlowne.textBoxWaga1.Text);
                             break;
                         case "ffb5e61d":
                             noweG = rodzic.G + int.Parse(oknoGlowne.textBoxWaga2.Text);
                             break;
                         case "fffff200":
                             noweG = rodzic.G + int.Parse(oknoGlowne.textBoxWaga3.Text);
                             break;
                         case "ffffc90e":
                             noweG = rodzic.G + int.Parse(oknoGlowne.textBoxWaga4.Text);
                             break;
                         case "ffff7f27":
                             noweG = rodzic.G + int.Parse(oknoGlowne.textBoxWaga5.Text);
                             break;
                         case "ffed1c24":
                             noweG = rodzic.G + int.Parse(oknoGlowne.textBoxWaga6.Text);
                             break;
                         case "ff00a2e8":
                             noweG = rodzic.G + int.Parse(oknoGlowne.textBoxWaga7.Text);
                             break;
                     }
                     if (noweG == rodzic.G)
                     {
                         continue;
                     }
                     int znalezionoWOtwartejKolejce = -1;
                     for (int j = 0; j < kolejkaOpen.Count; j++)
                     {
                         if (kolejkaOpen[j].WierzcholekDrogi == nowyWierzcholek.WierzcholekDrogi)
                         {
                             znalezionoWOtwartejKolejce = j;
                             break;
                         }
                     }
                     if (znalezionoWOtwartejKolejce != -1 && kolejkaOpen[znalezionoWOtwartejKolejce].G <= noweG)
                         continue;

                     int znalezionoWZamknietejKolejce = -1;
                     for (int j = 0; j < kolejkaClose.Count; j++)
                     {
                         if (kolejkaClose[j].WierzcholekDrogi == nowyWierzcholek.WierzcholekDrogi)
                         {
                             znalezionoWZamknietejKolejce = j;
                             break;
                         }
                     }
                     if (znalezionoWZamknietejKolejce != -1 && kolejkaClose[znalezionoWZamknietejKolejce].G <= noweG)
                         continue;

                     nowyWierzcholek.Rodzic = rodzic.WierzcholekDrogi;
                     nowyWierzcholek.G = noweG;
                     nowyWierzcholek.H = Math.Abs(nowyWierzcholek.WierzcholekDrogi.X - goal.X) + Math.Abs(nowyWierzcholek.WierzcholekDrogi.Y - goal.Y);
                     nowyWierzcholek.F = nowyWierzcholek.G + nowyWierzcholek.H;
                     kolejkaOpen.Push(nowyWierzcholek);
                     oknoGlowne.UpdateMap(nowyWierzcholek.WierzcholekDrogi, mapa, Color.White);
                 }
                 kolejkaClose.Add(rodzic);
             }
             czasWyszukiwania = proces.UserProcessorTime - czasRozpoczecia;
             
             if (znaleziono)
             {
                 Wierzcholek znalezionyWierzcholek = kolejkaClose[kolejkaClose.Count - 1];
                 for (int i = kolejkaClose.Count - 1; i >= 0; i--)
                 {
                     if (znalezionyWierzcholek.Rodzic == kolejkaClose[i].WierzcholekDrogi || i == kolejkaClose.Count - 1)
                     {
                         znalezionyWierzcholek = kolejkaClose[i];
                         oknoGlowne.UpdateMap(kolejkaClose[i].WierzcholekDrogi, mapa, Color.Violet);
                         oknoGlowne.pictureBoxMapa.Refresh();
                     }
                     else
                     {
                         kolejkaClose.RemoveAt(i);
                     }
                 }
                 stop = true;
                 return kolejkaClose;
             }
             stop = true;
             return null;
         }
        public void SzukajDrogi()
        {
            bool przekatne = false;
            if (radioButtonKierunek2.Checked)
            {
                przekatne = true;
            }
            AlgorytmyAZGwiazdka AlgorytmAZGwiazdka = new AlgorytmyAZGwiazdka(mapa, siatka, przekatne);
            List<Wierzcholek> odnalezionaDroga = AlgorytmAZGwiazdka.ZnajdzDroge(new Point(int.Parse(textBoxStartPosX.Text), int.Parse(textBoxStartPosY.Text)), new Point(int.Parse(textBoxGoalPosX.Text), int.Parse(textBoxGoalPosY.Text)));
            if (odnalezionaDroga != null)
            {
                UpdateStatusLabel("Znaleziono drogę! " + AlgorytmAZGwiazdka.CalkowityCzas.Ticks);
            }
        }

        private delegate void UpdateStatusLabelDelegate(String text);
        private void UpdateStatusLabel(String text)
        {
            if (labelStatus.InvokeRequired)
            {
                Invoke(new UpdateStatusLabelDelegate(UpdateStatusLabel), new object[] { text });
                return;
            }
            labelStatus.Text = text;
        }

        public delegate void UpdateMapDelegate(Point punkt, Bitmap mapka, Color kolor);
        public void UpdateMap(Point punkt, Bitmap mapka, Color kolor)
        {
            if (pictureBoxMapa.InvokeRequired)
            {
                Invoke(new UpdateMapDelegate(UpdateMap), new object[] { punkt, mapka, kolor });
                return;
            }
            mapka.SetPixel(punkt.X, punkt.Y, kolor);
            pictureBoxMapa.Image = mapka;
            pictureBoxMapa.Update();
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            Thread watek = new Thread(new ThreadStart(SzukajDrogi));
            watek.Start();
        }

Specjalistą nie jestem, ale wydaje mi się, że to powinno działać tak jak zakładam. Po zakończeniu wyszukiwania drogi jak użyję pictureBox.Refresh() to na mapce są zaznaczone wszystkie punkty, ale w trakcie działania algorytmu to nie działa :/

0

w updateMap wystarczy mapka.SetPixel i pictureBox.Refresh() (Update() tez powinno byc ok)

problem widze w ZnajdzDroge
wywal z tej metody oknoGlowne.pictureBoxMapa.Refresh(); - refresh robi UpdateMap(...)

poza tym wydaje sie ze powinno byc ok
sprobuj najwyzej rysowac sam po kontrolce/oknie

ile wynosi czas wyszukiwania (zmienna czasWyszukiwania.TotalMilliseconds)

0

Problem rozwiązany. Nie wiem czy to jedyne rozwiązanie, albo najlepsze, ale działa i spełnia moje wymagania. Przetestowałem kilka różnych technik i okazało się, że problem występuje tylko jeśli próbujemy zmieniać kontrolkę picturebox spoza klasy okna, w którym się ona znajduje. Ja algorytmy miałem napisane w oddzielnej klasie i stąd ten problem. Gdy przepisałem je do klasy okna, to wszystko działa prawidłowo. Nie jest to zbyt estetyczne, ale działa, a to w tej chwili jest najistotniejsze.

1 użytkowników online, w tym zalogowanych: 0, gości: 1