I'm creating app for image scan with WIA. But my images has a lot of unused space if scanned document size is not big. I need to crop that unused space like in Paint with Cross cursor and rectangle. How to do that in WPF?
Code of image cropping is:
private static Image cropImage(Image img, Rectangle cropArea)
{
Bitmap bmpImage = new Bitmap(img);
Bitmap bmpCrop = bmpImage.Clone(cropArea,bmpImage.PixelFormat);
return (Image)(bmpCrop);
}
Put the Image control in a Canvas and add an invisible rectangle to the canvas as well.
On left mouse button down set the top left of the rectangle at the mouse coordinates and make it visible.
On mouse move (and mouse button still down), set the size of the rectangle, so the opposite of the first corner moves with the mouse.
Example:
<Window x:Class="TestWpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
Title="MainWindow"
Height="350"
Width="525">
<Canvas MouseLeftButtonDown="Canvas_MouseLeftButtonDown"
MouseMove="Canvas_MouseMove"
Background="AntiqueWhite">
<Image Width="500"
Height="300" />
<Rectangle x:Name="selectionRectangle"
StrokeThickness="1"
Stroke="LightBlue"
Fill="#220000FF"
Visibility="Collapsed" />
</Canvas>
</Window>
And the code behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TestWpfApplication
{
public partial class MainWindow : System.Windows.Window
{
public MainWindow()
{
InitializeComponent();
}
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var mousePosition = e.GetPosition(sender as UIElement);
Canvas.SetLeft(selectionRectangle, mousePosition.X);
Canvas.SetTop(selectionRectangle, mousePosition.Y);
selectionRectangle.Visibility = System.Windows.Visibility.Visible;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var mousePosition = e.GetPosition(sender as UIElement);
selectionRectangle.Width = mousePosition.X - Canvas.GetLeft(selectionRectangle);
selectionRectangle.Height = mousePosition.Y - Canvas.GetTop(selectionRectangle);
}
}
}
}
Note that the Canvas will only respond to a click when it has a color.
Also: you'll need to handle the situation where the user drags the selection right to left and/or bottom to top because that will introduce negative sizes for the width and height of the rectangle. But I'll leave that to you.
Thanks for this code. My actual issues realted to CROP iamge show in another iamge control. How ist possible in WPF. Please fallow this link.
http://social.msdn.microsoft.com/Forums/en-US/302bb1c8-e272-48b1-982d-12cf2aefe8a3/how-to-crop-image-show-in-another-image-control-?forum=wpf
Thank
Related
I have a series of rectangles in which the user can add images to, by dragging the images in.
The image is then scaled down in proportion and the rectangle is then filled with an ImageBrush.
I need for the user to be able to manipulate the image within the rectangle to fit their needs. Like any photo collage app does.
My question is: How can I show the full, unmasked image on top of the rectangle so that the user can manipulate it to their needs? I don't know where to start with this one.
private async void Mask_Drop(object sender, DragEventArgs e)
{
Rectangle maskSq = e.OriginalSource as Rectangle;
var maskW = maskSq.Width.ToSingle();
var maskH = maskSq.Height.ToSingle();
double maskX = Canvas.GetLeft(maskSq);
double maskY = Canvas.GetTop(maskSq);
// Image sizes for bounding to mask
float boundH = Convert.ToSingle(size.Height);
float boundW = Convert.ToSingle(size.Width);
maskSq.Fill = new ImageBrush
{
ImageSource = new BitmapImage(new Uri("ms-appdata:///local/" + SelectedImage.Name, UriKind.Absolute)),
Stretch = Stretch.UniformToFill
};
}
private void Tap_Collage(object sender, TappedRoutedEventArgs e)
{
//Gets the full image from ImageBrush
ImageBrush brush = (ImageBrush)(((Rectangle)sender).Fill);
Rectangle rect = sender as Rectangle;
//Mask sure rectangle does not drag, just the image brush
rect.CanDrag = false;
rect.StrokeThickness = 6;
//Drag Image Functionality
rect.ManipulationDelta += ImageManipulation.Resize_ImageEdit;
ImageManipulation.ImageEdit_Drag = new TranslateTransform();
brush.Transform = ImageManipulation.ImageEdit_Drag;
//Zoom Image Functionality
ImageManipulation.ImageEdit_Zoom = new ScaleTransform();
brush.RelativeTransform = ImageManipulation.ImageEdit_Zoom;
}
Class
public static class ImageManipulation
{
public static TranslateTransform ImageEdit_Drag;
public static ScaleTransform ImageEdit_Zoom;
public static RotateTransform ImageEdit_Rotate;
public static void Resize_ImageEdit(object sender, ManipulationDeltaRoutedEventArgs e)
{
ImageEdit_Drag.X += e.Delta.Translation.X;
ImageEdit_Drag.Y += e.Delta.Translation.Y;
ImageEdit_Zoom.ScaleX *= e.Delta.Scale;
ImageEdit_Zoom.ScaleY *= e.Delta.Scale;
if (ImageEdit_Zoom.ScaleX < 1.0)
{
ImageEdit_Zoom.ScaleX = 1.0;
ImageEdit_Zoom.ScaleY = 1.0;
}
ImageEdit_Rotate.Angle += e.Delta.Rotation;
}
}
XAML
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:CollageGrid">
<Rectangle Width="{Binding CollageW}" Height="{Binding CollageH}" AllowDrop="True" CanDrag="True" Fill="Transparent"
Drop="Mask_Drop"
DragOver="Mask_DragOver"
ManipulationMode="TranslateX, TranslateY" Stroke="Black" StrokeThickness="2" DragEnter="Mask_DragEnter" DragLeave="Mask_DragLeave" Tapped="Tap_Collage">
<Rectangle.RenderTransform>
<TranslateTransform X="{Binding CollageX}" Y="{Binding CollageY}"/>
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
Example of what I'm looking to acheive:
How it looks currently:
You can try to replace the Rectangle with the Polyline to draw the Rectangle so that you can access the Image which is on the bottom of the Rectangle.
<Grid>
<Image Source="Assets/image1.jpg" Width="800"
Height="400" Tapped="Image_Tapped" />
<Polyline Stroke="Black" HorizontalAlignment="Center"
VerticalAlignment="Center"
StrokeThickness="4" Tapped="Polyline_Tapped"
Points="0,0,0,200,200,200,200,0,0,0" />
</Grid>
---Update---
UniformToFill will cause the image is resized to fill the destination dimensions while it preserves its native aspect ratio. If the aspect ratio of the destination rectangle differs from the source, the source content is clipped to fit in the destination dimensions. So it is not suitable for your requirement. You should manually scale the Image to make image fit one of your Rectangle's border and keep other part out of the Rectangle.
It seems you put the image as the Rectangle's background image brush, there are no other place to display the image out of the Rectangle. So I think, we may take a considerition for a new scenario.
As my pervious reply, using Image control to display the image and Polylines to draw a Rectangle above the Image so that we can operate the Image which is below the Rectangle using the manipulation events to fit the rectangle, meanwhile we can also use the community toolkit BackdropBlurBrush on the xaml layout to Blur the outer image.
I'm a newbie, I'm trying to built a visual drawing. I have a canvas and images (images which are created by clicked event) on this canvas.
Code XAML:
<Canvas Name="drawing" Background="Black"
MouseDown="drawing_MouseDown"
MouseMove="drawing_MouseMove"
MouseUp="drawing_MouseUp">
<Canvas.RenderTransform>
<MatrixTransform/>
</Canvas.RenderTransform>
</Canvas>
Code-behind:
namespace Panning_used_MatrixTransform
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private Point firstPoint = new Point();
private void drawing_MouseDown(object sender, MouseButtonEventArgs e)
{
firstPoint = e.GetPosition(this);
drawing.CaptureMouse();
}
private void drawing_MouseMove(object sender, MouseEventArgs e)
{
if (e.MiddleButton == MouseButtonState.Pressed)
{
var lsPoint = e.GetPosition(this);
var res = lsPoint - firstPoint;
var element = sender as UIElement;
var transform = element.RenderTransform as MatrixTransform;
var matrix = transform.Matrix;
matrix.TranslatePrepend(res.X, res.Y);
transform.Matrix = matrix;
//udate first point
firstPoint = lsPoint;
}
}
private void drawing_MouseUp(object sender, MouseButtonEventArgs e)
{
drawing.ReleaseMouseCapture();
}
}
}
I don't know how to keeping position and size of canvas not change when we panning this canvas (like CAD drawing).
More information:
I want to create the software which allow at the beginning we has nothing on the canvas. When we click a button, we can create some images where we want on the canvas. And clearly at the beginning nothing on it (So nothing on the canvas - XAML code).
Please help me!
For your requirement, the manipulation should be performed on the images, instead of the canvas. So when you pan the images, the canvas stays still.
I make a few changes to the XAML code, add another image for testing, also move the event from the canvas to the outer grid, but I do not rename your event handlers so you can compile the new code more easily.
<Grid Background="Black"
MouseDown="drawing_MouseDown"
MouseMove="drawing_MouseMove"
MouseUp="drawing_MouseUp">
<Canvas Name="drawing">
<Image x:Name="imgSource" Source="Resources/my-pic.png"
Height="100" Width="100" Canvas.Top="30" Canvas.Left="30">
<Image.RenderTransform>
<MatrixTransform/>
</Image.RenderTransform>
</Image>
<Image x:Name="imgSource2" Source="Resources/my-pic.png"
Height="100" Width="100" Canvas.Top="230" Canvas.Left="230">
<Image.RenderTransform>
<MatrixTransform/>
</Image.RenderTransform>
</Image>
</Canvas>
</Grid>
In the code behind, I apply the manipulation to each child element of the canvas.
I also remove the CaptureMouse()/ReleaseMouseCapture() since I think those two lines are not necessary.
private void drawing_MouseMove(object sender, MouseEventArgs e)
{
if (e.MiddleButton == MouseButtonState.Pressed))
{
var lsPoint = e.GetPosition(this);
var res = lsPoint - firstPoint;
foreach (UIElement element in drawing.Children)
{
var transform = element.RenderTransform as MatrixTransform;
var matrix = transform.Matrix;
matrix.TranslatePrepend(res.X, res.Y);
transform.Matrix = matrix;
}
//udate first point
firstPoint = lsPoint;
}
}
I need to show images (using Flip View controll) and allow users to zoom them (with pinch zoom and with double tap) and drag zoomed image.
I would like it to be compatible with Flip View (I mean it shouldn't ovveride draging gesture).
What is the best way to achieve that?
I need to show images (using Flip View controll) and allow users to zoom them (with pinch zoom and with double tap) and drag zoomed image.
We can use the ScrollViewer control and UIElement.DoubleTapped event to allow users to zoom the images (with pinch zoom and with double tap) and drag zoomed image.
In order to zoom the image with pinch zoom and drag zoomed image. We can put the Image into the ScrollViewer.
We can add UIElement.DoubleTapped event in the ScrollViewer to zoom the image with double tap.
For example:
In XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<FlipView Name="MyFlipView">
<FlipView.ItemTemplate>
<DataTemplate x:DataType="local:MyImage">
<ScrollViewer MinZoomFactor="1" DoubleTapped="scrollViewer_DoubleTapped"
ZoomMode="Enabled">
<Image Source="{Binding Path}" />
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
In code behind:
public ObservableCollection<MyImage> MyImages;
public MainPage()
{
this.InitializeComponent();
MyImages = new ObservableCollection<MyImage>();
MyImages.Add(new MyImage("ms-appx:///Assets/cliff.jpg"));
MyImages.Add(new MyImage("ms-appx:///Assets/grapes.jpg"));
MyImages.Add(new MyImage("ms-appx:///Assets/rainer.jpg"));
MyImages.Add(new MyImage("ms-appx:///Assets/sunset.jpg"));
MyImages.Add(new MyImage("ms-appx:///Assets/valley.jpg"));
MyFlipView.ItemsSource = MyImages;
}
private async void scrollViewer_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
var scrollViewer = sender as ScrollViewer;
var doubleTapPoint = e.GetPosition(scrollViewer);
if (scrollViewer.ZoomFactor != 1)
{
scrollViewer.ZoomToFactor(1);
}
else if (scrollViewer.ZoomFactor == 1)
{
scrollViewer.ZoomToFactor(2);
var dispatcher = Window.Current.CoreWindow.Dispatcher;
await dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
scrollViewer.ScrollToHorizontalOffset(doubleTapPoint.X);
scrollViewer.ScrollToVerticalOffset(doubleTapPoint.Y);
});
}
}
And the MyImage Class code:
public class MyImage
{
public MyImage()
{
}
public MyImage(string uri)
{
this.Path = uri;
}
public string Path { get; set; }
}
I'm using the following WPF
<Button Style="{DynamicResource NoChromeButton}" x:Name="cmdImage" Grid.Row="1" Margin="10" MouseDoubleClick="cmdImage_MouseDoubleClick" MouseDown="imgMain_MouseDown_1" MouseMove="imgMain_MouseMove_1" MouseUp="imgMain_MouseUp_1">
<Grid x:Name="ImageGrid">
<Image x:Name="imgMain" Panel.ZIndex="0" />
<Button x:Name="rectBounds" Template="{StaticResource DesignerItemTemplate}" Visibility="Hidden" IsVisibleChanged="Button_IsVisibleChanged" Panel.ZIndex="1" />
</Grid>
</Button>
The weird part is that the MouseUp, MouseDown, and MouseMove events of the outermost button don't even trigger iff the ImageSource of the Image isn't null (an image is loaded).
I tried moving them to the Image control. They do trigger, but behave unexpectedly.
private void imgMain_MouseDown_1(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(ImageGrid);
rect = new Rectangle
{
Margin =
new Thickness(e.GetPosition(ImageGrid).X, e.GetPosition(ImageGrid).Y,
ImageGrid.ActualWidth - e.GetPosition(ImageGrid).X,
ImageGrid.ActualHeight - e.GetPosition(ImageGrid).Y),
Stroke = Brushes.Black,
StrokeThickness = 1.0
};
ImageGrid.Children.Add(rect);
Panel.SetZIndex(rect, 2);
}
private void imgMain_MouseMove_1(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released || rect == null)
return;
Point pos = e.GetPosition(ImageGrid);
double x = Math.Min(pos.X, startPoint.X);
double y = Math.Min(pos.Y, startPoint.Y);
double w = Math.Max(pos.X, startPoint.X) - x;
double h = Math.Max(pos.Y, startPoint.Y) - y;
rect.Margin = new Thickness(x, y, ImageGrid.ActualWidth - x - w, ImageGrid.ActualHeight - y - h);
}
private void imgMain_MouseUp_1(object sender, MouseButtonEventArgs e)
{
rect = null;
}
By all apparent rules, a draggable rectangle should appear, and disappear once you let go of the mouse button. It doesn't. What's funny is that when I change the visibility of rectBounds, a rectangle does appear.
There are a few things to fix here.
First, your XAML. A button inside another button? I can't imagine how that should feel. Then the ZIndex. It's redundant since Image and Button are in the same Grid cell and the Image is declared before the Button. And it's also not necessary to set in on the newly created Rectangle in imgMain_MouseDown_1.
The weird part is that the MouseUp, MouseDown, and MouseMove events of
the outermost button don't even trigger if the ImageSource of the
Image isn't null.
This is exactly what you would expect, because a mouse event is only generated on those areas of a control that are actually drawn, in other words where hit testing succeeds. When there is no actual content (no image in your example), hit testing fails. You might simply assign a transparent background to the Grid to ensure that hit testing always succeeds:
<Grid x:Name="ImageGrid" Background="Transparent">
<Image x:Name="imgMain" />
<Button x:Name="rectBounds" Template="{StaticResource DesignerItemTemplate}" Visibility="Hidden" IsVisibleChanged="Button_IsVisibleChanged" />
</Grid>
By all apparent rules, a draggable rectangle should appear, and
disappear once you let go of the mouse button.
No it shouldn't, unless you actually remove that Rectangle from the Grid. Setting rect = null doesn't do that.
private void imgMain_MouseUp_1(object sender, MouseButtonEventArgs e)
{
ImageGrid.Children.Remove(rect);
rect = null;
}
Is there a way to bind to the mouse position in WPF in the XAML file? Or does that have to be done in code? I have a control inside a Canvas, and I just want the control to follow the mouse while the mouse cursor is inside the Canvas.
Thanks
EDIT:
OK, I figured it out a relatively easy way using the code-behind file. I added a MouseMove event handler on the Canvas, and then added:
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
// Get the x and y coordinates of the mouse pointer.
System.Windows.Point position = e.GetPosition(this);
double pX = position.X;
double pY = position.Y;
// Sets the position of the image to the mouse coordinates.
myMouseImage.SetValue(Canvas.LeftProperty, pX);
myMouseImage.SetValue(Canvas.TopProperty, pY);
}
using http://msdn.microsoft.com/en-us/library/ms746626.aspx as a guideline.
I tried to make a kind of decorator for this purpose.
You wrap the object, mouse position above which you want to control and bind some control to decorator MousePosition property.
public class MouseTrackerDecorator : Decorator
{
static readonly DependencyProperty MousePositionProperty;
static MouseTrackerDecorator()
{
MousePositionProperty = DependencyProperty.Register("MousePosition", typeof(Point), typeof(MouseTrackerDecorator));
}
public override UIElement Child
{
get
{
return base.Child;
}
set
{
if (base.Child != null)
base.Child.MouseMove -= _controlledObject_MouseMove;
base.Child = value;
base.Child.MouseMove += _controlledObject_MouseMove;
}
}
public Point MousePosition
{
get
{
return (Point)GetValue(MouseTrackerDecorator.MousePositionProperty);
}
set
{
SetValue(MouseTrackerDecorator.MousePositionProperty, value);
}
}
void _controlledObject_MouseMove(object sender, MouseEventArgs e)
{
Point p = e.GetPosition(base.Child);
// Here you can add some validation logic
MousePosition = p;
}
}
and XAML
<local:MouseTrackerDecorator x:Name="mouseTracker">
<Canvas Width="200" Height="200" Background="Red">
<Button Width="20" Height="20" Canvas.Left="{Binding ElementName=mouseTracker, Path=MousePosition.X}" Canvas.Top="{Binding ElementName=mouseTracker, Path=MousePosition.Y}" />
</Canvas>
</local:MouseTrackerDecorator>
Tried a few examples only. The msdn documentation is , i think, incorrectly worded
"How to: Make an Object Follow the Mouse Pointer"
should be
"How to: Make an object's size increase based on mouse position"
anyway.
I was able to achieve this effect by changing the canvas properties. Also not sure why everyone was attaching the event handler to the objects next top level layout property and not the window. Maybe you and most of the examples online are going for a different effect
<Window x:Class="FollowMouse.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" MouseMove="MouseMoveHandler">
<Canvas>
<Ellipse Name="ellipse" Fill="LightBlue"Width="100" Height="100"/>
</Canvas>
code behind
private void MouseMoveHandler(object sender, MouseEventArgs e)
{
/// Get the x and y coordinates of the mouse pointer.
System.Windows.Point position = e.GetPosition(this);
double pX = position.X;
double pY = position.Y;
/// Sets eclipse to the mouse coordinates.
Canvas.SetLeft(ellipse, pX);
Canvas.SetTop(ellipse, pY);
Canvas.SetRight(ellipse, pX);
}