我到处寻找并且没有遇到任何事情,但我想知道包含选择矩形的最佳方式,以便它不会超出范围。我有一个应用程序是用户在图像上绘制一个选择矩形。矩形也可以移动和调整大小。目前我只使用一个异常处理程序,当捕获超出范围的异常时,它会提醒用户。超出范围的例外仅在移动绘制的矩形时发生,并且我想使其更加流线化,实际的矩形不能在图像外拖动或调整大小。下面是我的裁剪控件的xaml和代码。
作物控制代码背后:
public partial class CropControl : UserControl
{
#region Data's
private bool isDragging = false;
private Point anchorPoint = new Point();
private bool MoveRect = false; //flag which intially set to false which means a crop rectangle is not moved but created.
private bool MoveInProgress = false; //flag that is set to true if the crop rect is moving, otherwise false.
private Point LastPoint; // The drag's last point
HitType MouseHitType = HitType.None; //part of the rectangle under the mouse
private enum HitType { None, Body, UL, UR, LR, LL, L, R, T, B }; //Enum for the part of the rectangle the mouse is over.
#endregion
#region Constructor
public CropControl()
{
InitializeComponent();
}
#endregion
#region Dependency Property
//Register the Dependency Property
public static readonly DependencyProperty SelectionProperty =
DependencyProperty.Register("Selection", typeof(Rect), typeof(CropControl), new PropertyMetadata(default(Rect)));
public Rect Selection
{
get { return (Rect)GetValue(SelectionProperty); }
set { SetValue(SelectionProperty, value); }
}
// this is used, to react on changes from ViewModel. If you assign a
// new Rect in your ViewModel you will have to redraw your Rect here
private static void OnSelectionChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
{
Rect newRect = (Rect)e.NewValue;
Rectangle selectionRectangle = d as Rectangle;
if (selectionRectangle != null)
return;
selectionRectangle.SetValue(Canvas.LeftProperty, newRect.X);
selectionRectangle.SetValue(Canvas.TopProperty, newRect.Y);
selectionRectangle.Width = newRect.Width;
selectionRectangle.Height = newRect.Height;
}
#endregion
private Point lastLoc;
#region MouseLeftButtonDown Event
private void LoadedImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
lastLoc = new Point(Canvas.GetLeft(selectionRectangle), Canvas.GetTop(selectionRectangle));
//This statement will enable the creation of a new rectangle only if the mouse left
//button press is outside of a created rectangle and that crop rectangle was initially created.
//This is known since the HitType if outside the rectangle will always be set to None and the crop rect width > 0.
//The previous cropping rect will be removed by setting its value to null.
if (MouseHitType== HitType.None && selectionRectangle.Width>0)
{
selectionRectangle.Width = 0; //set crop rectangle's width to 0
selectionRectangle.Height = 0; //set crop rectangle's height to 0
SetMouseCursor();
MoveRect = false; //flag that crop rectangle is not being moved but drawn.
}
//This statement test if the crop rectangle is not being dragged and moved. If true it would
//set the x and y position of the crop rect in accordance to Canvas. If false it means that
//crop rectangle was already created and is now being moved to different position in the canvas.
if (!isDragging && !MoveRect)
{
anchorPoint.X = e.GetPosition(BackPanel).X; //get the x position of the mouse
anchorPoint.Y = e.GetPosition(BackPanel).Y; //get the y position of the mouse
isDragging = true; //flag that the user is dragging the mouse to create a rectangle
BackPanel.Cursor = Cursors.Cross; //change the cursor to a cross while left button is held down
}
else
{
MouseHitType = SetHitType(selectionRectangle, e.GetPosition(BackPanel)); //get hittype
SetMouseCursor(); //set the mouse cursor based on the hittype
if (MouseHitType == HitType.None) return;
LastPoint = e.GetPosition(BackPanel);
MoveInProgress = true; //flag true since rectangle is being moved
}
}
#endregion
private double CanvasTop, CanvasLeft;
#region MouseMove Event
private void LoadedImage_MouseMove(object sender, MouseEventArgs e)
{
Point offset = new Point((anchorPoint.X-lastLoc.X),(anchorPoint.Y-lastLoc.Y));
var newX=(anchorPoint.X+(e.GetPosition(BackPanel).X)-anchorPoint.X);
var newY=(anchorPoint.Y+(e.GetPosition(BackPanel).Y)-anchorPoint.Y);
CanvasTop = newX - offset.X;
CanvasLeft = newY - offset.Y;
//Statement that checks if crop rect is being created or moved. If moved it will set the
//dimension of the rectanlge and if not it would set the location of the new rectangle.
if (isDragging && !MoveRect)
{
double x = e.GetPosition(BackPanel).X; //get x position of mouse
double y = e.GetPosition(BackPanel).Y; //get y position of mouse
selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X)); //set the bottom
selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y)); //set the top
selectionRectangle.Width = Math.Abs(x - anchorPoint.X); //set the width
selectionRectangle.Height = Math.Abs(y - anchorPoint.Y); //set the height
if (selectionRectangle.Visibility != Visibility.Visible) //make crop rectangle visible if its not.
selectionRectangle.Visibility = Visibility.Visible;
}
else if (!isDragging && MoveRect)
{
if (!MoveInProgress)
{
MouseHitType = SetHitType(selectionRectangle, e.GetPosition(BackPanel));
SetMouseCursor();
}
else
{
// See how much the mouse has moved.
Point point = e.GetPosition(BackPanel);
double offset_x = point.X - LastPoint.X;
double offset_y = point.Y - LastPoint.Y;
// Get the rectangle's current position.
double new_x = Canvas.GetLeft(selectionRectangle);
double new_y = Canvas.GetTop(selectionRectangle);
double new_width = selectionRectangle.Width;
double new_height = selectionRectangle.Height;
// Update the rectangle.
switch (MouseHitType)
{
case HitType.Body:
new_x += offset_x;
new_y += offset_y;
break;
case HitType.UL:
new_x += offset_x;
new_y += offset_y;
new_width -= offset_x;
new_height -= offset_y;
break;
case HitType.UR:
new_y += offset_y;
new_width += offset_x;
new_height -= offset_y;
break;
case HitType.LR:
new_width += offset_x;
new_height += offset_y;
break;
case HitType.LL:
new_x += offset_x;
new_width -= offset_x;
new_height += offset_y;
break;
case HitType.L:
new_x += offset_x;
new_width -= offset_x;
break;
case HitType.R:
new_width += offset_x;
break;
case HitType.B:
new_height += offset_y;
break;
case HitType.T:
new_y += offset_y;
new_height -= offset_y;
break;
}
// Don't use negative width or height.
if ((new_width > 0) && (new_height > 0))
{
// Update the rectangle.
Canvas.SetLeft(selectionRectangle, new_x);
Canvas.SetTop(selectionRectangle, new_y);
selectionRectangle.Width = new_width;
selectionRectangle.Height = new_height;
// Save the mouse's new location.
LastPoint = point;
}
}
}
}
#endregion
#region MouseLeftButtonUp Event
private void LoadedImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
//statement which checks if the mouse left button action is for either creating or
//moving the crop rectangle. If true, isDragging=false since the crop rect is created
//and moverect = true since the created rectangle is ready to be moved.
if (isDragging && !MoveRect)
{
isDragging = false;
if (selectionRectangle.Width > 0)
{
MoveRect = true;
}
}
else
{
MoveInProgress = false; //flags Move in progress as false since rect move action is done.
}
// Set the Selection to the new rect, when the mouse button has been released
Selection = new Rect(
(double)selectionRectangle.GetValue(Canvas.LeftProperty),
(double)selectionRectangle.GetValue(Canvas.TopProperty),
selectionRectangle.Width,
selectionRectangle.Height);
}
#endregion
#region Mutator's
// Return a HitType value to indicate what is at the point.
private HitType SetHitType(Rectangle rect, Point point)
{
double left = Canvas.GetLeft(selectionRectangle);
double top = Canvas.GetTop(selectionRectangle);
double right = left + selectionRectangle.Width;
double bottom = top + selectionRectangle.Height;
//statement that checks if cursor is outside the area of the crop rectangle
//and returns HitType.None.
if (point.X < left) return HitType.None;
if (point.X > right) return HitType.None;
if (point.Y < top) return HitType.None;
if (point.Y > bottom) return HitType.None;
const double GAP = 10; //sets the gap which when mouse over a cursor change is triggered
//statement that checks where the mouse is located within the rectangle.
if (point.X - left < GAP)
{
// Left edge.
if (point.Y - top < GAP) return HitType.UL;
if (bottom - point.Y < GAP) return HitType.LL;
return HitType.L;
}
if (right - point.X < GAP)
{
// Right edge.
if (point.Y - top < GAP) return HitType.UR;
if (bottom - point.Y < GAP) return HitType.LR;
return HitType.R;
}
if (point.Y - top < GAP) return HitType.T;
if (bottom - point.Y < GAP) return HitType.B;
return HitType.Body;
}
// Set a mouse cursor appropriate for the current hit type.
private void SetMouseCursor()
{
// See what cursor we should display.
Cursor desired_cursor = Cursors.Arrow;
switch (MouseHitType)
{
case HitType.None:
desired_cursor = Cursors.Arrow;
break;
case HitType.Body:
desired_cursor = Cursors.ScrollAll;
break;
case HitType.UL:
case HitType.LR:
desired_cursor = Cursors.SizeNWSE;
break;
case HitType.LL:
case HitType.UR:
desired_cursor = Cursors.SizeNESW;
break;
case HitType.T:
case HitType.B:
desired_cursor = Cursors.SizeNS;
break;
case HitType.L:
case HitType.R:
desired_cursor = Cursors.SizeWE;
break;
}
// Display the desired cursor.
if (BackPanel.Cursor != desired_cursor)
BackPanel.Cursor = desired_cursor;
}
#endregion
}
裁剪控制XAML:
<UserControl.Resources>
<Storyboard x:Key="MarchingAnts">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="selectionRectangle"
Storyboard.TargetProperty="(Shape.StrokeDashOffset)"
RepeatBehavior="Forever">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000"
Value="10"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource MarchingAnts}"/>
</EventTrigger>
</UserControl.Triggers>
<Canvas Name="BackPanel" Background="Transparent" MouseLeftButtonDown="LoadedImage_MouseLeftButtonDown" MouseMove="LoadedImage_MouseMove" MouseLeftButtonUp="LoadedImage_MouseLeftButtonUp">
<Rectangle Name="selectionRectangle" Stroke="#FFFFFFFF"
StrokeThickness="1" StrokeDashOffset="0"
Fill="#220000FF" Visibility="Collapsed"
StrokeDashArray="5"/>
</Canvas>
对于这种困惑感到抱歉,但我改变了我的解释。绘制时矩形不会超出界限,但只有在移动绘制的矩形时才会出现。此异常也在我的视图模型的裁剪方法中捕获,如下所示:
public void Crop()
{
////Get a copy of the selection in case it changes during execution
Rect cropSelection = Selection;
//// use it to crop your image
Int32Rect rcFrom = new Int32Rect();
rcFrom.X = (int)((cropSelection.X) * (ImagePath.Width) / (ImagePath.Width));
rcFrom.Y = (int)((cropSelection.Y) * (ImagePath.Height) / (ImagePath.Height));
rcFrom.Width = (int)cropSelection.Width;
rcFrom.Height = (int)cropSelection.Height;
try
{
BitmapSource bs = new CroppedBitmap(ImagePath as BitmapSource, rcFrom);
CroppedImage = bs;
}
catch (Exception e)
{
MessageBox.Show("Selection Rectangle is outside the image." + "\n" + "Adjust the cropping rectangle so it's within the boundaries of the Image ", " Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
更新
我能够通过计算选择矩形相对于其父(Canvas)的大小和位置来实现它,它采用与图像相同的大小。以下是我添加到我的代码中的内容。
double bottom = new_y + selectionRectangle.Height;
double right = new_x+selectionRectangle.Width;
if (new_y< 0)
new_y = 0;
if (new_x < 0)
new_x = 0;
if (bottom > BackPanel.ActualHeight)
new_y = BackPanel.ActualHeight-selectionRectangle.Height;
if (right > BackPanel.ActualWidth)
new_x = BackPanel.ActualWidth - selectionRectangle.Width;
if (new_height > BackPanel.ActualHeight)
new_height = BackPanel.ActualHeight;
if (new_width > BackPanel.ActualWidth)
new_width = BackPanel.ActualWidth;
添加了new_height和new_width,因为如果矩形占据整个图像,仍然会抛出异常。
答案 0 :(得分:0)
如果我正确理解了问题 - 当选择rect超出图像rect时,您会向用户显示消息?如果是这样,为什么不检查:如果新选择状态将超出图像区域 - 那么只是不移动选择并保持旧状态?我的意思是 - 将新选择rect的边界位置与图像rect进行比较并做出决定:将您的选择矩移动到新位置(或改变或不改变它的大小)。