2015-04-05 42 views
0

我到處尋找並沒有遇到任何東西,但我想知道包含選擇矩形的最佳方式,以便它不會超出範圍。我有一個應用程序,用戶在圖像頂部繪製一個選擇矩形。矩形也可以移動和調整大小。目前我只使用一個異常處理程序,當超出範圍的異常時,它會提醒用戶。超出範圍的異常只發生在移動繪製的矩形時,我想使其更加簡化,即實際的矩形不能在圖像之外拖動或調整大小。下面是我的作物控制背後的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); 
     } 
    } 

更新:

我能得到它的工作通過計算選擇矩形的大小和位置對其父(帆布)採取與圖像相同的尺寸。以下是我添加到我的代碼中的內容。

 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

如果您在您的Rectangle上使用MouseDragElementBehavior來處理拖動,那麼它有一個方便的ConstrainToParentBounds屬性,該屬性可以設置您的權利。 – 2015-04-06 14:37:32

+0

@克里斯W.感謝您的提示。我曾嘗試添加行爲到我的矩形,並將約束設置爲父爲真,它確實工作,但部分。矩形本身不會在視覺上超出界限,但如果拖動到圖像的邊緣並撞到牆上,它仍會拋出界限異常。我猜我需要對我的代碼進行少量清理才能使其工作。 – mcvanta 2015-04-06 23:18:20

回答

0

如果我正確理解問題 - 當選擇rect超出圖像rect時向用戶顯示消息?如果是這樣,爲什麼不檢查:如果新的選擇狀態將超出圖像區域 - 那麼只是不移動選擇並保持舊狀態?我的意思是 - 比較新選擇矩形和圖像矩形的邊界位置並作出決定:移動或不移動你的選擇直到新位置(或改變或不改變它的大小)。

+0

謝謝你的回覆,但是我糾正了我的解釋。繪製的矩形不會超出範圍,但會在移動或調整繪製的矩形的大小時發生。當我裁剪位圖所選部分時,在視圖模型中會發生異常。我明白你的觀點,但我是C#和XAML的新手,所以我不知道如何做到這一點。你在說我應該將我的選擇矩形與圖像矩形的位置進行比較。問題是,我的視圖模型只接收選擇矩形的位置而不是圖像,所以我需要在我的用戶控件中進行。 – mcvanta 2015-04-06 11:39:35

+0

@mcvanta,你能否給我們提供全功能的例子,它會在正確的地方拋出我們的界限異常(和步驟 - 重現異常)。因爲現在的代碼對我來說不會產生任何例外,而你對我的問題還不夠清楚。 添加的Code方法在任何地方都沒有調用,但我想,檢查圖像大小和糾正rcFrom大小以避免異常 - 也不能解決您的問題。因此,全功能的例子將澄清 - 實際上哪裏是麻煩以及你想實現什麼。 – dzaraev 2015-04-07 05:33:34