Form move inside client desktop area - c#

My goal is to prevent partial or total hidden of my form when user move it through mouse.
For instance, if my desktop resolution is 1024x768 the following will be "x/y" minimum/maximum value of coordinates for form location properties: 0, 1024 - form.width, 0, 768 - form.height.
I use some native APi to accomplish all this and all works fine, but often I notice a bad refresh (flickering) of my form (I'm managing all on form_move event).
It seems like SuspendLayout method doesn't works fine, or else the form move event doesn't fire every pixel changed, but maybe more than one (see the below code to realize what I mean).
My code looks like this:
private void Form1_Move(object sender, EventArgs e)
{
this.SuspendLayout();
// Desktop Resolution (API utility)
int x = Desktop.GetXResolution();
int y = Desktop.GetYResolution();
// Taskbar Info (API utility)
Taskbar tb = new Taskbar();
int minX = 0;
int maxX = x - this.Width;
int minY = 0;
int maxY = y - this.Height;
if (!tb.AutoHide)
{
if (tb.Position != TaskbarPosition.Unknown && !tb.Size.IsEmpty)
{
if (tb.Position == TaskbarPosition.Top) minY += tb.Size.Height;
switch (tb.Position)
{
case TaskbarPosition.Top: minY = tb.Size.Height; break;
case TaskbarPosition.Bottom: maxY -= tb.Size.Height; break;
case TaskbarPosition.Left: minX = tb.Size.Width; break;
case TaskbarPosition.Right: maxX -= tb.Size.Width; break;
}
}
}
// Restore X Position
if (this.Location.X < minX) this.Location = new Point(minX, this.Location.Y);
if (this.Location.X > maxX) this.Location = new Point(maxX, this.Location.Y);
// Restore Y Poistion
if (this.Location.Y < minY) this.Location = new Point(this.Location.X, minY);
if (this.Location.Y > maxY) this.Location = new Point(this.Location.X, maxY);
this.ResumeLayout(false);
}
As I already said, often when I restore Location property my winForm take a flickering.
Any suggestion will be appreciated.

Move occurs after a move has completed. The form has moved and then you move it back. It's going to flicker anyway.
Instead, prevent it from moving in the first place:
private const int WM_MOVING = 0x216;
private void WriteTheRect(IntPtr dest, Rectangle rect) {
System.Runtime.InteropServices.Marshal.WriteInt32(dest, 0, rect.Left);
System.Runtime.InteropServices.Marshal.WriteInt32(dest, 4, rect.Top);
System.Runtime.InteropServices.Marshal.WriteInt32(dest, 8, rect.Right);
System.Runtime.InteropServices.Marshal.WriteInt32(dest, 12, rect.Bottom);
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_MOVING)
{
// RECT structure pointed to by lParam: left, top, right, bottom
Rectangle r = Rectangle.FromLTRB(System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 0),
System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 4),
System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 8),
System.Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 12)
);
Rectangle allowed = Rectangle.FromLTRB(0, 0, 1600, 900);
if (r.Left <= allowed.Left || r.Top <= allowed.Top || r.Right >= allowed.Right || r.Bottom >= allowed.Bottom)
{
int offset_x = r.Left < allowed.Left ? (allowed.Left - r.Left) : (r.Right > allowed.Right ? (allowed.Right - r.Right) : (0));
int offset_y = r.Top < allowed.Top ? (allowed.Top - r.Top) : (r.Bottom > allowed.Bottom ? (allowed.Bottom - r.Bottom) : (0));
r.Offset(offset_x, offset_y);
WriteTheRect(m.LParam, r);
}
}
base.WndProc(ref m);
}
Obviously, replace the constants with your actual desired bounds.

Related

How to segmentation object c#

