1. 程式人生 > >WPF使用Canvas繪製可變矩形

WPF使用Canvas繪製可變矩形

1、問題以及解決辦法

最近因為專案需要,需要實現一個位置校對的功能,大致的需求如下:有一個圖片,有一些位置資訊,但是位置資訊可能和實際有些偏差,需要做簡單調整,後面會對這張圖片進行切割等,做些處理。(位置資訊連線起來是一個個小矩形。)

解決以上問題的大致思路如下:使用canvas進行繪製,把圖片作為canvas的背景,在canvas上繪製矩形,類似於qq截圖一樣,矩形框可以使用滑鼠拖動調整大小。然後在記下修改後的位置,提供給後面切割圖片使用。目前的關鍵問題就是實現類似qq截圖那樣可以拖動的矩形

2、實現的效果預覽


以上是實現的demo的效果。主要由定位點和連線組成。

3、可變矩形實現

  1. 定位點
    定位點主要用於描述在矩形上的小方框,滑鼠拖動的事件就是由它觸發,它位置的移動會聯動相關線的移動。


定位點主要是定位點的一些基本屬性和基本方法,主要包括繪製方法,移動方法。
其中:AnchorPointType為定位點的型別;

    public enum AnchorPointType
    {
        /// <summary>
        /// 上下
        /// </summary>
        NS,
        /// <summary>
        /// 左右
        /// </summary>
        WE,
        ///
<summary>
/// 右上 /// </summary> NE, /// <summary> /// 左下 /// </summary> SW, /// <summary> /// 右下 /// </summary> NW, /// <summary> /// 左上 /// </summary> SE }

draw()方法用於繪製矩形:

        public Rectangle Draw() 
        {
            double offset = this.Width / 2;
            Rectangle retc = new Rectangle()
            {
                Margin = new Thickness(this.X - offset, this.Y - offset, 0, 0),
                Width = this.Width,
                Height = this.Height,
                Fill = Brushes.LightGoldenrodYellow,
                Stroke = Brushes.Black,
                StrokeThickness = 1,
                DataContext = this.Key
            };
            this.retc = retc;
            return retc;
        }

move()方法使用者改變定位點位置

        public void Move(double x,double y)
        {
            double offset = this.Width / 2;
            this.retc.Margin = new Thickness(x-offset,y-offset,0,0);
            this.X = x;
            this.Y = y;
        }
  1. 可變矩形
    這部分主要實現繪製矩形功能,主要程式碼如下:
        public void Init()
         {
             //按x軸分類
             IEnumerable<IGrouping<double, Point>> pointXs = points.GroupBy(o => o.X);
             //按y周分類
             IEnumerable<IGrouping<double, Point>> pointYs = points.GroupBy(o => o.Y);
             //繪製豎線
             DrawXLine(pointXs);
             //繪製橫線
             DrawYLine(pointYs);
             //設定定位點
             AddAnchorPoints();
             //繪製定位點並且新增事件
             foreach (AnchorPoint anchorPoint in anchorPoints)
             {
                 Rectangle rec=anchorPoint.Draw();
                 rec.MouseLeftButtonDown += new MouseButtonEventHandler(rec_MouseLeftButtonDown);
                 rec.MouseMove += new MouseEventHandler(rec_MouseMove);
                 canvas.Children.Add(rec);
             }
             //canvas新增事件
             canvas.MouseLeftButtonUp += new MouseButtonEventHandler(canvas_MouseLeftButtonUp);
             canvas.MouseMove += new MouseEventHandler(canvas_MouseMove);
             canvas.MouseLeave += new MouseEventHandler(canvas_MouseLeave);
         }
    
    如上程式碼:
    如上程式碼:
    1、按x軸,y軸分類
    2、繪製豎線,橫線
    3、設定定位點
    4、繪製定位點並且新增事件監聽
    5、給canvas新增事件
    給每個定位點新增滑鼠MouseMove和MouseLeftButtonDown事件,給canvas新增MouseMove,MouseLeave,MouseLeftButtonUp事件。
    具體程式碼不在貼上了,如果需要程式碼,可以去下載原始碼
  2. 矩形線聯動
    矩形線聯動,主要是以點帶線,通過判斷線是否和動點相關聯,聯動相關的線。主要程式碼如下:
         private void MoveLines(double x, double y)
         {
             List<Line> moveLines = new List<Line>();
             moveLines = lines.Where(o => o.Y1 == curAnchorPoint.Y
                 || o.Y2 == curAnchorPoint.Y
                 || o.X1 == curAnchorPoint.X
                 || o.X2 == curAnchorPoint.X).ToList();
             foreach (Line line in moveLines)
             {
                 if (line.Y1 == curAnchorPoint.Y)
                 {
                     line.Y1 = y;
                 }
                 if (line.Y2 == curAnchorPoint.Y)
                 {
                     line.Y2 = y;
                 }
                 if (line.X1 == curAnchorPoint.X)
                 {
                     line.X1 = x;
                 }
                 if (line.X2 == curAnchorPoint.X)
                 {
                     line.X2 = x;
                 }
             }
         }
    
  3. 定位點聯動

點的聯動,和線的聯動方法類似,但是點的聯動要比線的聯動複雜,主要在以下三個方面:1、點的移動需要聯動中點的聯動。2、在矩形的四個頂點變動時,需要聯動其他兩個相鄰的頂點,和相鄰兩條線的中點的聯動。3、不同的方向點的聯動,不一樣,會有很多if else。
由於程式碼較長,就不貼上了,需要原始碼可以去獲取原始碼。
線中點聯動:線的中點聯動需要考慮,移動的點是否關聯到計算這個中點的兩個點或者一個點,關聯到中點的這兩個點如果有改變,則這個中點就需要改變。主要程式碼如下:

        private void MoveRefAnchorPoint(double x, double y,AnchorPoint movedAnchorPoint)
        {
            foreach (AnchorPoint anchorPoint in anchorPoints)
            {
                if (anchorPoint.RefPoint.Length == 2)
                {
                    if (anchorPoint.RefPoint[0].X == x && anchorPoint.RefPoint[0].Y == y)
                    {
                        anchorPoint.RefPoint[0].X = movedAnchorPoint.X;
                        anchorPoint.RefPoint[0].Y = movedAnchorPoint.Y;
                    }
                    else if (anchorPoint.RefPoint[1].X == x && anchorPoint.RefPoint[1].Y == y)
                    {
                        anchorPoint.RefPoint[1].X = movedAnchorPoint.X;
                        anchorPoint.RefPoint[1].Y = movedAnchorPoint.Y;
                    }
                    anchorPoint.X = (anchorPoint.RefPoint[0].X + anchorPoint.RefPoint[1].X) / 2;
                    anchorPoint.Y = (anchorPoint.RefPoint[0].Y + anchorPoint.RefPoint[1].Y) / 2;
                    anchorPoint.Move();
                }
            }
        }

以上為單個關聯點改變的情況的程式碼,兩個關聯點都變動的程式碼和這個類似。

4、獲取原始碼