I'm defining shapes which inherit from class Shape and implement the 'Geometry' property.
Here's an example:
public class Landmark : Shape
{
public override bool IsInBounds(Point currentLocation)
{
return (((currentLocation.X >= Location.X - 3) && (currentLocation.X <= Location.X + 3)) && ((currentLocation.Y >= Location.Y - 3) && (currentLocation.Y <= Location.Y + 3)));
}
protected override Geometry DefiningGeometry
{
get
{
var landMark = new EllipseGeometry {Center = Location};
Stroke = Brushes.Red;
return landMark;
}
}
protected override void OnIsMouseDirectlyOverChanged(DependencyPropertyChangedEventArgs e)
{
StrokeThickness = IsMouseDirectlyOver ? 12 : 6;
Mouse.OverrideCursor = IsMouseDirectlyOver ? Mouse.OverrideCursor = Cursors.ScrollAll : Mouse.OverrideCursor = Cursors.Arrow;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Location = e.GetPosition(this);
InvalidateVisual();
}
}
}
When I click on the Shape and move my mouse, I expect the Shape to be redrawn in the new location - and it does work.
However, if I move the mouse "too" quickly, then I'm "leaving" the OnMouseMove event, and the shape gets stuck in the last position which the mouse pointer and the Shape's location were in "sync".
Can such issue be solved?
Yes, by capturing the Mouse.
For that to work you must establish when to capture and when to release. Since you want this to work with the left mouse button, you can capture in OnMouseDown and release the mouse in OnMouseUp.
Related
I'm trying to create a custom Canvas by inherit from Canvas with the so famous feature Zoom and Pan.
I succeeded to Pan and Zoom using the mouse events (move and wheel), BUT : all the canvas (in my case black background) moved and not only my few shapes.
it's about the global Pan and Zoom and not drag&Drop one shape
so I added a custom Shape inherited from shape like this :
public class DataShape : Shape, INotifyPropertyChanged
{
public int ID;
public Geometry DataShapeGeometry;
public ScaleTransform ShapeScaleTransfrom;
public TranslateTransform ShapeTranslateTransform;
public TransformGroup ShapeTransformGroup;
protected override Geometry DefiningGeometry { get { return DataShapeGeometry; } }
public DataShape()
{
DataShapeGeometry = new LineGeometry(); // just to not having Null object that prevent creation of " DataShapeGeometry.Transform "
ShapeTransformGroup = new TransformGroup();
ShapeTranslateTransform = new TranslateTransform();
ShapeScaleTransfrom = new ScaleTransform();
ShapeTransformGroup.Children.Add(ShapeTranslateTransform);
ShapeTransformGroup.Children.Add(ShapeScaleTransfrom);
DataShapeGeometry.Transform = ShapeTransformGroup;
}
for the Custom Canvas I have this (part of the code)
first I used the _pan TranslateTransform as a global Transform, as I said it worked but all my Canvas moved, then i wonted to use the shapeTranslateTransform and the shapeScalTransform , no reaction but i confirmed that the values incremente properly (the zoom geting bigger and bigger , same for the offset, i even used a Textbox to show that :))
public class ZoomPanCanvas : Canvas
{
public static int Global_ID = 0;
public MainWindow w1;
public List<DataShape> selectedDataShapes;
public DataShape selectedShape;
private Point? dragStartPoint = null;
private Matrix transform = Matrix.Identity;
private TranslateTransform _pan = new TranslateTransform();
public ZoomPanCanvas()
{
this.RenderTransform = _pan;
this.MouseWheel += ZoomPanCanvas_MouseWheel;
this.MouseLeftButtonDown += ZoomPanCanvas_MouseLeftButtonDown;
this.MouseMove += ZoomPanCanvas_MouseMove;
this.MouseLeftButtonUp += ZoomPanCanvas_MouseLeftButtonUp;
}
public void RedrawAll()
{
this.InvalidateArrange();
}
#region EVENTS_METHOD
private void ZoomPanCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.Cursor = Cursors.Hand;
dragStartPoint = e.GetPosition(this);
}
bool b = false;int j = 0;
private void ZoomPanCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point point = e.GetPosition(this);
/* this was working <--------------------------------------
_pan.X += point.X - dragStartPoint.Value.X;
_pan.Y += point.Y - dragStartPoint.Value.Y;*/
foreach (UIElement datashape in this.Children)
{
// it works we need to pan each shape
selectedShape = datashape as DataShape;
((TranslateTransform) (selectedShape.ShapeTransformGroup.Children[0])).X += point.X - dragStartPoint.Value.X;
((TranslateTransform)(selectedShape.ShapeTransformGroup.Children[0])).Y += point.Y - dragStartPoint.Value.Y;
//((selectedShape.DataShapeGeometry.Transform)as TranslateTransform).X += point.X - dragStartPoint.Value.X;
// ((selectedShape.DataShapeGeometry.Transform) as TranslateTransform).Y += point.Y - dragStartPoint.Value.Y;
}
dragStartPoint = point;
}
}
private void ZoomPanCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
this.Cursor = Cursors.Arrow;
dragStartPoint = null;
}
private void ZoomPanCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
Point position = e.GetPosition(this);
// scale get only two values, it does'nt increment !
double scale = e.Delta > 0 ? 1.1 : (1.0 / 1.1);
//this.transform.ScaleAt(scale, scale, position.X, position.Y);
//this.RenderTransform = new MatrixTransform(this.transform);
foreach (UIElement datashape in this.Children)
{
// it works we need to pan each shape
selectedShape = datashape as DataShape;
//selectedShape.DataShapeGeometry.Transform.Value.Scale(scale, scale);
((ScaleTransform)(selectedShape.ShapeTransformGroup.Children[1])).ScaleX = ((ScaleTransform)(selectedShape.ShapeTransformGroup.Children[1])).ScaleX * scale;
((ScaleTransform)(selectedShape.ShapeTransformGroup.Children[1])).ScaleY = ((ScaleTransform)(selectedShape.ShapeTransformGroup.Children[1])).ScaleY * scale;
w1.TXT1.Text = ((ScaleTransform)(selectedShape.ShapeTransformGroup.Children[1])).ScaleX.ToString();
}
}
#endregion
}
I trayed to apply the GroupTransform to initialize all types of transform (Translate for Pan andScale for Zoom) in the Datashape Constructor.
I reached "selectedShape.ShapeTransformGroup.Children[]" from inside my ZoomPanCanvas i confirmed that values change but no impact in the interface.
I dare have a great expectation to create a sort of intelligent Cad Canvas (or like visio ).
I will not use ItemsControl, it's useful but when dealing with Algorithmic Geometry I prefer a straightforward simple canvas.
Many thanks
I have a WPF application that suppresses all the mouse inputs so I want to create my own inputs using xaml/behavior for MapSui 3.02. I've added the following method into my ViewModel to allow the user to zoom.
public void MapControlOnMouseWheel(object sender, MouseWheelEventArgs e)
{
Easing easing = default;
if (e.Delta > 0)
{
MapControl.Navigator.ZoomIn(mousePosition, 200, easing);
}
if (e.Delta < 0)
{
MapControl.Navigator.ZoomOut(mousePosition, 200, easing);
}
}
This Zoom method works ok, but it's not as good as the out of the box MapSui Zoom. It's not as smooth, and if you zoom a little too fast it ignores inputs until it has finished the last zoom event.
The out of the box MapSui zoom will zoom faster if you scroll the mouse button faster.
How is this achieved?
For completeness, here is the panning Method which explains where mousePosition comes from in the above zooming Method.
public void MapControlOnMouseMove(object sender, MouseEventArgs e)
{
var screenPosition = e.GetPosition(MapControl);
mousePosition = new Point(screenPosition.X, screenPosition.Y);
Point worldPosition = MapControl.Viewport.ScreenToWorld(screenPosition.X, screenPosition.Y);
if (mouseMiddledown)
{
var panX = lastPosition.X - worldPosition.X;
var panY = lastPosition.Y - worldPosition.Y;
Point newCenter = new Point(lastCentre.X + panX, lastCentre.Y + panY);
MapControl.Navigator?.NavigateTo(newCenter, MapControl.Viewport.Resolution, 0);
lastPosition = new Point(worldPosition.X + panX, worldPosition.Y + panY);
lastCentre = newCenter;
}
else
{
lastPosition = worldPosition;
lastCentre = MapControl.Viewport.Center;
}
CoordsText = $"{worldPosition.X:F0}, {worldPosition.Y:F0}";
}
I want to select some structures (polygons and rectangles) in a panel. I have used the new ExtendedPanel Class, for the opacity of the mouse panel.
panel1 is for the structure, extendedPanel1 is for the selected area of the mouse. (SetSelectionRect() ist the set of the selections area)
In the figure below, the red is the graph I drew on panel1, and the green is the rectangle selected by the mouse. In fact, it should present a green rectangle, which is the rectangle when the mouse selection ends, but now there are many. This shows that the transparency setting in the extendetPanel1 works after extendedPanel1.Invalidate();, but the historical rectangle drawn does not disappear.
Can you please tell me, how should I write the code for the mouse selection in the Panel?
I actually want to realize some polygons and editing. I drew some polygons (rectangles) in panel1, and now I want to use the mouse to select some parts and make some changes (such as deleting some polygons).
My thoughts on this are: Draw the polygons on panel1, and panel2 displays the selection by the mouse, but the bottom of panel2 is transparent.
Then, according to the coordinate calculation, etc., it is judged whether the geometric figure in panel1 is in the area selected in panel2. If it is, then I will delete it. I don’t know if my thoughts are reasonable.
If you can provide a suitable solution, I am very grateful.
code of extendetpanel:
public class ExtendedPanel : Panel
{
private const int WS_EX_TRANSPARENT = 0x20;
public ExtendedPanel()
{
SetStyle(ControlStyles.Opaque, true);
}
private int opacity = 0;
[DefaultValue(0)]
public int Opacity
{
get
{
return this.opacity;
}
set
{
if (value < 0 || value > 100)
throw new ArgumentException("value must be between 0 and 100");
this.opacity = value;
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | WS_EX_TRANSPARENT;
return cp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
using (var brush = new SolidBrush(Color.FromArgb(this.opacity * 255 / 100, this.BackColor)))
{
e.Graphics.FillRectangle(brush, this.ClientRectangle);
}
base.OnPaint(e);
}
}
Code of paint and events:
private void extendedPanel1_Paint(object sender, PaintEventArgs e)
{
base.OnPaint(e);
extendedPanel1.Opacity = 0;
if (mouseDown)
{
using (Pen pen = new Pen(Color.Green, 1F))
{
pen.DashStyle = DashStyle.Dash;
e.Graphics.DrawRectangle(pen, selection);
}
}
}
private void extendedPanel1_MouseDown(object sender, MouseEventArgs e)
{
selectionStart = extendedPanel1.PointToClient(MousePosition);
mouseDown = true;
}
private void extendedPanel1_MouseUp(object sender, MouseEventArgs e)
{
mouseDown = false;
SetSelectionRect();
extendedPanel1.Invalidate();
}
private void extendedPanel1_MouseMove(object sender, MouseEventArgs e)
{
if (!mouseDown)
return;
selectionEnd = extendedPanel1.PointToClient(MousePosition);
SetSelectionRect();
extendedPanel1.Invalidate();
}
I know there are many discussion this topic already but still i could not find an answer. Hope someone have a idea.
I load a bitmap in a PictureBox (SizeMode Normal, DockStyle Fill) and use the Paint event to draw the image.
To zoom I use MouseWheel event and increase / decrease zoom factor (Like _zoomFac += 1). The goal is to zoom at the mouse position. So in the MouseWheel event I also save the mouse position (_imageZoomLocation). This is my code (only little test implementation):
public partial class Form1 : Form
{
private Image _image;
private float _zoomFac;
private PointF _imageZoomLocation;
public Form1()
{
InitializeComponent();
_image = null;
_zoomFac = 1F;
_imageZoomLocation = PointF.Empty;
pictureBox.MouseWheel += new MouseEventHandler(OnMouseWheel);
}
private void pb_Paint(object sender, PaintEventArgs e)
{
if (_image == null)
return;
e.Graphics.TranslateTransform(-_imageZoomLocation.X + _imageZoomLocation.X / _zoomFac, -_imageZoomLocation.Y + _imageZoomLocation.Y / _zoomFac);
e.Graphics.ScaleTransform(_zoomFac, _zoomFac, MatrixOrder.Append);
e.Graphics.DrawImage(_image, new Point(0,0));
}
private void fileToolStripMenuItem_Click(object sender, EventArgs e)
{
... Code for loading image
}
private void OnMouseWheel(object sender, MouseEventArgs e)
{
if (_image == null)
return;
_imageZoomLocation = e.Location;
if (e.Delta > 0)
_zoomFac += 1F;
else
if (_zoomFac - 1F < 1F)
_zoomFac = 1F;
else
_zoomFac -= 1F;
Refresh();
}
private void pb_MouseEnter(object sender, EventArgs e)
{
pictureBox.Focus();
}
}
This works well as long as _imageZoomLocation is not changed, means the mouse is not moved i can zoom in and out perfectly (I can change mouse position when zoom factor is 1 and everything still works). However when I move the mouse to different position in a zoomed state (e.g. zoom factor is 2) and scroll, the image jumpes once to a different position than the mouse is and after this "jump" everything works well again.
Can anyone explain this behaviour?
In the following line, you are dividing imageZoomLocation.X and .Y by the zoom factor only half the time. Instead, maybe you need a separate variable to keep the previous mouse location.
e.Graphics.TranslateTransform(-_imageZoomLocation.X + _imageZoomLocation.X / _zoomFac, _
-_imageZoomLocation.Y + _imageZoomLocation.Y / _zoomFac);
I'm creating a simple application that draws a horizontal and a vertical line following the mouse.
The form is transparent using TransparencyKey, and the lines are drawn using the Paint event:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Pen pen = new Pen(Color.Lime);
e.Graphics.DrawLine(pen, 0, py, this.Size.Width, py);
e.Graphics.DrawLine(pen, px, 0, px, this.Size.Height);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
px = e.X; // get cursor X pos
py = e.Y; // get cursor Y pos
Invalidate(); // fire Paint event
}
But the MouseMove event only is fired when the mouse is over the lines drawn. How to make the form catch mouse events when transparent? (Only the mouse move, I want the form still click-through)
As you can read here: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.transparencykey%28v=vs.110%29.aspx - "Any mouse actions, such as the click of the mouse, that are performed on the transparent areas of the form will be transferred to the windows below the transparent area". One way to accomplish your goal is to write your own method, that will check the cursor relative position to the form in a loop, here's what I mean (it worked for me with TransparencyKey, resizing and moving the form):
private void MouseMovedWithTransparency()
{
Point lastCursorPos = new Point(-1, -1); // Starting point.
while (this.Visible)
{
Point currentCursorPos = Cursor.Position;
if (this.ContainsFocus && currentCursorPos.X > this.Left && currentCursorPos.X < this.Left + this.Width &&
currentCursorPos.Y > this.Top && currentCursorPos.Y < this.Top + this.Height)
{
if ((currentCursorPos.X != lastCursorPos.X) || (currentCursorPos.Y != lastCursorPos.Y))
{
// Do actions as in MouseMoved event.
// Save the new position, so it won't be triggered, when user doesn't move the cursor.
lastCursorPos = currentCursorPos;
}
}
Application.DoEvents(); // UI must be kept responsive.
Thread.Sleep(1);
}
}
All you have to do now is to invoke this method from the Shown event - this.Visible has to be true, so it's the easiest way to make it work.