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
Related
i am trying to zoom in and zoom out on a content page using xamarin.forms.
I am able zoom in and zoom out but the problem is scrolling is not working.
i want zoom an image. with this code zooming is working perfectly. But while zooming i am not able to see full image. i must scroll to view the rest of the image. for that i need to scroll. but scrolling is not working.
XAML
xmlns:helper="clr-namespace:KPGTC.Deals.Mobile.Helpers"
<helper:PinchToZoomContainer>
<helper:PinchToZoomContainer.Content>
<Image x:Name="img_Popup"/>
</helper:PinchToZoomContainer.Content>
</helper:PinchToZoomContainer>
Code:
public class PinchToZoomContainer : ContentView
{
double MIN_SCALE = 1;
double MAX_SCALE = 4;
double startScale = 1;
double currentScale = 1;
double xOffset = 0;
double yOffset = 0;
bool _isActive = false;
public PinchToZoomContainer()
{
DependencyService.Get<IHelpers>().ShowAlert("Double-tap to zoom");
//var _pinchGesture = new PinchGestureRecognizer();
//_pinchGesture.PinchUpdated += OnPinchUpdated;
//GestureRecognizers.Add(_pinchGesture);
var _tapGesture = new TapGestureRecognizer { NumberOfTapsRequired = 2 };
_tapGesture.Tapped += On_Tapped;
GestureRecognizers.Add(_tapGesture);
var _panGesture = new PanGestureRecognizer();
_panGesture.PanUpdated += OnPanUpdated;
GestureRecognizers.Add(_panGesture);
TranslationX = 0;
TranslationY = 0;
_isActive = false;
}
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
if (_isActive)
{
if (e.TotalX > 0)
{
if (e.TotalX > 2)
{
TranslationX += 15;
}
}
else
{
if (e.TotalX < -2)
{
TranslationX -= 15;
}
}
}
}
private void On_Tapped(object sender, EventArgs e)
{
if (Scale > MIN_SCALE)
{
_isActive = false;
this.ScaleTo(MIN_SCALE, 250, Easing.CubicInOut);
this.TranslateTo(0, 0, 250, Easing.CubicInOut);
}
else
{
_isActive = true;
AnchorX = AnchorY = 0.5;
this.ScaleTo(MAX_SCALE, 250, Easing.CubicInOut);
}
}
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running)
{
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max(1, currentScale);
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp(-Content.Height * (currentScale - 1), 0);
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}
}
Alright, this was quite a tough one and admittedly I don't understand fully how I made it, but I made it.
Some thoughts:
You mixed the translation of the container and the content, which is quite tricky to handle - if this is possible at all
When panning, you added 15 every time the pan event was raised, but there is a better way: Just store the initial offset of the content and then add the TotalX and TotalY respectively to the TranslationX and the TranslationY of the content (this was the easy part)
Panning while zooming was quite hard to get right and I had to find it out by trial and error
Basically you have to store the origin of the pinch gesture when the gesture starts and calculate the diff between the original origin and the current origin
Then you have to add the diff (multiplied by the with and height respectively of the control) to the target translation
Here is the code for the panning:
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
if (e.StatusType == GestureStatus.Started)
{
this.xOffset = this.Content.TranslationX;
this.yOffset = this.Content.TranslationY;
}
if (e.StatusType != GestureStatus.Completed
&& e.StatusType != GestureStatus.Canceled)
{
this.Content.TranslationX = this.xOffset + e.TotalX;
this.Content.TranslationY = this.yOffset + e.TotalY;
}
if (e.StatusType == GestureStatus.Completed)
{
this.xOffset = this.Content.TranslationX;
this.yOffset = this.Content.TranslationY;
}
}
And here for the pinching
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
this.startScale = this.Content.Scale;
this.Content.AnchorX = 0;
this.Content.AnchorY = 0;
this.startScaleOrigin = e.ScaleOrigin;
}
if (e.Status == GestureStatus.Running)
{
var originDiff = PinchToZoomContainer.CalculateDiff(e.ScaleOrigin, this.startScaleOrigin);
this.currentScale += (e.Scale - 1) * this.startScale;
this.currentScale = Math.Max(1, this.currentScale);
double renderedX = this.Content.X + this.xOffset;
double deltaX = renderedX / this.Width;
double deltaWidth = this.Width / (this.Content.Width * this.startScale);
double originX = (this.startScaleOrigin.X - deltaX) * deltaWidth;
double renderedY = this.Content.Y + this.yOffset;
double deltaY = renderedY / this.Height;
double deltaHeight = this.Height / (this.Content.Height * this.startScale);
double originY = (startScaleOrigin.Y - deltaY) * deltaHeight;
double targetX = this.xOffset - ((originX) * this.Content.Width) * (this.currentScale - this.startScale) - originDiff.X * this.Content.Width;
double targetY = this.yOffset - ((originY) * this.Content.Height) * (this.currentScale - this.startScale) - originDiff.Y * this.Content.Height;
this.Content.TranslationX = targetX.Clamp(-this.Content.Width * (this.currentScale - 1), 0);
this.Content.TranslationY = targetY.Clamp(-this.Content.Height * (this.currentScale - 1), 0);
this.Content.Scale = this.currentScale;
}
if (e.Status == GestureStatus.Completed)
{
this.xOffset = this.Content.TranslationX;
this.yOffset = this.Content.TranslationY;
}
}
(Of course you have to add Point startScaleOrigin to your class).
Finally you need the method to calculate the distance between two points
private static Point CalculateDiff(Point first, Point second)
{
return second.Offset(-first.X, -first.Y);
}
Unfortunately I did not manage to get the tapping right, but I think you should be able to figure it out from here.
For an assignment I've had to convert a fractal rendering program from Java to C# and I think I've done it but when i try to run it I get the error that is present in the title and I have no idea why it is happening. This is the code for the renderer itself which presents me with no errors:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Form1
{
public partial class Form1 : Form
{
public Form1()
{
init();
start();
this.DoubleBuffered = true;
}
//code to convert HSB to RGB from HSB.cs. All your code so i made it take up less space.
public struct HSBColor
{
float h;
float s;
float b;
int a;
public HSBColor(float h, float s, float b) { this.a = 0xff; this.h = Math.Min(Math.Max(h, 0), 255); this.s = Math.Min(Math.Max(s, 0), 255); this.b = Math.Min(Math.Max(b, 0), 255); }
public HSBColor(int a, float h, float s, float b) { this.a = a; this.h = Math.Min(Math.Max(h, 0), 255); this.s = Math.Min(Math.Max(s, 0), 255); this.b = Math.Min(Math.Max(b, 0), 255); }
public float H { get { return h; } }
public float S { get { return s; } }
public float B { get { return b; } }
public int A { get { return a; } }
public Color Color { get { return FromHSB(this); } }
public static Color FromHSB(HSBColor hsbColor)
{
float r = hsbColor.b;
float g = hsbColor.b;
float b = hsbColor.b;
if (hsbColor.s != 0)
{
float max = hsbColor.b; float dif = hsbColor.b * hsbColor.s / 255f; float min = hsbColor.b - dif; float h = hsbColor.h * 360f / 255f;
if (h < 60f) { r = max; g = h * dif / 60f + min; b = min; }
else if (h < 120f) { r = -(h - 120f) * dif / 60f + min; g = max; b = min; }
else if (h < 180f) { r = min; g = max; b = (h - 120f) * dif / 60f + min; }
else if (h < 240f) { r = min; g = -(h - 240f) * dif / 60f + min; b = max; }
else if (h < 300f) { r = (h - 240f) * dif / 60f + min; g = min; b = max; }
else if (h <= 360f) { r = max; g = min; b = -(h - 360f) * dif / 60 + min; }
else { r = 0; g = 0; b = 0; }
}
return Color.FromArgb(hsbColor.a, (int)Math.Round(Math.Min(Math.Max(r, 0), 255)), (int)Math.Round(Math.Min(Math.Max(g, 0), 255)), (int)Math.Round(Math.Min(Math.Max(b, 0), 255)));
}
}
private const int MAX = 256; // max iterations
private const double SX = -2.025; // start value real
private const double SY = -1.125; // start value imaginary
private const double EX = 0.6; // end value real
private const double EY = 1.125; // end value imaginary
private static int x1, y1, xs, ys, xe, ye;
private static double xstart, ystart, xende, yende, xzoom, yzoom;
private static float xy;
private int c = 0;
//private Image picture; Taken out, not needed
// create rectangle variable JGB
Rectangle rec;
private Graphics g1;
//private Cursor c1, c2; Taken out, not needed
private System.Drawing.Bitmap bitmap;
public void init()
{
//setSize(640, 480); changed this code to JGB:
this.Size = new Size(640, 480);
// Taken all lines out below. Not needed.
/*finished = false;
addMouseListener(this);
addMouseMotionListener(this);
c1 = new Cursor(Cursor.WAIT_CURSOR);
c2 = new Cursor(Cursor.CROSSHAIR_CURSOR); */
x1 = 640;
y1 = 480;
xy = (float)x1 / (float)y1;
//picture = createImage(x1, y1); Taken out and replaced with JGB:
bitmap = new Bitmap(x1, y1);
//g1 = picture.getGraphics(); changed to get my bitmap
g1 = Graphics.FromImage(bitmap);
//finished = true; Finished variable deleted so not needed
}
//Code below didnt appear to do anything so i deleted it
/*public void destroy() // delete all instances
{
if (finished)
{
removeMouseListener(this);
removeMouseMotionListener(this);
picture = null;
g1 = null;
c1 = null;
c2 = null;
System.gc(); // garbage collection
}
} */
public void start()
{
//action = false;
//rectangle = false;
initvalues();
// added dialog box for instance loading and save varaibles needed for position and zoom to text file
DialogResult dialog = MessageBox.Show("Would You Like to Load Your Last Instance?", "Load Instance?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (dialog == DialogResult.Yes)
{
string[] lines = System.IO.File.ReadAllLines(#"C:\Users\Public\Writelines.txt");
xzoom = System.Convert.ToDouble(lines[0]);
yzoom = System.Convert.ToDouble(lines[1]);
xstart = System.Convert.ToDouble(lines[2]);
ystart = System.Convert.ToDouble(lines[3]);
}
else
{
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
}
mandelbrot();
}
public void stop()
{
}
/*public void paint(Graphics g, PaintEventArgs e)
{
update(g);
}
public void update(Graphics g)
{
//g.DrawImage(picture, 0, 0);
}*/
private void mandelbrot()
{
int x, y;
float h, b, alt = 0.0f;
Color color;
Pen pen = new Pen(Color.Black);
for (x = 0; x < x1; x += 2)
for (y = 0; y < y1; y++)
{
h = pointcolour(xstart + xzoom * (double)x, ystart + yzoom * (double)y, c);
if (h != alt)
{
b = 1.0f - h * h;
color = HSBColor.FromHSB(new HSBColor(h * 255, 0.8f * 255, b * 255));
pen = new Pen(color);
alt = h;
}
g1.DrawLine(pen, x, y, x + 1, y);
}
}
private float pointcolour(double xwert, double ywert, int j)
{
double r = 0.0, i = 0.0, m = 0.0;
// int j = 0;
while ((j < MAX) && (m < 4.0))
{
j++;
m = r * r - i * i;
i = 2.0 * r * i + ywert;
r = m + xwert;
}
return (float)j / (float)MAX;
}
private void initvalues()
{
xstart = SX;
ystart = SY;
xende = EX;
yende = EY;
if ((float)((xende - xstart) / (yende - ystart)) != xy)
xstart = xende - (yende - ystart) * (double)xy;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
using (Pen pen = new Pen(Color.White, 2))
{
e.Graphics.DrawRectangle(pen, rec);
}
Invalidate();
}
//added load method
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
xe = e.X;
ye = e.Y;
if (xs < xe)
{
if (ys < ye) rec = new Rectangle(xs, ys, (xe - xs), (ye - ys));
else rec = new Rectangle(xs, ye, (xe - xs), (ys - ye));
}
else
{
if (ys < ye) rec = new Rectangle(xe, ys, (xs - xe), (ye - ys));
else rec = new Rectangle(xe, ye, (xs - xe), (ys - ye));
}
this.Invalidate();
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// e.consume();
xs = e.X;
ys = e.Y; // starting point y
this.Invalidate();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
rec = new Rectangle(0, 0, 0, 0);
if (e.Button == MouseButtons.Left)
{
int z, w;
//e.consume();
//xe = e.X;
//ye = e.Y;
if (xs > xe)
{
z = xs;
xs = xe;
xe = z;
}
if (ys > ye)
{
z = ys;
ys = ye;
ye = z;
}
w = (xe - xs);
z = (ye - ys);
if ((w < 2) && (z < 2)) initvalues();
else
{
if (((float)w > (float)z * xy)) ye = (int)((float)ys + (float)w / xy);
else xe = (int)((float)xs + (float)z * xy);
xende = xstart + xzoom * (double)xe;
yende = ystart + yzoom * (double)ye;
xstart += xzoom * (double)xs;
ystart += yzoom * (double)ys;
}
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
mandelbrot();
string stringxzoom = xzoom.ToString();
string stringyzoom = yzoom.ToString();
string stringystart = ystart.ToString();
string stringxstart = xstart.ToString();
string[] lines = { stringxzoom, stringyzoom, stringxstart, stringystart };
System.IO.File.WriteAllLines(#"C:\Users\Public\Writelines.txt", lines);
this.Invalidate();
//Repaint();
}
}
private void restartToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Restart();
}
private void exitToolStripMenuItem1_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void menuToolStripMenuItem_Click(object sender, EventArgs e)
{
}
private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
}
}
}
and this is the code that is used for the form designer which was auto generated and I'm not sure why an error is being presented because I've never had one before:
namespace WindowsFormsApplication1
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
}
#endregion
}
}
I believe it is caused because your namespaces are not the same. Since the partial code generated by the designer doesn't inherit from Form, you don't have a method to override. Once you make the two classes tie together properly by matching the namespaces, it should work.
To fix it, you can either change the namespace of the designer code to match your namespace of Form1:
namespace Form1
{
partial class Form1
{
//...
}
}
Or change your form to match the designer:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
//....
}
}
Edit the project properties and update the Default namespace to match the desired namespace for all forms
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!
I have a WPF application, wherein I draw rectangles in a canvas.I need to add a functionality in which when i draw a rectangle if there is a rectangle next to it (for eg: suppose first rectangle x coordinate is 236 and second rectangle coordinate is 235) i need to snap the second rectangle x coordinate to 236 as shown in the image.
The snap would be done only if the distance difference is 10.
I have written the following code to do this.
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
startPos = e.GetPosition(Canvas);
System.Windows.Point curPosition = e.GetPosition(SectionCanvas);
rect = new System.Windows.Shapes.Rectangle
{
Stroke = brushColor,
StrokeDashArray = new DoubleCollection { 2, 2 },
Tag = "rectangle"
};
Canvas.SetLeft(rect, startPos.X);
Canvas.SetTop(rect, startPos.X);
SectionCanvas.Children.Add(rect);
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
currentPos = e.GetPosition(SectionCanvas);
var x = Math.Min(currentPos.X, startPos.X);
var y = Math.Min(currentPos.Y, startPos.Y);
var w = Math.Max(currentPos.X, startPos.X) - x;
var h = Math.Max(currentPos.Y, startPos.Y) - y;
rect.Width = w;
rect.Height = h;
Canvas.SetLeft(rect, x);
Canvas.SetTop(rect, y);
}
private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
{
if(rect == null)
{
MessageBox.Show("Could not capture section, Please try again");
return;
}
endPos = e.GetPosition(SectionCanvas);
IEnumerable<Rect> coordinates = rectCollection.Select(r => new Rect(Canvas.GetLeft(r), Canvas.GetTop(r), r.Width, r.Height));
Rect newCordinates = new Rect(Canvas.GetLeft(rect), Canvas.GetTop(rect), rect.Width, rect.Height);
if (coordinates.Any(c => c.IntersectsWith(newCordinates)))))
{
MessageBox.Show("New Rectangle intersects with existing rectangle");
Canvas.Children.Remove(rect);
return;
}
rectCollection.Add(rect);
rect = null;
foreach(Point p in tempCollection)
{
if((startPos.X <= (p.X + 10) && startPos.X >= (p.X -10)))
{
startPos.X = p.X;
}
if(endPos.X <= (p.X + 10) && endPos.X >= (p.X - 10))
{
var x1 = Math.Max(endPos.X,p.X) - Math.Min(endPos.X, p.X);
var w1 = startPos.X - x1;
endPos.X = p.X;
startPos.X = w1;
}
if ((startPos.Y <= (p.Y + 10) && startPos.Y >= (p.Y - 10)))
{
startPos.Y = p.Y;
}
if (endPos.Y <= (p.Y + 10) && endPos.Y >= (p.Y - 10))
{
var x1 = Math.Max(endPos.Y, p.Y) - Math.Min(endPos.Y, p.Y);
var w1 = startPos.Y - x1;
endPos.Y = p.Y;
}
}
var x = Math.Min(currentPos.X, startPos.X);
var y = Math.Min(currentPos.Y, startPos.Y);
var w = Math.Max(currentPos.X, startPos.X) - x;
var h = Math.Max(currentPos.Y, startPos.Y) - y;
rect.Width = w;
rect.Height = h;
rect.Stroke = Brushes.Coral;
Canvas.SetLeft(rect, x);
Canvas.SetTop(rect, y);
rect = null;
tempCollection.Add(startPos);
tempCollection.Add(endPos);
}
The above code doesnt work when I am changing the endpoints values. While debugging I can see that the end point value changes but the rectangle drawn doesn't change. I am not able to find out what I am doing wrong.
I figured out the answer which was a dumb mistake from my end.
While calculating the width and height after snapping, I am using currentPos, instead I should use endPos.
var x = Math.Min(endPos.X, startPos.X);
var y = Math.Min(endPos.Y, startPos.Y);
var w = Math.Max(endPos.X, startPos.X) - x;
var h = Math.Max(endPos.Y, startPos.Y) - y;
I have a task to make a software model of device which has touchpad and screen.
I'm using C# and Windows XP.
So I have TouchpadPanel and ScreenPanel.
How can I route mouse inputs of TouchpadPanel to ScreenPanel? I want ScreenPanel (or ScreenForm) not to capture mouse and its events but to get them from TouchpadPanel. Is it really possible to do it?
I spent couple of days trying to figure out the best way to solve the problem which I described above.
Finally I found very good manner which helped me successfully finish requested job.
I decided to share with you - hopefully it might save you some time.
I used System.Windows.Forms.ControlPaint to create all the controls that I needed from scratch: buttons, labels, custom textboxes and comboboxes. Most important of all - I made separate top layer for cursor.
Now when I'm drawing everything myself I can draw extra cursor which is always visible in my window no matter what I'm doing with the real cursor.
Here is some code example:
class MCDUComboBox : MCDUStateControl
{
public event EventHandler<GosNIIAS.EventArgs<string>> SelectedIndexChanged;
private const int buttonOffset = 2;
private const int buttonWidth = 20;
private const int buttonHeight = 20;
private string[] m_items;
private int m_hightlight_index;
private int m_selected_index;
private Rectangle m_drop_down_bounds;
private int m_scroll_index;
private ButtonState m_top_scroll_button_state;
private ButtonState m_bottom_scroll_button_state;
#region Properties
public string[] Items
{
get { return m_items; }
set { m_items = value; }
}
public override string Text
{
get { return base.Text; }
set
{
if (ControlState == ControlState.Normal)
base.Text = value;
}
}
#endregion
public MCDUComboBox()
{
this.Font = new System.Drawing.Font("Courier New", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.m_items = new string[0];
this.m_hightlight_index = -1;
this.m_selected_index = -1;
this.m_drop_down_bounds = new Rectangle();
this.m_scroll_index = 0;
this.m_top_scroll_button_state = ButtonState.Normal;
this.m_bottom_scroll_button_state = ButtonState.Normal;
}
public override void Draw(Graphics g)
{
if (Visible)
{
Rectangle bounds = ClipRectangle;
if (ControlState == ControlState.Normal)
{
g.FillRectangle(BackBrush, bounds);
g.DrawString(Text, Font, ForeBrush, bounds, StringFormat);
}
else if (ControlState == ControlState.Edit)
{
g.FillRectangle(ForeBrush, bounds);
ControlPaint.DrawBorder3D(g, bounds, Border3DStyle.Sunken);
Rectangle rectangle = new Rectangle(bounds.X + bounds.Width - buttonOffset - buttonWidth,
bounds.Y + buttonOffset,
buttonWidth,
bounds.Height - 2 * buttonOffset);
ControlPaint.DrawComboButton(g, rectangle, ButtonState.Normal);
}
else if (ControlState == ControlState.WaitingFeedback)
{
g.FillRectangle(BackBrush, bounds);
g.DrawString(Text, Font, WaitFeedbackBrush, bounds, StringFormat);
}
}
}
public override void OnMouseDown(MouseEventArgs e)
{
if (Visible)
{
Rectangle bounds = ClipRectangle;
if (bounds.Contains(e.Location))
{
if (e.Button == MouseButtons.Left)
{
if (ControlState == ControlState.Normal)
ControlState = ControlState.Edit;
}
}
else
{
if (ControlState == ControlState.Edit)
ControlState = ControlState.Normal;
}
}
}
public override bool OnMouseDownTopLayer(MouseEventArgs e)
{
if (Visible && ControlState == MCDU.Drawing.ControlState.Edit)
{
if (m_drop_down_bounds.Contains(e.Location) && m_items.Length > 0)
{
Rectangle bounds = new Rectangle(m_drop_down_bounds.X,
m_drop_down_bounds.Y,
m_items.Length > 10 ? m_drop_down_bounds.Width - buttonWidth
: m_drop_down_bounds.Width,
m_drop_down_bounds.Height);
if (bounds.Contains(e.Location))
{
if (SelectedIndexChanged != null)
{
int itemHeight = m_drop_down_bounds.Height / Math.Min(10, m_items.Length);
m_selected_index = (e.Location.Y - m_drop_down_bounds.Y) / itemHeight;
string text = m_items[m_selected_index + m_scroll_index];
SelectedIndexChanged(this, new GosNIIAS.EventArgs<string>(text));
base.Text = text;
ControlState = ControlState.WaitingFeedback;
}
else
ControlState = ControlState.Normal;
}
else
{
bounds = new Rectangle(m_drop_down_bounds.X + m_drop_down_bounds.Width - buttonWidth,
m_drop_down_bounds.Y,
buttonWidth,
buttonHeight);
if (bounds.Contains(e.Location))
{
m_scroll_index -= 1;
m_scroll_index = Math.Max(0, m_scroll_index);
m_top_scroll_button_state = ButtonState.Pushed;
}
else
{
bounds = new Rectangle(m_drop_down_bounds.X + m_drop_down_bounds.Width - buttonWidth,
m_drop_down_bounds.Y + m_drop_down_bounds.Height - buttonHeight,
buttonWidth,
buttonHeight);
if (bounds.Contains(e.Location))
{
m_scroll_index += 1;
m_scroll_index = Math.Min(m_items.Length - 10, m_scroll_index);
m_bottom_scroll_button_state = ButtonState.Pushed;
}
}
}
return true;
}
else
return false;
}
else
return false;
}
public override void OnMouseMove(MouseEventArgs e)
{
if (Visible && ControlState == MCDU.Drawing.ControlState.Edit)
{
if (m_drop_down_bounds.Contains(e.Location))
{
Rectangle bounds = new Rectangle(m_drop_down_bounds.X,
m_drop_down_bounds.Y,
m_items.Length > 10 ? m_drop_down_bounds.Width - buttonWidth
: m_drop_down_bounds.Width,
m_drop_down_bounds.Height);
if (bounds.Contains(e.Location) && m_items.Length > 0)
{
int itemHeight = m_drop_down_bounds.Height / Math.Min(10, m_items.Length);
m_hightlight_index = (e.Location.Y - m_drop_down_bounds.Y) / itemHeight + m_scroll_index;
}
}
}
}
public override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
m_top_scroll_button_state = ButtonState.Normal;
m_bottom_scroll_button_state = ButtonState.Normal;
}
public override void DrawTopLayer(Graphics g)
{
if (Visible && ControlState == MCDU.Drawing.ControlState.Edit)
{
int itemHeight = GetItemHeight(g);
Rectangle bounds = ClipRectangle;
m_drop_down_bounds = new Rectangle(bounds.X,
bounds.Y + bounds.Height,
bounds.Width,
itemHeight * Math.Max(1, Math.Min(10, m_items.Length)) + 2);
g.FillRectangle(ForeBrush, m_drop_down_bounds);
g.DrawRectangle(new Pen(BackColor), new Rectangle(m_drop_down_bounds.X,
m_drop_down_bounds.Y,
m_drop_down_bounds.Width - 1,
m_drop_down_bounds.Height - 1));
for (int index = 0; index < Math.Min(10, m_items.Length); index++)
{
Rectangle itemBounds = new Rectangle(bounds.X,
bounds.Y + bounds.Height + index * itemHeight,
m_items.Length > 10 ? bounds.Width - buttonWidth : bounds.Width,
itemHeight);
if (m_hightlight_index == index + m_scroll_index)
g.FillRectangle(new SolidBrush(SystemColors.Highlight), itemBounds);
g.DrawString(m_items[index + m_scroll_index], Font, BackBrush, itemBounds, StringFormat);
}
if (m_items.Length > 10)
{
Rectangle rectangle = new Rectangle(m_drop_down_bounds.X + m_drop_down_bounds.Width - buttonWidth - 1,
m_drop_down_bounds.Y + 1,
buttonWidth,
itemHeight * 10);
g.FillRectangle(new SolidBrush(SystemColors.ScrollBar), rectangle);
rectangle = new Rectangle(m_drop_down_bounds.X + m_drop_down_bounds.Width - buttonWidth - 1,
m_drop_down_bounds.Y + 1,
buttonWidth,
buttonHeight);
ControlPaint.DrawScrollButton(g, rectangle, ScrollButton.Up, m_top_scroll_button_state);
rectangle = new Rectangle(m_drop_down_bounds.X + m_drop_down_bounds.Width - buttonWidth - 1,
m_drop_down_bounds.Y + 1 + itemHeight * 10 - buttonHeight,
buttonWidth,
buttonHeight);
ControlPaint.DrawScrollButton(g, rectangle, ScrollButton.Down, m_bottom_scroll_button_state);
int height = (int)((itemHeight * 10 - 2 * buttonHeight) * 10.0 / m_items.Length);
int y = m_drop_down_bounds.Y + 1 + buttonHeight +
(int)((itemHeight * 10 - 2 * buttonHeight) * m_scroll_index / m_items.Length);
rectangle = new Rectangle(m_drop_down_bounds.X + m_drop_down_bounds.Width - buttonWidth - 1,
y,
buttonWidth,
height);
ControlPaint.DrawButton(g, rectangle, ButtonState.Normal);
}
}
}
protected int GetItemHeight(Graphics g)
{
return (int)g.MeasureString(" ", Font).Height;
}
}