Zoom on Canvas in WPF C# - c#

I implemented Canvas and ToolBar (in WPF C#) and vector graphics editor like Paint almost. I have a problem with the implementation of the Zoom. As I understand the Zoom:
0) I choose the tool ZoomTool;
1) I choose a point on Canvas;
2) I click on it and all the necessary work happens here :
1. Moving (shifting) the selected point to the center of Canvas;
2. Zoom in 2 times (as an example) relative to the center
The problem is the implementation of this work (shift and zoom in), how to do it?
enter image description here

This is my code:
class RecZoomTool : Tool
{
public RecZoomTool()
{
}
public override void MouseDown(Point mousePosition)
{
Point pointDirection = new Point(Creator.CanvasWidth / 2, Creator.CanvasHeight /2);
Creator.ShearingZoom(Point.Subtract(pointDirection, mousePosition));
Creator.Zooming(2);
}
}
then --->
public static void Zooming(double delta)
{
foreach (var figure in Figures)
{
for (int j = 0; j < figure.coordinates.Count; j++)
{
figure.coordinates[j] = new Point(
figure.coordinates[j].X * scaleZoom,
figure.coordinates[j].Y * scaleZoom
);
}
}
// scaleZoom += delta;
}
public static void ShearingZoom(Point dPoint, Point mPoint)
{
foreach (var figure in Figures)
{
Vector delta = Point.Subtract(mPoint, dPoint);
for (int j = 0; j < figure.coordinates.Count; j++)
{
figure.coordinates[j] = new Point(dPoint.X, dPoint.Y);
//counterZoom++;
//delta = Point.Subtract(figure.coordinates[j], dPoint);
figure.coordinates[j] = Point.Add(figure.coordinates[j], delta);
}
offsetPosition += delta;
}
}

Correct answer for every MouseClick:
public override void MouseDown(Point mousePosition)
{
double valueZoom = 2.0;
Point pointDirection = new Point(0.0, 0.0);
Creator.Shifting(Point.Subtract(pointDirection, mousePosition));
Creator.Zooming(valueZoom);
}
And there's in main class:
public static int scaleZoom = 2;
public static Vector offsetPosition = new Vector(0.0, 0.0);
public static int counterZoom = 0;
public static void Zooming(double scaleZoom)
{
foreach (var figure in Figures)
{
for (int j = 0; j < figure.coordinates.Count; j++)
{
figure.coordinates[j] = new Point(
figure.coordinates[j].X * scaleZoom,
figure.coordinates[j].Y * scaleZoom
);
}
}
}
public static void Shifting(Vector delta)
{
offsetPosition = delta / scaleZoom;
foreach (var figure in Figures)
{
for (int j = 0; j < figure.coordinates.Count; j++)
{
figure.coordinates[j] = Point.Add(figure.coordinates[j], offsetPosition);
}
}
}

Related

C# OutOfRangeException that should be impossible

