Rysowanie kwadratów za pomocą pętli for w WPF

0

Witam,
Postanowiłem podszkolić się w programowaniu w C#. Jako iż podstawy znam (pętle, warunki, funkcje itp) pomyślałem iż najlepszym sposobem będzie zwyczajne pisanie programów. Do tego chciałbym przy okazji nauczyć się pisać w WPF.

Pierwszy program to Labirynt rysowany losowo. Mapa ma się składać z kwadratów które mogą być pustym polem, przejściem lub też ścianą. Wklejam kod.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;

namespace lab
{
    
    public partial class MainWindow : Window
    {
        const int cellPx = 10;          //cell size in pixels
        const int edgeX = 30;           //map width in celss
        const int edgeY = 30;           //map height in cells
        
        public MainWindow()
        {
            InitializeComponent();
            int[,] rectangle = new int[edgeX, edgeY];
            rectangle = createLabirynt();
            
        }

        private void drawMap (int[,] rectangle)
        {
            Canvas cPan = new Canvas();

            Rectangle rectNull = new Rectangle();
            Rectangle rectPassage = new Rectangle();
            Rectangle rectWall = new Rectangle();
            
            rectNull.Width = cellPx;
            rectNull.Height = cellPx;
            rectNull.StrokeThickness = 1;
            rectNull.Stroke = Brushes.White;
            rectNull.Fill = Brushes.Yellow;

            rectPassage.Width = cellPx;
            rectPassage.Height = cellPx;
            rectPassage.StrokeThickness = 1;
            rectPassage.Stroke = Brushes.White;
            rectPassage.Fill = Brushes.Green;

            rectWall.Width = cellPx;
            rectWall.Height = cellPx;
            rectWall.StrokeThickness = 1;
            rectWall.Stroke = Brushes.White;
            rectWall.Fill = Brushes.Red;
            
            
            cPan.Margin = new Thickness(0);
            for (int i = 0; i < edgeX; i++)
            {
                for (int j = 0; i < edgeY; j++)
                {
                    if (rectangle[i, j] == 0)
                    {
                        rectNull.Margin = new Thickness(i * cellPx, j * cellPx, 0, 0);
                        cPan.Children.Add(rectNull);

                    }
                    else if (rectangle[i, j] == 1)
                    {
                        rectPassage.Margin = new Thickness(i * cellPx, j * cellPx, 0, 0);
                        cPan.Children.Add(rectPassage);

                    }
                    else
                    {
                        rectWall.Margin = new Thickness(i * cellPx, j * cellPx, 0, 0);
                        cPan.Children.Add(rectWall);

                    }
                }
            }

            

                        
            this.Content = cPan;
        }
        
        private int [,] createLabirynt ()
        {            
            int[,] rectangle = new int[edgeX, edgeY]; //0 - null, can be changed on value 1 or 2; 1 - passage, can't be changed; 2 - wall, can't be changed;

            Random rand = new Random();
            int enterEdge;      // map edge, on which entrance will be placed;   
            Pen blackPen = new Pen(Brushes.Black, 1);
            
            int [] tEnter = new int [2];
            int [] tExit = new int [2];

            enterEdge = rand.Next(1, 5); //lottery edge: 1 - top, 2 - right, 3 - down, 4 - left;
            if(enterEdge == 1)
            {
                tEnter[0] = rand.Next(1, edgeX - 2);
                tEnter[1] = 0;
            }
            else if(enterEdge == 2)
            {
                tEnter[0] = edgeX -1;
                tEnter[1] = rand.Next(1, edgeY - 2);
            }
            else if(enterEdge == 3)
            {
                tEnter[0] = rand.Next(1, edgeX - 2);
                tEnter[1] = edgeY - 1;
            }
            else 
            {
                tEnter[0] = 0;
                tEnter[1] = rand.Next(1, edgeY - 2);
            }
            drawMap(rectangle);
            return rectangle;
        }