I used this code for segmentation, I'm trying to detect pixels one by one because my object is a binary, not a grayscale. when i run the program, it draws 2 object. The first object is successfully drawn (object still has a black color and a red rectangle), but the second object fails get drawn. Screenshot is here. Please help me, why does this happen?
#region Edge Detection
private void btnSegmentasi_Click(object sender, EventArgs e)
{
Segments = new List<ImageSegment>();
Bitmap bmp = (Bitmap)pb2.Image;
imageArea = new Rectangle(0, 0, pb2.Image.Width - 1, pb2.Image.Height - 1);
for (int y = 0; y < pb2.Image.Height; y++)
{
for (int x = 0; x < pb2.Image.Width; x++)
{
bool skip = false;
foreach (ImageSegment segment in Segments)
{
if (pointIsInRect(x, y, segment.Rect))
{
skip = true;
break;
}
}
if (skip) continue;
Color warna = bmp.GetPixel(x, y);
if (warna.G == 0)
startEdgeDetection(x, y, ref bmp);
}
}
DGVProses.DataSource = Segments;
if (Segments.Count > 0)
{
Graphics g = pb2.CreateGraphics();
Rectangle[] rects = (from theSegment in Segments select theSegment.Rect).ToArray();
g.DrawRectangles(new Pen(Brushes.Red), rects);
g.Dispose();
}
}
private void startEdgeDetection(int x, int y, ref Bitmap bmp)
{
Point startPoint = new Point(x, y);
Point currPoint = new Point(x, y);
int sudut = 180;
int xMin = x, yMin = y, xMax = x, yMax = y;
do
{
sudut -= 45;
Point offset = angleToPoint(ref sudut);
Point trialPoint = new Point(currPoint.X + offset.X, currPoint.Y + offset.Y);
if (!pointIsInRect(trialPoint.X, trialPoint.Y, imageArea))
continue;
Color theColor = bmp.GetPixel(trialPoint.X, trialPoint.Y);
if (theColor.G == 0)
{
currPoint = trialPoint;
sudut -= 180;
if (currPoint.X > xMax)
xMax = currPoint.X;
else if (currPoint.X < xMin)
xMin = currPoint.X;
if (currPoint.Y > yMax)
yMax = currPoint.Y;
else if (currPoint.Y < yMin)
yMin = currPoint.Y;
if (sudut < 0)
sudut += 360;
if (currPoint == startPoint && sudut == 180)
break;
}
}
while (!(currPoint == startPoint && sudut == 180));
Rectangle r = new Rectangle(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
Bitmap newImage = new Bitmap(r.Width + 2, r.Height + 2);
using (Graphics g = Graphics.FromImage(newImage))
{
g.FillRectangle(Brushes.White, 0, 0, newImage.Width, newImage.Height);
g.DrawImage(bmp, new Rectangle(1, 1, r.Width, r.Height), r, GraphicsUnit.Pixel);
g.Dispose();
}
Segments.Add(new ImageSegment(r, newImage));
}
private Point angleToPoint(ref int sudut)
{
if (sudut < 0)
sudut += 360;
switch (sudut)
{
case 135: return new Point(-1, -1);
case 90: return new Point(0, -1);
case 45: return new Point(1, -1);
case 0: return new Point(1, 0);
case 315: return new Point(1, 1);
case 270: return new Point(0, 1);
case 225: return new Point(-1, 1);
default: return new Point(-1, 0);
}
}
private bool pointIsInRect(int x, int y, Rectangle rect)
{
if (x < rect.X)
return false;
if (x > rect.X + rect.Width)
return false;
if (x < rect.Y)
return false;
if (x > rect.Y + rect.Height)
return false;
return true;
}
#endregion
Okay, I think I've now got a clue of how your algorithm is supposed to work. I'd guess you are running around in circles within the object. I do not really know why it does not happen for the first object, but this is another story.
When you enter startEdgeDetection you start at some point, check if it's black, move by an angle and repeat the whole procedure. You stop when the current point reaches the starting point. The crux is, that this algorithm does not guarantee to walk the whole object, but may just do the following (I do not know it is exactly like this, but pretty much):
OOOOOO
O####O
O####O
OOOOOO
OOOOOO
O*###O
O####O
OOOOOO
OOOOOO
O**##O
O####O
OOOOOO
OOOOOO
O**##O
O#*##O
OOOOOO
OOOOOO
O**##O
O**##O
OOOOOO
O = pixels filled with white
# = pixels filled with black
* = pixels you stepped through
You've reached your starting point again and the algorithm stops, but the bounding box does not contain the whole object, but just a part. If all of your objects bounding boxes have either a width or a height of 1 you fill up your whole object with bounding boxes, hence it appears red.
You'll have to fix the startEdgeDetection to avoid the described case and make sure that you really detect the edge.
I made up a simple class that finds the bounding box of an object. It should be easy to apply it to your problem.
public class BoundingBoxCalculator
{
Bitmap bitmapToCalculateBoundingBoxFor;
Point startingPoint;
Point[] neighborOffsets =
{new Point(-1,-1),
new Point(0,-1),
new Point(1,-1),
new Point(-1, 0),
new Point(1, 0),
new Point(-1,1),
new Point(0,1),
new Point(1,1)};
public BoundingBoxCalculator(Bitmap bitmapContainingObject, Point borderPoint)
{
this.bitmapToCalculateBoundingBoxFor = bitmapContainingObject;
this.startingPoint = borderPoint;
}
public Rectangle CalculateBoundingBox()
{
List<Point> edgePoints = CalculateEdge();
int minX = edgePoints.Min(p => p.X);
int maxX = edgePoints.Max(p => p.X);
int minY = edgePoints.Min(p => p.Y);
int maxY = edgePoints.Max(p => p.Y);
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
}
List<Point> CalculateEdge()
{
List<Point> edgePoints = new List<Point>();
Point currentPoint = startingPoint;
do
{
IEnumerable<Point> neighboringEdgePoints = GetNeighboringEdgePoints(currentPoint);
IEnumerable<Point> neighboringEdgePointsNotVisited = from p in neighboringEdgePoints where !edgePoints.Contains(p) select p;
edgePoints.Add(currentPoint);
if(neighboringEdgePointsNotVisited.Count() == 0
&& neighboringEdgePoints.Contains(startingPoint))
{
currentPoint = startingPoint;
}
else if(neighboringEdgePointsNotVisited.Count() == 1)
{
Point nextPoint = neighboringEdgePointsNotVisited.First();
currentPoint = nextPoint;
}
else if(neighboringEdgePointsNotVisited.Count() > 1)
{
Point nextPoint = GetPointWithMinDistance(currentPoint, neighboringEdgePointsNotVisited);
currentPoint = nextPoint;
}
else
{
throw new Exception();
}
} while(currentPoint != startingPoint);
return edgePoints;
}
Point GetPointWithMinDistance(Point origin, IEnumerable<Point> pointsToTest)
{
double minDistance = double.MaxValue;
Point pointWithMinDistance = new Point(0,0);
foreach(Point pointToTest in pointsToTest)
{
double currentDistance = GetPointsDistance(origin, pointToTest);
if(currentDistance < minDistance)
{
minDistance = currentDistance;
pointWithMinDistance = pointToTest;
}
}
return pointWithMinDistance;
}
double GetPointsDistance(Point p1, Point p2)
{
return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}
IEnumerable<Point> GetNeighboringEdgePoints(Point currentPoint)
{
IEnumerable<Point> neighboringPoints = GetNeighboringPoints(currentPoint);
List<Point> neighboringEdgePoints = new List<Point>();
foreach(Point pointToTest in neighboringPoints)
{
if(GetNeighboringPoints(pointToTest).Count() < 8)
{
neighboringEdgePoints.Add(pointToTest);
}
}
return neighboringEdgePoints;
}
IEnumerable<Point> GetNeighboringPoints(Point currentPoint)
{
List<Point> neighbors = new List<Point>();
for(int offsetsCount = 0; offsetsCount < neighborOffsets.Length; offsetsCount++)
{
Point currentPointWithOffset = AddPointOffset(currentPoint, neighborOffsets[offsetsCount]);
if(IsInImage(currentPointWithOffset) &&
IsInObject(currentPointWithOffset))
{
neighbors.Add(currentPointWithOffset);
}
}
return neighbors;
}
bool IsInImage(Point pointToTest)
{
return pointToTest.X >= 0
&& pointToTest.X < bitmapToCalculateBoundingBoxFor.Width
&& pointToTest.Y >= 0
&& pointToTest.Y < bitmapToCalculateBoundingBoxFor.Height;
}
bool IsInObject(Point pointToTest)
{
Color colorInPointPosition = bitmapToCalculateBoundingBoxFor.GetPixel(pointToTest.X, pointToTest.Y);
//assume object is color is not white
return colorInPointPosition.R != 255
|| colorInPointPosition.G != 255
|| colorInPointPosition.B != 255;
}
Point AddPointOffset(Point point, Point offset)
{
return new Point(point.X + offset.X, point.Y + offset.Y);
}
}
Find an example at:
https://dotnetfiddle.net/49bnTV
I just tested it with a rectangle, but I guess it should work with any shape. Just give it a try.

How can i check that the pictureBox i'm moving around will not get out the borders top left right bottom of the second picturebox area?

private void picZoom_MouseMove(object sender, MouseEventArgs e)
{
if (PicImageClicked == false)
{
if (checkBox1.Checked)
{
int x = e.X + picZoom.Left - picImage.Left;
int y = e.Y + picZoom.Top - picImage.Top;
if (x <= picImage.Width && y <= picImage.Height &&
x <= picImage.Top && y <= picImage.Left)
{
MouseEventArgs e2 = new MouseEventArgs(MouseButtons.None, 0, x, y, 0);
picImageReposition(null, e2);
}
}
else
{
UpdateZoomedImage(e);
}
}
}
picImage is a bigger pictureBox and picZoom is a smaller pictureBox the picZoom i'm moving aorund inside the picImage are with the mouse.
This condition:
if (x <= picImage.Width && y <= picImage.Height
Is working if i'm moving the picZoom to the left border or the bottom border it stop on it and not continue.
But the second condition:
x <= picImage.Top && y <= picImage.Left
Is not working good it make everything slow and it's not stopping on the left or top borders.
I want to make a condition/s so the picZoom will stay in the picImage area borders.
What i tried now is:
private void picZoom_MouseMove(object sender, MouseEventArgs e)
{
Point pnt;
int x, y;
if (MouseButtons.Left == e.Button)
{
pnt = picZoom.PointToScreen(e.Location);
pnt = this.PointToClient(pnt);
x = pnt.X - mouseDown.X;
y = pnt.Y - mouseDown.Y;
if (x < picImage.Left)
{
x = picImage.Left;
}
else if (x + picZoom.Width > picImage.Left + picImage.Width)
{
x = picImage.Left + picImage.Width - picZoom.Width;
}
else
{ }
if (y < picImage.Top)
{
y = picImage.Top;
}
else if (y + picZoom.Height > picImage.Top + picImage.Height)
{
y = picImage.Top + picImage.Height - picZoom.Height;
}
else
{ }
picZoom.Location = new Point(x, y);
if (PicImageClicked == false)
{
if (checkBox1.Checked)
{
MouseEventArgs e2 = new MouseEventArgs(MouseButtons.None, 0, x, y, 0);
picImageReposition(null, e2);
}
else
{
UpdateZoomedImage(e);
}
}
}
}
picImageReposition is:
private void picImageReposition(object sender, MouseEventArgs e)
{
// If no picture is loaded, return
if (picImage.Image == null)
return;
if (PicImageClicked == false)
{
picZoom.BringToFront();
picZoom.Left = e.X + picImage.Left - picZoom.Width/2;
picZoom.Top = e.Y + picImage.Top - picZoom.Height/2;
UpdateZoomedImage(e);
}
}
And UpdateZoomedImage is:
private void UpdateZoomedImage(MouseEventArgs e)
{
// Calculate the width and height of the portion of the image we want
// to show in the picZoom picturebox. This value changes when the zoom
// factor is changed.
int zoomWidth = picZoom.Width / _ZoomFactor;
int zoomHeight = picZoom.Height / _ZoomFactor;
// Calculate the horizontal and vertical midpoints for the crosshair
// cursor and correct centering of the new image
int halfWidth = zoomWidth / 2;
int halfHeight = zoomHeight / 2;
// Create a new temporary bitmap to fit inside the picZoom picturebox
tempBitmap = new Bitmap(zoomWidth, zoomHeight, PixelFormat.Format24bppRgb);
// Create a temporary Graphics object to work on the bitmap
Graphics bmGraphics = Graphics.FromImage(tempBitmap);
// Clear the bitmap with the selected backcolor
bmGraphics.Clear(_BackColor);
// Set the interpolation mode
bmGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw the portion of the main image onto the bitmap
// The target rectangle is already known now.
// Here the mouse position of the cursor on the main image is used to
// cut out a portion of the main image.
bmGraphics.DrawImage(picImage.Image,
new Rectangle(0, 0, zoomWidth, zoomHeight),
new Rectangle(e.X - halfWidth, e.Y - halfHeight, zoomWidth, zoomHeight),
GraphicsUnit.Pixel);
// Draw the bitmap on the picZoom picturebox
picZoom.Image = tempBitmap;
// Draw a crosshair on the bitmap to simulate the cursor position
bmGraphics.DrawLine(Pens.Black, halfWidth + 1, halfHeight - 4, halfWidth + 1, halfHeight - 1);
bmGraphics.DrawLine(Pens.Black, halfWidth + 1, halfHeight + 6, halfWidth + 1, halfHeight + 3);
bmGraphics.DrawLine(Pens.Black, halfWidth - 4, halfHeight + 1, halfWidth - 1, halfHeight + 1);
bmGraphics.DrawLine(Pens.Black, halfWidth + 6, halfHeight + 1, halfWidth + 3, halfHeight + 1);
// Dispose of the Graphics object
bmGraphics.Dispose();
// Refresh the picZoom picturebox to reflect the changes
picZoom.Refresh();
}
If the formula of moving the picZoom control is what you want eg
int x = e.X + picZoom.Left - picImage.Left;
int y = e.Y + picZoom.Top - picImage.Top;
then the comparison is(x, y is the position in picImage meaning that 0,0 is the left top position):
if(x >= 0 && y >= 0 && x + picZoom.Width <= picImage.Width && y + picZoom.Height <= picImage.Height)
{
...
...
}
EDIT
When you click in picZoom and drag, the control moves with the mouse:
private Point mouseDown;
private void picZoom_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = e.Location;
}
private void picZoom_MouseMove(object sender, MouseEventArgs e)
{
Point pnt;
int x, y;
if (MouseButtons.Left == e.Button)
{
pnt = picZoom.PointToScreen(e.Location);
pnt = this.PointToClient(pnt);
x = pnt.X - mouseDown.X;
y = pnt.Y - mouseDown.Y;
if (x < picImage.Left)
{
x = picImage.Left;
}
else if (x + picZoom.Width > picImage.Left + picImage.Width)
{
x = picImage.Left + picImage.Width - picZoom.Width;
}
else
{ }
if (y < picImage.Top)
{
y = picImage.Top;
}
else if (y + picZoom.Height > picImage.Top + picImage.Height)
{
y = picImage.Top + picImage.Height - picZoom.Height;
}
else
{ }
picZoom.Location = new Point(x, y);
}
}
valter
This is not an answer to your question as much as it is a suggestion. Could you not create a Rect() directly over your picture and simply check if the mouse position is within it's bounds.
Something along the lines of:
Rectangle rect = obj.GetBounds(e.Graphics);
if (!rect.Intersects(e.ClipRectangle))
continue;
(stolen from another post)
So I looked over your code again, and it struck me.
You are comparing an x value to the top bound of your picture and a y value to the left bound...
x <= picImage.Top && y <= picImage.Left
should be
x <= picImage.Left && y <= picImage.Top
That is, if I understand what you are trying to do.
Good luck!

ZOOM Out function in MS Chart c#

I have the following Winforms code:
void chart1_MouseWheel(object sender, MouseEventArgs e)
{
double xMin = chart1.ChartAreas[0].AxisX.ScaleView.ViewMinimum;
double xMax = chart1.ChartAreas[0].AxisX.ScaleView.ViewMaximum;
if (e.Delta < 0)
{ //chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset();
//chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset();
}
if (e.Delta > 0)
{
double posXStart = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.Location.X) - (xMax - xMin)/2 ;
double posXFinish = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.Location.X) + (xMax - xMin)/2;
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(posXStart, posXFinish);
}
}
The Zoom In function is working but when e.Delta < 0, I need the Zoom Out function based on the above code.
try
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset(1);
chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset(1);
If you set the saveState to true when you are zooming, the ZoomReset(1) will go back to the last zoom state. Or if you set the saveState to false, the ZoomReset(1) will just zoom all the way back out. Here is my code, I do mine with the mouse click, but I'm sure you can get it working with scroll wheel:
private void chart1_SelectionRangeChanged(object sender, CursorEventArgs e)
{
double startX, endX, startY, endY;
if (chart1.ChartAreas[0].CursorX.SelectionStart > chart1.ChartAreas[0].CursorX.SelectionEnd)
{
startX = chart1.ChartAreas[0].CursorX.SelectionEnd;
endX = chart1.ChartAreas[0].CursorX.SelectionStart;
}
else
{
startX = chart1.ChartAreas[0].CursorX.SelectionStart;
endX = chart1.ChartAreas[0].CursorX.SelectionEnd;
}
if (chart1.ChartAreas[0].CursorY.SelectionStart > chart1.ChartAreas[0].CursorY.SelectionEnd)
{
endY = chart1.ChartAreas[0].CursorY.SelectionStart;
startY = chart1.ChartAreas[0].CursorY.SelectionEnd;
}
else
{
startY = chart1.ChartAreas[0].CursorY.SelectionStart;
endY = chart1.ChartAreas[0].CursorY.SelectionEnd;
}
if (startX == endX && startY == endY)
{
return;
}
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(startX, (endX - startX), DateTimeIntervalType.Auto, true);
chart1.ChartAreas[0].AxisY.ScaleView.Zoom(startY, (endY - startY), DateTimeIntervalType.Auto, true);
}
As Baddack points out you can use the ZoomReset(1) method to go back one step in the zoom history. However, if you use ZoomReset(0) you can reset all zoom operations without having to turn the history saving off.