I am so confessed right now I don't even know how to properly form this question.
I have some code (as shown below) that is run on a different thread with the variable i not being referenced anywhere else that could interfere with it here. I just don't understand what is happening to cause this error, I put some watchers down with visual studio code and the values all seem to be fine and in range but almost randomly out of nowhere I will get this error.
Is it possible that this is caused by another section of code despite to my best knowledge being completely isolated from it? I even went as far to rename all other uses of i to different letters and I still get this problem.
Is it just something with for loops?
Am I somehow modifying i without even know it?
I just don't even know what to ask.
Here is the full code
`
using SFML.Graphics;
using SFML.Window;
using System.Diagnostics;
using System.Numerics;
namespace RenderEngine
{
public class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
PhysicsEngine re = new PhysicsEngine();
for (int i = 0; i < 100; i++)
{
Debug.WriteLine(i);
}
}
}
public class PhysicsEngine
{
Solver solver = new Solver();
public void RenderEngine2()
{
ContextSettings settings = new ContextSettings();
settings.AntialiasingLevel = 8;
RenderWindow window = new RenderWindow(new VideoMode(2000, 1000), "Poop", Styles.Default, settings);
SFML.Graphics.Color color = new SFML.Graphics.Color(0, 0, 0, 0);
window.SetVerticalSyncEnabled(true);
CircleShape shape = new CircleShape(5);
shape.FillColor = new SFML.Graphics.Color(0, 0, 255, 255);
window.Closed += handelclose;
int drawcount = 0;
while (window.IsOpen)
{
window.Clear(color);
window.DispatchEvents();
try
{
foreach (Partical partical in solver.Particals)
{
shape.Position = new SFML.System.Vector2f((partical.position.X), (partical.position.Y));
window.Draw(shape);
drawcount++;
}
drawcount = 0;
}
catch
{
Debug.WriteLine("notready yet");
}
window.Display();
}
void handelclose(object sender, EventArgs e)
{
window.Close();
Environment.Exit(Environment.ExitCode);
}
}
List<Partical> todraw = new List<Partical>();
void EngineLoop()
{
while (true)
{
foreach (Partical partical in solver.Particals)
{
int x = (int)Math.Round(partical.position.X/10);
int y = (int)Math.Round(partical.position.Y/10);
List<int> ts = solver.Grid[x, y];
if (ts != null)
{
for (int brokenint = 0; brokenint < ts.Count; brokenint++)
{
Debug.WriteLine(partical.ID);
Debug.WriteLine(ts[brokenint]);
if (partical.ID != ts[brokenint])
{
Vector2 pos = new Vector2(partical.ID, ts[brokenint]);
if (solver.Collision.Count > 0)
{
if (!solver.Collision.Contains(pos))
solver.Collision.Add(pos);
}
else
{
solver.Collision.Add(pos);
}
}
}
}
}
}
}
private int particalcount = 10;
bool canstart = false;
public PhysicsEngine()
{
Parallel.Invoke(() =>
{
while (!canstart) { Thread.Sleep(100); }
Debug.WriteLine("third thread");
RenderEngine2();
},
() =>
{
while (!canstart) { Thread.Sleep(100); }
Debug.WriteLine("engine started");
EngineLoop();
},
() =>
{
Debug.WriteLine("first thread");
PhysicsLoop(this);
}
);
}
void PhysicsLoop(PhysicsEngine PhyEng)
{
int frames = 0;
long second = 0;
PhysicsStart(PhyEng);
Thread.Sleep(1000);
Stopwatch sw = Stopwatch.StartNew();
solver.startupdate();
while (true)
{
sw.Start();
todraw = solver.Particals;
solver.update();
frames++;
if (second != (Stopwatch.GetTimestamp() / Stopwatch.Frequency))
{
second = (Stopwatch.GetTimestamp() / Stopwatch.Frequency);
Debug.WriteLine(frames);
frames = 0;
}
sw.Stop();
sw.Reset();
if (sw.ElapsedMilliseconds < 15)
Thread.Sleep(15 - (int)sw.ElapsedMilliseconds);
}
}
void PhysicsStart(PhysicsEngine phyeng)
{
for (int i = 0; i < 210; i++)
{
for (int j = 0; j < 110; j++)
{
solver.Grid[i,j] = new List<int>();
}
}
Random rand = new Random();
for (int i = 0; i < particalcount; i++)
{
Partical partical = new Partical();
partical.position = new Vector2(rand.Next(0, 2000), rand.Next(0, 1000));
partical.oldposition = partical.position;
partical.ID = i;
int x1 = (int)Math.Round((partical.position.X + 5) / 10);
int y1 = (int)Math.Round((partical.position.Y + 5) / 10);
int x2 = (int)Math.Round((partical.position.X - 5) / 10);
int y2 = (int)Math.Round((partical.position.Y - 5) / 10);
solver.Grid[x1, y1].Add(partical.ID);
solver.Grid[x2, y1].Add(partical.ID);
solver.Grid[x1, y2].Add(partical.ID);
solver.Grid[x2, y2].Add(partical.ID);
solver.Particals.Add(partical);
}
canstart = true;
}
}
public class Partical
{
public Vector2 position = new Vector2(0, 0);
public Vector2 oldposition = new Vector2(0, 0);
public Vector2 acceleration = new Vector2(0, 0);
Vector2 zero = new Vector2(0, 0);
public int ID = new int();
public void updatePosition(float sub)
{
Vector2 velocity = position - oldposition;
oldposition = position;
position = position + (velocity * 0.9f) + acceleration * sub;
acceleration = zero;
}
public void accelerate(Vector2 accel)
{
acceleration = acceleration + accel;
}
}
public class Solver
{
public List<Partical> Particals = new List<Partical>();
public List<Vector2> Collision = new List<Vector2>();
public List<int>[,] Grid = new List<int>[2100,1100];
public void update()
{
int subcount = 8;
float sub = 1f / (float)subcount;
for (int i = 0; i < subcount; i++)
{
applyGravity(sub);
updatePositions(sub);
for (int j = 0; j < Collision.Count; j++)
{
solvecolisions((int)Collision[j].X, (int)Collision[j].Y);
}
Collision.Clear();
}
}
public void startupdate()
{
applyGravity(0.5f);
updatePositions(0.5f);
applyGravity(0.5f);
updatePositions(0.5f);
}
void updatePositions(float sub)
{
foreach (Partical partical in Particals)
{
partical.updatePosition(sub);
int x1 = (int)Math.Round((partical.oldposition.X + 5) / 10);
int y1 = (int)Math.Round((partical.oldposition.Y + 5) / 10);
int x2 = (int)Math.Round((partical.oldposition.X - 5) / 10);
int y2 = (int)Math.Round((partical.oldposition.Y - 5) / 10);
Grid[x1,y1].Remove(partical.ID);
Grid[x2,y1].Remove(partical.ID);
Grid[x1,y2].Remove(partical.ID);
Grid[x2,y2].Remove(partical.ID);
x1 = (int)Math.Round((partical.position.X + 5) / 10);
y1 = (int)Math.Round((partical.position.Y + 5) / 10);
x2 = (int)Math.Round((partical.position.X - 5) / 10);
y2 = (int)Math.Round((partical.position.Y - 5) / 10);
Grid[x1,y1].Add(partical.ID);
Grid[x2,y1].Add(partical.ID);
Grid[x1,y2].Add(partical.ID);
Grid[x2,y2].Add(partical.ID);
}
}
void applyGravity(float sub)
{
float gravitystrangth = -0.1f;
foreach (Partical partical in Particals)
{
float a = partical.position.Y;
float b = partical.position.X;
b -= 1000;
a -= 500;
double angle = Math.Atan2(a, b);
float newA = gravitystrangth * (float)(Math.Sin(angle));
float newB = gravitystrangth * (float)(Math.Sin((Math.PI / 180) * 90 - angle));
Vector2 gravity = new Vector2(newB, newA);
partical.accelerate(gravity);
}
}
void solvecolisions(int id1, int id2)
{
Partical part = Particals[id1];
Partical part2 = Particals[id2];
if (part != part2)
{
Vector2 colisionaxis = part.position - part2.position;
float dist = colisionaxis.Length();
if (dist < 10)
{
Vector2 n = colisionaxis / dist;
float delta = 10 - dist;
part.position += 0.5f * delta * n;
part2.position -= 0.5f * delta * n;
}
}
}
public List<Partical> GetParticals()
{
return Particals;
}
}
}
`
You ts list depends on solver.Grid[x,y]. I am sure that some of your code that is not visible on the screen makes some changes of solver.Grid, probably deletes some times, or replaces them. This is what causing the error.