        private void Button_Click(object sender, RoutedEventArgs e)
        {
               
        }
    }
}

Metoda createLabirynt () aktualnie losuje tylko wejście do labiryntu. Niestety w metodzie drawMap() pojawia się error

A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll

Additional information: 'The invocation of the constructor on type 'lab.MainWindow' that matches the specified binding constraints threw an exception.' Line number '3' and line position '9'. 

Domyślam się, iż to przez kilkukrotną zmianę marginesu (może być ustawiony tylko raz dla danego obiektu?)

rectNull.Margin = new Thickness(i * cellPx, j * cellPx, 0, 0);
 cPan.Children.Add(rectNull); 

I tu potrzebuję pomocy. Nie wiem za bardzo jak rozwiązać ten problem. Wszelkie pomysły mile widziane.

Pozdrawiam

0

Proponuję przejść debuggerem cały kod i sprawdzić gdzie konkretnie (tj. jaka linijka) występuje błąd. Bo jak na razie to wiadomo tylko która funkcja jest problemem, nic więcej.

0

Uproszczony kod w którym występuje błąd:

        
private void drawMap (int[,] rectangle)
        {
            Canvas cPan = new Canvas();

            Rectangle rectNull = new Rectangle();
            Rectangle rectPassage = new Rectangle();
            Rectangle rectWall = new Rectangle();
            
            rectNull.Width = cellPx;
            rectNull.Height = cellPx;
            rectNull.StrokeThickness = 1;
            rectNull.Stroke = Brushes.White;
            rectNull.Fill = Brushes.Yellow;

            rectPassage.Width = cellPx;
            rectPassage.Height = cellPx;
            rectPassage.StrokeThickness = 1;
            rectPassage.Stroke = Brushes.White;
            rectPassage.Fill = Brushes.Green;

            rectWall.Width = cellPx;
            rectWall.Height = cellPx;
            rectWall.StrokeThickness = 1;
            rectWall.Stroke = Brushes.White;
            rectWall.Fill = Brushes.Red;


            //cPan.Margin = new Thickness(0);
            //for (int i = 0; i < edgeX; i++)
            //{
            //    for (int j = 0; i < edgeY; j++)
            //    {
            //        if (rectangle[i, j] == 0)
            //        {
            //            rectNull.Margin = new Thickness(i * cellPx, j * cellPx, 0, 0);
            //            cPan.Children.Add(rectNull);

            //        }
            //        else if (rectangle[i, j] == 1)
            //        {
            //            rectPassage.Margin = new Thickness(i * cellPx, j * cellPx, 0, 0);
            //            cPan.Children.Add(rectPassage);

            //        }
            //        else
            //        {
            //            rectWall.Margin = new Thickness(i * cellPx, j * cellPx, 0, 0);
            //            cPan.Children.Add(rectWall);

            //        }
            //    }
            //}

            rectNull.Margin = new Thickness(0, 0, 0, 0); //pierwsza pozycja pustego kwadratu (kolor żółty)
            cPan.Children.Add(rectNull);

            rectNull.Margin = new Thickness(10, 0, 0, 0); //druga pozycja pustego kwadratu - próba ustawinie go obok pierwszego
            cPan.Children.Add(rectNull); //w tym miejscu pojawia się błąd

                        
            this.Content = cPan;
        } 

I sam error

user image

0

No ale czekaj, Ty dwa razy dodajesz do obiektu Canvas obiekt rectNull. Tutaj jeżeli chcesz dodać kilka prostokątów, to musisz utworzyć każdy obiekt oddzielnie. Nie da rady wcisnąć dwóch naraz zmieniając tylko margines. To nie tak działa :)

0

No tak też podejrzewałem. W jaki więc sposób utworzyć kilkadziesiąt - kilkaset pojedynczych kwadratów?

0
aszejda napisał(a):

W jaki więc sposób utworzyć kilkadziesiąt - kilkaset pojedynczych kwadratów?

Pewno trzeba użyć pętli.

1
somekind napisał(a):