Basic Image Refresh Issue through Array Manipulation

I am trying to make a simple tile game engine and running into issues. I get stuck when i have to redraw the tile.
int[,] level = {
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
};
This is my array and all the values are 0 thus off. Each corresponding value is linked to a seperate tile that will turn on and off as you press the keys.
//Event Handler (W,A,S,D) is used for movements
private void panel1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
.
.
.
}
//Responsible for movements
private void tmrMov_Tick(object sender, EventArgs e)
{
level[_x, _y] = 0;
if (_objDirection == Direction.Right)
{
if (_x < _boardWidth - 1 && _x >= 0 && _x + 1 < _boardWidth - 1 && _x + 1 >= 0)
_x += 1;
else
_x = _boardWidth - 1;
}
.
.
.
level[_x, _y] = _k;
Invalidate();
}
This is my timer function that is supposed to 'manipulate' array values so when the program is running, one can decide which tile to turn on/off through keys.
Anyways, my problem is refreshing the image which concerns the 'invalidate()' function call. Though, i do have a feeling it can change array values on the fly, i can't seem to refresh the image to jump to another tile.
Here's the link to the complete project i've been working on: http://www.mediafire.com/?g10a0zzt8hru11v
Here's a similar but different question i asked some days back: Setting up basic game and debugging fundamental problems
Thanks in advance!
So...I hope you don't take any offense to this, but I went through and tidied things up a bit and offered some suggestions/fixes/comments/etc.
Hope this helps! Let me know if any of the suggested changes need explanation.
So, let's start at the designer.cs file:
// Ahh, here's yer problem: the panel control doesn't raise PreviewKeyDowns - the form does, tho
this.panel1.PreviewKeyDown +=
new System.Windows.Forms.PreviewKeyDownEventHandler(
this.panel1_PreviewKeyDown);
// the form will raise these, so bind the handler to it
// (and rename the method)
this.PreviewKeyDown +=
new System.Windows.Forms.PreviewKeyDownEventHandler(
this.Form1_PreviewKeyDown);
Ah, one big problem down - none of your key events were actually getting to your handler. Let's pop back over to the form code-behind.
So, in the constructor - all this can go:
// arrays of T (T[], int[], etc), come initialized to default(T),
// so none of this is needed.
level[0, 0] = 0;
level[0, 1] = 0;
level[0, 2] = 0;
level[0, 3] = 0;
level[0, 4] = 0;
Jump down to the Paint handler:
// The next two checks have the x and y swapped,
// meaning the apparent motion will not match what the
// actual direction should be
//Empty Tile
if (level[y, x] == 0)
{
//Occupied Tile
if (level[y, x] == 1)
{
// Now the render will mactch properly
//Empty Tile
if (level[x, y] == 0)
{
//Occupied Tile
if (level[x, y] == 1)
{
Onward! To the Timer.Tick handler:
#region Timer function
// doing this could cause flickering
// or flashing if the paint fires while
// we're updating things
level[_x, _y] = 0;
#region Timer function
// instead, keep track temporarily
// what they were - we'll come back to this later on
var oldX = _x;
var oldY = _y;
Further on to the if/else chains:
// There's a lot of replication and style choices here that
// will make it harder to debug/troubleshoot
if (_objDirection == Direction.Right)
{
if (_x < _boardWidth - 1 && _x >= 0 && _x + 1 < _boardWidth - 1 && _x + 1 >= 0)
_x += 1;
else
_x = _boardWidth - 1;
}
else if (_objDirection == Direction.Left)
Let's see if we can get rid of some of the repetition:
// let's figure these out ahead of time
var spaceOnLeft = _x > 0;
var spaceOnRight = _x < _boardWidth - 1;
var spaceOnTop = _y > 0;
var spaceOnBottom = _y < _boardHeight - 1;
// switch is a bit like the if/else construct you had
switch (_objDirection)
{
case Direction.Up:
// this means: if(spaceOnTop) y = y-1 else y = height-1
_y = spaceOnTop ? _y - 1 : _boardHeight - 1;
break;
case Direction.Down:
_y = spaceOnBottom ? _y + 1 : 0;
break;
case Direction.Left:
_x = spaceOnLeft ? _x - 1 : _boardWidth - 1;
break;
case Direction.Right:
_x = spaceOnRight ? _x + 1 : 0;
break;
}
Skip to the end...
// now we'll use the old position to clear...
level[oldX, oldY] = 0;
// then set the new position
level[_x, _y] = _k;
// Since we're only writing on the panel,
// we only need to rerender the panel
panel1.Refresh();
One last bit - the key down handler:
// Hah - artificial difficulty due
// to awkward key choice? Out of curiosity,
// why not Keys.Up, Down, Left, Right?
if (e.KeyCode == Keys.E)
{
_objDirection = Direction.Left;
}
else if (e.KeyCode == Keys.D)
{
_objDirection = Direction.Right;
}
else if (e.KeyCode == Keys.W)
{
_objDirection = Direction.Up;
}
else if (e.KeyCode == Keys.S)
{
_objDirection = Direction.Down;
}
// same deal here, but with keys
// Or switch to Up, Down, Left, Right :)
switch (e.KeyCode)
{
case Keys.E:
_objDirection = Direction.Up;
break;
case Keys.D:
_objDirection = Direction.Down;
break;
case Keys.W:
_objDirection = Direction.Left;
break;
case Keys.S:
_objDirection = Direction.Right;
break;
}
Full code drop of the Form1.cs class:
//Listing all the parameters
public partial class Form1 : Form
{
#region Declaring Parameters
enum Direction
{
Left, Right, Up, Down
}
private int _x;
private int _y;
private int _k;
private Direction _objDirection;
Random rand = new Random();
private int _boardWidth;
private int _boardHeight;
private int[,] level;
#endregion
//Giving values to parameters
public Form1()
{
InitializeComponent();
#region Initialial values
_k = 1;
_boardWidth = 6;
_boardHeight = 6;
_x = rand.Next(0, _boardWidth - 1);
_y = rand.Next(0, _boardHeight - 1);
_objDirection = Direction.Left;
//Array that works as a board or platform which we used to distinguish tiles
level = new int[_boardWidth, _boardHeight];
#endregion
}
//Paint is used for drawing purposes only
private void panel1_Paint(object sender, PaintEventArgs e)
{
/*
int[,] level = {
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
{ 0, 0, 0, 0, 0 ,0 },
};
*/
#region Looping through tiles
//Initializing first randomly filled tile
level[_x, _y] = _k;
for (int y = 0; y < _boardHeight; y++)
{
for (int x = 0; x < _boardWidth; x++)
{
//Empty Tile
if (level[x, y] == 0)
{
// Create pen.
Pen redPen = new Pen(Color.Red, 1);
// Create rectangle.
Rectangle redRect = new Rectangle(x * 50, y * 50, 50, 50);
// Draw rectangle to screen.
e.Graphics.DrawRectangle(redPen, redRect);
}
//Occupied Tile
if (level[x, y] == 1)
{
// Create solid brush.
SolidBrush blueBrush = new SolidBrush(Color.Blue);
// Create rectangle.
Rectangle rect = new Rectangle(x * 50, y * 50, 50, 50);
// Fill rectangle to screen.
e.Graphics.FillRectangle(blueBrush, rect);
}
}
}
#endregion
}
//Responsible for movements
private void tmrMov_Tick(object sender, EventArgs e)
{
#region Timer function
// instead, keep track temporarily
// what they were
var oldX = _x;
var oldY = _y;
// let's figure these out ahead of time
var spaceOnLeft = _x > 0;
var spaceOnRight = _x < _boardWidth - 1;
var spaceOnTop = _y > 0;
var spaceOnBottom = _y < _boardHeight - 1;
// switch is a bit like the if/else construct you had
switch (_objDirection)
{
case Direction.Up:
// this means: if(spaceOnTop) y = y-1 else y = height-1
_y = spaceOnTop ? _y - 1 : _boardHeight - 1;
break;
case Direction.Down:
_y = spaceOnBottom ? _y + 1 : 0;
break;
case Direction.Left:
_x = spaceOnLeft ? _x - 1 : _boardWidth - 1;
break;
case Direction.Right:
_x = spaceOnRight ? _x + 1 : 0;
break;
}
// now we'll use the old values to clear...
level[oldX, oldY] = 0;
// then set the new value
level[_x, _y] = _k;
#endregion
panel1.Refresh();
}
//Event Handler (W,A,S,D) is used for movements
private void Form1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
#region Controls
// same deal here, but with keys
switch (e.KeyCode)
{
case Keys.Up:
e.IsInputKey = true;
_objDirection = Direction.Up;
break;
case Keys.Down:
e.IsInputKey = true;
_objDirection = Direction.Down;
break;
case Keys.Left:
e.IsInputKey = true;
_objDirection = Direction.Left;
break;
case Keys.Right:
e.IsInputKey = true;
_objDirection = Direction.Right;
break;
}
#endregion
}
}
Cheers!

Windows Forms -> WPF image control conversion question

I've always worked with Windows Forms, but now I'm trying to learn WPF due to it's advantages. Some time ago I created a picturebox control (with help of Damien here). And for me it's very hard to convert this control into WPF's Image control. I haven't found any appropriate help on the Internet.
My control is used for displaying (founded before) middle between two pages on a scanned image of book. It consists of two moveable points, line between them and areas to the left and to the right filled with semitransparent polygons.
The problem is that WPF is VERY different. It's even hard to draw a filled circle on a Image control.
Here is my code listing:
public partial class SplitPictureBox : System.Windows.Forms.PictureBox
{
public SplitPictureBox()
{
InitializeComponent();
}
private int mPointMoveInProgress = 0;
private int handleRadius = 5;
public int HandleRaduis
{
get { return handleRadius; }
set { handleRadius = value; }
}
private int middleTop = 0;
private int middleBottom = 0;
private int middle;
public int Middle
{
get
{
return (middleTop + middleBottom) /2;
}
set { middle = value; }
}
private double theta;
public double Theta
{
get
{
return (Math.Atan(((middleTop - middleBottom) / (double)this.Height)) * 180) / Math.PI;
}
set
{
theta = value;
int deltaX = (int)((Math.Tan((Math.PI / 180) * value)) * this.Height / 2);
middleTop = middle + deltaX;
middleBottom = middle - deltaX;
}
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
SolidBrush left = new SolidBrush(Color.FromArgb(80, Color.Blue));
SolidBrush right = new SolidBrush(Color.FromArgb(80, Color.Green));
SolidBrush brush = new SolidBrush(Color.Red);
pe.Graphics.FillPolygon(left, new Point[4] { new Point(0,0), new Point(middleTop,0),
new Point(middleBottom, this.Height), new Point(0, this.Height)
});
pe.Graphics.FillPolygon(right, new Point[4] { new Point(this.Width,0), new Point(middleTop,0),
new Point(middleBottom, this.Height), new Point(this.Width, this.Height)
});
// Draw line
pe.Graphics.DrawLine(new Pen(Color.Red, 2), new Point(middleTop, handleRadius), new Point(middleBottom, this.Height - handleRadius));
Rectangle rectangle;
// Draw first handle
rectangle = new Rectangle(middleTop - handleRadius, 0, handleRadius * 2, handleRadius * 2);
pe.Graphics.FillEllipse(brush, rectangle);
// Draw second handle
rectangle = new Rectangle(middleBottom - handleRadius, this.Height - handleRadius * 2, handleRadius * 2, handleRadius * 2);
pe.Graphics.FillEllipse(brush, rectangle);
}
private Point moveLineTop;
private Point moveLineBottom;
protected override void OnMouseDown(MouseEventArgs e)
{
moveLineTop = new Point(e.X - middleTop, 0);
moveLineBottom = new Point(e.X - middleBottom, this.Height);
if (Math.Abs(e.X - middleTop) < handleRadius && Math.Abs(e.Y) <= handleRadius * 2)
{
Cursor.Current = Cursors.Hand;
mPointMoveInProgress = 1;
}
else if (Math.Abs(e.X - middleBottom) < handleRadius && Math.Abs(e.Y - this.Height) <= handleRadius * 2)
{
Cursor.Current = Cursors.Hand;
mPointMoveInProgress = 2;
}
else if (Math.Abs(e.X - x) < handleRadius && e.Y > handleRadius * 2 && e.Y < this.Height - handleRadius * 2)
{
Cursor.Current = Cursors.SizeWE;
mPointMoveInProgress = 3;
}
else mPointMoveInProgress = 0;
base.OnMouseDown(e);
}
private int x = 0;
protected override void OnMouseMove(MouseEventArgs e)
{
x = middleTop - (int)((e.Y * (middleTop - middleBottom)) / (double)this.Height);
if (mPointMoveInProgress == 1)
{
Cursor.Current = Cursors.Hand;
if (e.X > 0 && e.X < this.Width)
{
middleTop = e.X;
Refresh();
}
}
else if (mPointMoveInProgress == 2)
{
Cursor.Current = Cursors.Hand;
if (e.X > 0 && e.X < this.Width)
{
middleBottom = e.X;
Refresh();
}
}
else if (mPointMoveInProgress == 3)
{
if (e.X - moveLineTop.X >= 0 && e.X - moveLineTop.X <= this.Width &&
e.X - moveLineBottom.X >= 0 && e.X - moveLineBottom.X <= this.Width)
{
Cursor.Current = Cursors.SizeWE;
middleTop = e.X - moveLineTop.X;
middleBottom = e.X - moveLineBottom.X;
Refresh();
}
}
else
{
if (Math.Abs(e.X - middleTop) < handleRadius && Math.Abs(e.Y) <= handleRadius * 2)
Cursor.Current = Cursors.Hand;
else if (Math.Abs(e.X - middleBottom) < handleRadius && Math.Abs(e.Y - this.Height) <= handleRadius * 2)
Cursor.Current = Cursors.Hand;
else if (Math.Abs(e.X - x) < handleRadius && e.Y > handleRadius * 2 && e.Y < this.Height - handleRadius * 2)
Cursor.Current = Cursors.SizeWE;
else Cursor.Current = Cursors.Default;
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
mPointMoveInProgress = 0;
middle = (middleTop + middleBottom) / 2;
base.OnMouseUp(e);
}
}
Could anybody to help me with this? Give me some useful links or code samples.
Thanks!
You are looking for Adorners, they can draw items over other controls, and also handle events, etc.
Some tips:
How to: Implement an Adorner
Adorners How-To Topics

Categories

Resources