how can I add more convex hulls in one picture

i'm doing homework in Visual Studio 2017.
I have to create a convex hull and then add more convex hulls to it, without changing others. So I find out how to make one convex hull but I don't know how to add more in this code. I have to create new botton (Add convex hull) in a code and then I don't know how to continue.
Thank you for all your answers.
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
Point zacasna = new Point();
zacasna.X = e.X;
zacasna.Y = e.Y;
seznamTock.Add(zacasna);
pictureBox1.Image = Image.FromFile(file);
using (var g = Graphics.FromImage(pictureBox1.Image))
{
for (int i = 0; i < seznamTock.Count; i++)
{
g.DrawEllipse(barva_tock, seznamTock[i].X, seznamTock[i].Y,
5, 5);
}
pictureBox1.Refresh();
}
}
private List<Point> konveksnaLupina(List<Point> tocka)
{
List<Point> hull = new List<Point>();
Point vPointOnHull = tocka.Where(p => p.X == tocka.Min(min =>
min.X)).First();
Point vEndpoint;
do
{
hull.Add(vPointOnHull);
vEndpoint = tocka[0];
for (int i = 1; i < tocka.Count; i++)
{
if ((vPointOnHull == vEndpoint)
|| (Orientacija(vPointOnHull, vEndpoint, tocka[i]) ==
-1))
{
vEndpoint = tocka[i];
}
}
vPointOnHull = vEndpoint;
}
while (vEndpoint != hull[0]);
return hull;
}
private static int Orientacija(Point p1, Point p2, Point p)
{
// Determinanta
int Orin = (p2.X - p1.X) * (p.Y - p1.Y) - (p.X - p1.X) * (p2.Y -
p1.Y);
if (Orin > 0)
{
return -1; //orientacija v levo
}
if (Orin < 0)
{
return 1; //orientacija v desno
}
return 0; //neutralna orientacija
}
private void button2_Click(object sender, EventArgs e)
{
if (seznamTock.Count < 3)
{
MessageBox.Show("Premalo tock!");
return;
}
List<Point> hull = new List<Point>();
hull = konveksnaLupina(seznamTock);
pictureBox1.Image = Image.FromFile(file);
using (var g = Graphics.FromImage(pictureBox1.Image))
{
for (int j = 0; j < seznamTock.Count; j++)
{
g.DrawEllipse(barva_tock, seznamTock[j].X, seznamTock[j].Y,
5, 5);
}
for (int j = 0; j < hull.Count; j++)
{
Point tocka1 = new Point(hull[j].X, hull[j].Y);
Point tocka2 = new Point(hull[(j + 1) % hull.Count].X,
hull[(j + 1) % hull.Count].Y);
g.DrawLine(barva_kon_lupine, tocka1, tocka2);
}
for (int j = 0; j < hull.Count; j++)
{
g.DrawEllipse(barva_tock_kon_lupine, hull[j].X, hull[j].Y,
`enter code here`5, 5);
}
pictureBox1.Refresh();
}
}
}
add new points in to the new list, but without changing previous figure
You are thinking List<Point> is figure. Well, it's not. It's a list of points. Figures may have more properties and if you have several figures stored as array of points - how would you know which point belong to which figure?
OOP can make it more clear:
abstract class Figure
{
public abstract void Draw(Graphics graphics);
}
class Convex: Figure
{
public List<Point> Points { get; set; }
public Color Color { get; set; } = Color.Black;
... // more properties
public override void Draw(Graphics graphics)
{
if(Points != null)
using(var pen = new Pen(Color))
graphics.DrawLines(pen, Points.ToArray());
}
}
... // more figures
You can hold different figures in List<Figure> and simply add new figure to it:
class Billboard
{
public List<Figure> Figures { get; } = new List<Figure>();
public void Add(Figure figure) => Figures.Add(figure);
... // more methods
}
Redraw:
Billboard _billboard = new Billboard();
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach(var figure in _billboard.Figures)
figure.Draw(e.Graphics);
}
I like Sinatrs approach. But, here's an alternative, just using a collection and your existing code. It's not, the best mod. Think, it does what you want.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
Collection<List<Point>> test = new Collection<List<Point>>();
List<Point> seznamTock = new List<Point>();
Pen barva_tock = new Pen(Color.Blue);
Pen barva_kon_lupine = new Pen(Color.Black);
Pen barva_tock_kon_lupine = new Pen(Color.Yellow);
public Form1()
{
InitializeComponent();
test.Add(seznamTock);
}
private List<Point> konveksnaLupina(List<Point> tocka)
{
List<Point> hull = new List<Point>();
Point vPointOnHull = tocka.Where(p => p.X == tocka.Min(min =>
min.X)).First();
Point vEndpoint;
do
{
hull.Add(vPointOnHull);
vEndpoint = tocka[0];
for (int i = 1; i < tocka.Count; i++)
{
if ((vPointOnHull == vEndpoint)
|| (Orientacija(vPointOnHull, vEndpoint, tocka[i]) ==
-1))
{
vEndpoint = tocka[i];
}
}
vPointOnHull = vEndpoint;
}
while (vEndpoint != hull[0]);
return hull;
}
private static int Orientacija(Point p1, Point p2, Point p)
{
// Determinanta
int Orin = (p2.X - p1.X) * (p.Y - p1.Y) - (p.X - p1.X) * (p2.Y - p1.Y);
return (Orin == 0) ? Orin : ((Orin > 0) ? -1 : 1);
//if (Orin > 0)
//{
// return -1; //orientacija v levo
//}
//if (Orin < 0)
//{
// return 1; //orientacija v desno
//}
//return 0; //neutralna orientacija
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
Point zacasna = new Point();
zacasna.X = e.X;
zacasna.Y = e.Y;
seznamTock.Add(zacasna);
//pictureBox1.Image = Image.FromFile(file);
using (var g = Graphics.FromImage(pictureBox1.Image))
{
for (int i = 0; i < seznamTock.Count; i++) { g.DrawEllipse(barva_tock, seznamTock[i].X, seznamTock[i].Y, 5, 5); }
pictureBox1.Refresh();
}
}
private void button1_Click(object sender, EventArgs e)
{
//if (seznamTock.Count < 3)
//{
// MessageBox.Show("Premalo tock!");
// return;
//}
List<Point> hull = new List<Point>();
hull = konveksnaLupina(seznamTock);
//pictureBox1.Image = Image.FromFile(file);
using (var g = Graphics.FromImage(pictureBox1.Image))
{
for (int j = 0; j < seznamTock.Count; j++)
{
g.DrawEllipse(barva_tock, seznamTock[j].X, seznamTock[j].Y,
5, 5);
}
for (int j = 0; j < hull.Count; j++)
{
Point tocka1 = new Point(hull[j].X, hull[j].Y);
Point tocka2 = new Point(hull[(j + 1) % hull.Count].X,
hull[(j + 1) % hull.Count].Y);
g.DrawLine(barva_kon_lupine, tocka1, tocka2);
}
for (int j = 0; j < hull.Count; j++)
{
g.DrawEllipse(barva_tock_kon_lupine, hull[j].X, hull[j].Y, 5, 5);
}
pictureBox1.Refresh();
test.Add(seznamTock);
seznamTock = new List<Point>();
}
}
}
}