Pewno trzeba użyć pętli.

Bardzo zabawne :P Rozwiązałem problem korzystając z klasy DrawingVisual.
Pomocna okazała się książka "Pro C# 5.0 and the .Net 4.5 Framework" - Andrew Troelsen

Kod dla potomnych:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;

namespace lab
{
    
    public partial class MainWindow : Window
    {
        const int cellPx = 10;          //cell size in pixels
        const int edgeX = 30;           //map width in celss
        const int edgeY = 30;           //map height in cells

        

        public MainWindow()
        {
            InitializeComponent();
            int[,] rectangle = new int[edgeX, edgeY];
            rectangle = createLabirynt();
            
        }

        
        private int [,] createLabirynt ()
        {            
            int[,] rectangle = new int[edgeX, edgeY]; //0 - null, can be changed on value 1 or 2; 1 - passage, can't be changed; 2 - wall, can't be changed;

            Random rand = new Random();
            int enterEdge;      // map edge, on which entrance will be placed;   
            Pen blackPen = new Pen(Brushes.Black, 1);
            
            int [] tEnter = new int [2];
            int [] tExit = new int [2];

            enterEdge = rand.Next(1, 5); //lottery edge: 1 - top, 2 - right, 3 - down, 4 - left;
            if(enterEdge == 1)
            {
                tEnter[0] = rand.Next(1, edgeX - 2);
                tEnter[1] = 0;
            }
            else if(enterEdge == 2)
            {
                tEnter[0] = edgeX -1;
                tEnter[1] = rand.Next(1, edgeY - 2);
            }
            else if(enterEdge == 3)
            {
                tEnter[0] = rand.Next(1, edgeX - 2);
                tEnter[1] = edgeY - 1;
            }
            else 
            {
                tEnter[0] = 0;
                tEnter[1] = rand.Next(1, edgeY - 2);
            }
            drawMap(rectangle);
            return rectangle;
        }

        private Visual rectNull (int x, int y)
        {
            DrawingVisual drV = new DrawingVisual();

            using (DrawingContext drC = drV.RenderOpen())
            {
                Rect rect = new Rect(new Point(x,y), new Size(cellPx, cellPx));
                drC.DrawRectangle(Brushes.Yellow, null, rect);
            }
            return drV;
        }

        private Visual rectPassage(int x, int y)
        {
            DrawingVisual drV = new DrawingVisual();

            using (DrawingContext drC = drV.RenderOpen())
            {
                Rect rect = new Rect(new Point(x, y), new Size(cellPx, cellPx));
                drC.DrawRectangle(Brushes.Green, null, rect);
            }
            return drV;
        }

        private Visual rectWall(int x, int y)
        {
            DrawingVisual drV = new DrawingVisual();

            using (DrawingContext drC = drV.RenderOpen())
            {
                Rect rect = new Rect(new Point(x, y), new Size(cellPx, cellPx));
                drC.DrawRectangle(Brushes.Red, null, rect);
            }
            return drV;
        }


        private void drawMap(int[,] rectangle)
        {
            RenderTargetBitmap bmp = new RenderTargetBitmap(500, 500, 100, 100, PixelFormats.Pbgra32);


            for (int i = 0; i < edgeX; i++)
            {
                for (int j = 0; j < edgeY; j++)
                {
                    if (rectangle[i, j] == 0)
                    {
                        bmp.Render(rectNull(i * cellPx, j * cellPx));
                    }
                    else if (rectangle[i, j] == 1)
                    {
                        bmp.Render(rectPassage(i * cellPx, j * cellPx));
                    }
                    else
                    {
                        bmp.Render(rectWall(i * cellPx, j * cellPx));
                    }
                }
            }

            myImage.Source = bmp;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
               
        }
    }
} 

Xaml:

<Window x:Class="lab.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="500" Width="500">
    <Grid Margin="0,0,-8,-30">        
        <Image Name="myImage" Height="500"/>
    </Grid>
</Window> 

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