Unity / C#: Close GUI when when clicking outside of it

How do I close a GUI-menue when clicking outside of it?
What would be the smartest way to do it?
Here a picture of a two-stage GUI example:
And the belonging code, feel free to use it for your own purpose:
using UnityEngine;
using System.Data;
using Mono.Data.SqliteClient;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using UnityEngine.EventSystems;
public class menue2 : MonoBehaviour
{
private Vector2 scrollViewVector = Vector2.zero;
static int menueWidth = 125;
static int menueHeight = 25;
Rect dropDownRect1 = new Rect(0,0,menueWidth,menueHeight);
Rect dropDownRect2 = new Rect (menueWidth, menueHeight, 170, menueHeight);
// Menue Level 1
public static string[] list = {"Navigation","Setup Ordner", "Optionen Ordner", "Setup Dateien", "Optionen Dateien", "blabla"};
// Menue Level 2
public static string[] camSetup = {"Kamera Zoom Min.", "Kamera Zoom Max.", "Kamera Geschw. Normal", "Kamera Geschw. Schnell"};
public static string[] setupOrdner = {"Farbe = ","Höhe = ","setupFolder3","setupFolder4","setupFolder5","setupFolder6"};
public static string[] optionenOrdner = {"setupFolder1","setupFolder2","setupFolder3","setupFolder4","setupFolder5","set upFolder6"};
public static string[] setupDatei = {"oooooo","ppp","üüüüüüüüü","qqqqqq","nnnnnnn","mmmm"};
public static string[] optionenDatei = {"wwwww","eeeee","rrrrr","tttt","zzzzzzz","uuuuuu"};
public static string[] blabla = {"aaaaa","ssssss","gggg","ddddd","jjjjjj","hhhhh"};
public static string[][] listCollection = {camSetup, setupOrdner, optionenOrdner, setupDatei, optionenDatei, blabla};
int indexNumber;
int indexNumber2;
int level = 0;
string lastClicked = null;
public GameObject other;
void OnGUI()
{
if (GUI.Button (new Rect ((dropDownRect1.x), dropDownRect1.y, dropDownRect1.width, dropDownRect1.height), "Setup")) {
if (level == 0) {
level = 1;
lastClicked = "Setup";
} else if (level > 0)
level = 0;
}
if (level >= 1) {
for (int index = 0; index < list.Length; index++) {
if (GUI.Button (new Rect (0, (index * 25 + dropDownRect1.height), dropDownRect1.width, dropDownRect1.height), list [index])) {
indexNumber = index;
level = 2;
lastClicked = (list [index]);
}
}
if (level == 2 || level == 3) {
for (int index = 0; index < camSetup.Length; index++) {
if (GUI.Button (new Rect (menueWidth, ((indexNumber * menueHeight) + (index * menueHeight) + menueHeight), menueWidth, menueHeight), listCollection [indexNumber] [index])) {
indexNumber2 = index;
level = 3;;
lastClicked = (listCollection [indexNumber] [index]);
}
}
}
}
if (Input.GetMouseButtonUp (0)) {
print (lastClicked);
}
}
}
Thanks a lot for any help!
I stayed with scripting my GUI in the end and found a quite simple solution.
void closeMenue()
{
Vector2 mousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
if (Input.GetMouseButtonDown (0) && mousePosition.x > Screen.width/2 || Input.GetMouseButtonDown (0) && mousePosition.y < Screen.height/2)
{
level = 0; // closing everything
}
}
So I simply check for the cursor position.. here adjusted to a menue on the top left.
But the allowed area can easily be changed to the personal situation of course.

Program 'for loop' is changing multiple values in the array in one instance

I was working line by line in the debugger option (red dot) in express 2010 visual c#.
This loop was assiging creatures[2].sprite.position and creatures[3].sprite.position to the same vector (100,320) at the same instance, when i equaled 2.
Further testing showed that when i = 3 the same would happen, then on i = 4, all 2,3, and 4 were changed at the same time to the same vector.
any suggestions on whats causing this, it feels like a bug, thinking about it now, I should try a different variable than i, maybe its being used some where else.
public class GameImages
{
//Image Diminsions
public Texture2D texture;
//Images position on the Viewport
public Vector2 position = new Vector2(0,0);
//Color
public Microsoft.Xna.Framework.Color color = Microsoft.Xna.Framework.Color.White;
}
public class CreaturesAttributes
{
//Image
public GameImages sprite;
//Level
public double Level;
}
CreaturesAttributes[] creatures = new CreaturesAttributes[100];
public void LoadTeam()
{
int j = 0;
for (int i = 1; i < creatures.Length; i++)
{
if (creatures[i].Color1 != null)
{
creatures[i].sprite.position = new Vector2(j, 320);
j += 100;
}
else
{
i = creatures.Length;
}
}
}
protected override void Initialize()
{
for (int i = 0; i < creatures.Length; i++)
{
creatures[i] = new CreaturesAttributes();
creatures[i].sprite = new GameImages();
}
}
This happens because you probably initialized all your array elements to point to a single instance:
var newCreature = new Creature(/* ... */);
for (int i = 0; i < creatures.Length; i++) {
creatures[i] = newCreature;
}
Or perhaps you created multiple creatures, but with the same "sprite":
var defaultSprite = new Sprite(/* ... */);
for (int i = 0; i < creatures.Length; i++) {
creatures[i] = new Creature();
creatures[i].sprite = defaultSprite;
}
or maybe sprite or position are static fields:
static Sprite sprite;
//or
static Vector2 position;
All the above will cause all your mutations and accesses to happen to the same instance, so it looks like changing one is changing all of them (while there in reality is just a single creature/sprite/position that you're referencing from multiple places).
The solution is to ensure that the fields are not static, and make sure to create a new creature and creature.sprite for each:
// No static fields, and new instances for each index
for (int i = 0; i < creatures.Length; i++) {
creatures[i] = new Creature();
creatures[i].sprite = new Sprite();
}

Why do I get the error "NullReferenceException was unhandled"?

I am trying to make a slider puzzle game and I keep getting the error "NullReferenceException was unhandled" when I call myBoard.paint(e.Graphics) in my form1. Please help me!!!
Here is my code for Form1 (Let me know if I need to post some of my other classes code):
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;
using System.Diagnostics;
namespace SliderPuzzle
{
public partial class Form1 : Form
{
private int tileSize;
private int rowsCols;
private SlidePuzzle myBoard;
private Stopwatch timer;
private int moveCount;
public Form1()
{
InitializeComponent();
pictureBox1.TabIndex = 3;
pictureBox1.Size = new Size(100, 50);
pictureBox1.Location = new Point(16, 71);
pictureBox1.BackColor = Color.PaleGreen;
pictureBox1.BorderStyle = BorderStyle.Fixed3D;
pictureBox1.TabStop = false;
tileSize = imageList1.ImageSize.Width;
rowsCols = 3;
pictureBox1.Width = rowsCols * tileSize;
pictureBox1.Height = rowsCols * tileSize;
}
public void initGame()
{
myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1);
timer = new Stopwatch();
moveCount = 0;
timer.Start();
}
private void Form1_Load(object sender, EventArgs e)
{
initGame();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
this.myBoard.paint(e.Graphics);
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (myBoard.move(e.Y / tileSize, e.X / tileSize))
++moveCount;
Refresh();
if (!myBoard.winner())
return;
timer.Stop();
if (MessageBox.Show(string.Format("You won!!\nIt took you {0} moves and {1:F2} seconds.\nPlay again?", (object)moveCount, (object)timer.Elapsed.TotalSeconds), "Game Over", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.No)
{
Close();
}
else
{
initGame();
Refresh();
}
}
}
}
Update #1: Okay, so I moved myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1); to my constructor, but now none of the images are showing up on it. Here is what It looks like vs what it is supposed to look like:
Edit #2: Okay, I moved it back to where it was before and put
if (this.myBoard != null)
this.myBoard.paint(e.Graphics);
instead, and it works a little better and looks better as well. But the images not showing up is still a problem.
Edit #3: Here is the SliderPuzzle.Paint Code:
public void paint(Graphics g)
{
for (int r = 0; r < this.myGrid.getNumRows(); ++r)
{
for (int c = 0; c < this.myGrid.getNumCols(); ++c)
this.myGrid.get(new Location(r, c)).paint(g);
}
}
Edit #4: Here is the code for the SliderPuzzle Class:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace SliderPuzzle
{
internal class SlidePuzzle
{
private static Random rand = new Random();
private int myTileSize;
private BoundedGrid myGrid;
private ImageList myImages;
private Location myBlankLoc;
static SlidePuzzle()
{
}
public SlidePuzzle(int rowsCols, int tileSize, ImageList images)
{
this.myTileSize = tileSize;
this.myGrid = new BoundedGrid(rowsCols, rowsCols);
this.myImages = images;
this.myBlankLoc = new Location(rowsCols - 1, rowsCols - 1);
this.initBoard();
}
private void initBoard()
{
int index1 = 0;
for (int r = 0; r < this.myGrid.getNumRows(); ++r)
{
for (int c = 0; c < this.myGrid.getNumCols(); ++c)
{
this.myGrid.put(new Location(r, c), new Tile(index1, this.myTileSize, new Location(r, c), this.myImages.Images[index1]));
++index1;
}
}
for (int index2 = 0; index2 < 1000; ++index2)
{
Location adjacentLocation = this.myBlankLoc.getAdjacentLocation(SlidePuzzle.rand.Next(4) * 90);
if (this.myGrid.isValid(adjacentLocation))
{
this.swap(this.myBlankLoc, adjacentLocation);
this.myBlankLoc = adjacentLocation;
}
}
}
public bool move(int row, int col)
{
Location loc1 = new Location(row, col);
if (Math.Abs(this.myBlankLoc.getRow() - row) + Math.Abs(this.myBlankLoc.getCol() - col) != 1)
return false;
this.swap(loc1, this.myBlankLoc);
this.myBlankLoc = loc1;
return true;
}
public bool winner()
{
int num = 0;
for (int r = 0; r < this.myGrid.getNumRows(); ++r)
{
for (int c = 0; c < this.myGrid.getNumCols(); ++c)
{
if (this.myGrid.get(new Location(r, c)).getValue() != num)
return false;
++num;
}
}
return true;
}
private void swap(Location loc1, Location loc2)
{
Tile tile1 = this.myGrid.put(loc2, this.myGrid.get(loc1));
Tile tile2 = this.myGrid.put(loc1, tile1);
tile1.setLocation(loc1);
tile2.setLocation(loc2);
}
public void paint(Graphics g)
{
for (int r = 0; r < this.myGrid.getNumRows(); ++r)
{
for (int c = 0; c < this.myGrid.getNumCols(); ++c)
this.myGrid.get(new Location(r, c)).paint(g);
}
}
}
}
Update #5: Here is the Tile Class:
using System.Drawing;
namespace SliderPuzzle
{
internal class Tile
{
private int myValue;
private int mySize;
private Location myLoc;
private Image myImage;
public Tile(int value, int tileSize, Location loc, Image img)
{
this.myValue = value;
this.mySize = tileSize;
this.myLoc = loc;
this.myImage = img;
}
public int getValue()
{
return this.myValue;
}
public void setLocation(Location newLoc)
{
this.myLoc = newLoc;
}
public void paint(Graphics g)
{
g.DrawImage(this.myImage, this.myLoc.getCol() * this.mySize, this.myLoc.getRow() * this.mySize);
}
}
}
Edit #6: Here is the Location Class:
namespace SliderPuzzle
{
internal class Location
{
public const int LEFT = -90;
public const int RIGHT = 90;
public const int HALF_LEFT = -45;
public const int HALF_RIGHT = 45;
public const int FULL_CIRCLE = 360;
public const int HALF_CIRCLE = 180;
public const int AHEAD = 0;
public const int NORTH = 0;
public const int NORTHEAST = 45;
public const int EAST = 90;
public const int SOUTHEAST = 135;
public const int SOUTH = 180;
public const int SOUTHWEST = 225;
public const int WEST = 270;
public const int NORTHWEST = 315;
private int row;
private int col;
public Location(int r, int c)
{
this.row = r;
this.col = c;
}
public int getRow()
{
return this.row;
}
public int getCol()
{
return this.col;
}
public Location getAdjacentLocation(int direction)
{
int num1 = (direction + 22) % 360;
if (num1 < 0)
num1 += 360;
int num2 = num1 / 45 * 45;
int num3 = 0;
int num4 = 0;
if (num2 == 90)
num3 = 1;
else if (num2 == 135)
{
num3 = 1;
num4 = 1;
}
else if (num2 == 180)
num4 = 1;
else if (num2 == 225)
{
num3 = -1;
num4 = 1;
}
else if (num2 == 270)
num3 = -1;
else if (num2 == 315)
{
num3 = -1;
num4 = -1;
}
else if (num2 == 0)
num4 = -1;
else if (num2 == 45)
{
num3 = 1;
num4 = -1;
}
return new Location(this.getRow() + num4, this.getCol() + num3);
}
public bool equals(Location other)
{
if (this.getRow() == other.getRow())
return this.getCol() == other.getCol();
else
return false;
}
public int hashCode()
{
return this.getRow() * 3737 + this.getCol();
}
public int compareTo(Location otherLoc)
{
if (this.getRow() < otherLoc.getRow())
return -1;
if (this.getRow() > otherLoc.getRow())
return 1;
if (this.getCol() < otherLoc.getCol())
return -1;
return this.getCol() > otherLoc.getCol() ? 1 : 0;
}
public string toString()
{
return "(" + (object)this.getRow() + ", " + (string)(object)this.getCol() + ")";
}
}
}
Edit #7: Here is the last class, the BoundedGrid Class:
using System;
using System.Collections.Generic;
namespace SliderPuzzle
{
internal class BoundedGrid
{
private Tile[,] occupantArray;
public BoundedGrid(int rows, int cols)
{
this.occupantArray = new Tile[rows, cols];
}
public int getNumRows()
{
return this.occupantArray.GetLength(0);
}
public int getNumCols()
{
return this.occupantArray.GetLength(1);
}
public bool isValid(Location loc)
{
if (0 <= loc.getRow() && loc.getRow() < this.getNumRows() && 0 <= loc.getCol())
return loc.getCol() < this.getNumCols();
else
return false;
}
public List<Location> getOccupiedLocations()
{
List<Location> list = new List<Location>();
for (int r = 0; r < this.getNumRows(); ++r)
{
for (int c = 0; c < this.getNumCols(); ++c)
{
Location loc = new Location(r, c);
if (this.get(loc) != null)
list.Add(loc);
}
}
return list;
}
public Tile get(Location loc)
{
if (!this.isValid(loc))
throw new Exception("Location " + (object)loc + " is not valid");
else
return this.occupantArray[loc.getRow(), loc.getCol()];
}
public Tile put(Location loc, Tile obj)
{
if (!this.isValid(loc))
throw new Exception("Location " + (object)loc + " is not valid");
if (obj == null)
throw new NullReferenceException("obj == null");
Tile tile = this.get(loc);
this.occupantArray[loc.getRow(), loc.getCol()] = obj;
return tile;
}
public Tile remove(Location loc)
{
if (!this.isValid(loc))
throw new Exception("Location " + (object)loc + " is not valid");
Tile tile = this.get(loc);
this.occupantArray[loc.getRow(), loc.getCol()] = (Tile)null;
return tile;
}
}
}
Edit #8: When I click on the picturebox, the program crashes and it says the the timer.Stop(); in form1 gives me a NullReferenceException!!!
Edit #9: Okay, THAT worked... I have found that the images still do not show up, but I think that they are never being placed on the grid. When I click on the grid ( still has no images) It says that I have won. This should only display after I move the tiles into the correct order. Any idea what is going on?
Edit #10: My program finally works now! It turns out I had something missplaced in the constructor of form1, now everything works! The images show up and everything! How cool is that!!!
THANK YOU EVERYONE FOR YOU CONTRIBUTIONS, I WILL NOW GET A GREAT GRADE ON MY SCHOOL PROJECT!
This is one of those problems that will give you headaches. Trying to nail down the exact sequence of events is fine, when you can guarantee that the events are never going to be invoked out of sequence. Unfortunately the Paint event is one that gets fired at all sorts of odd times, any may be fired even before the Load event.
The only real answer is to never rely on events being fired in a particular sequence. Always check that your myBoard object is valid before trying to paint it:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (this.myBoard != null)
this.myBoard.paint(e.Graphics);
}
And yes, as others have noted, it is better to create all of your objects as soon as possible - in the constructor for instance, which is what a constructor is for after all. Even then, you can still get events being fired by actions in the constructor that will ruin your whole day. Your even handlers should be set as late as possible in the constructor code to account for this.
Rule(s) of thumb:
if you're creating custom objects that will be used in event handlers, always try to create and initialize them prior to calling InitializeComponent in the constructor.
Wire up your event handlers as late as possible, especially for things like Paint - it won't be useful before your constructor is done, so do it last in the constructor.
Your actual Form1_Load event never gets called (the one where you are initializing your board).
Add this code at the end of the Form1 constructor this.Load +=Form1_Load;
Your pictureBox1.Paint event is raised before your Form1.Load event. Move
myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1);
to your constructor and you should be fine.

Categories

Resources