Jelajahi Sumber

ComPDFKit.Tool(Win) - 表单多选效果(能Ctrl多选,框选多选)

liyuxuan 2 bulan lalu
induk
melakukan
207dd493e8

+ 756 - 0
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.AnnotSelector.cs

@@ -0,0 +1,756 @@
+using ComPDFKit.Import;
+using ComPDFKit.Tool.DrawTool;
+using ComPDFKitViewer;
+using ComPDFKitViewer.BaseObject;
+using ComPDFKitViewer.Helper;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool
+{
+    public partial class CPDFViewerTool
+    {
+        private class AnnotSelectAreaData
+        {
+            public Point HitAreaPos { get; set; }
+            public Point MoveAreaPos { get; set; }
+            public Rect HitAreaBound { get; set; }
+            public int HitPageIndex { get; set; } = -1;
+        }
+        private int AnnotSelectorIndex { get; set; } = -1;
+        private AnnotSelector Selector { get; set; }
+        private AnnotSelectAreaData AreaDrawData { get; set; } = new AnnotSelectAreaData();
+        private AnnotSelectAreaData AreaMoveData { get; set; } = new AnnotSelectAreaData();
+        private bool DrawSelect { get; set; } = true;
+        private bool AllowMultiSelect { get; set; }
+        private bool IsMoved { get; set; } = false;
+        private Point OffsetPos { get; set; }=new Point(0,0);
+        private void AnnotSelectInsert()
+        {
+            Selector = new AnnotSelector();
+            Selector.PDFViewer = PDFViewer;
+            int annotViewindex = PDFViewer.GetMaxViewIndex();
+            PDFViewer.InsertView(annotViewindex, Selector);
+            AnnotSelectorIndex = Selector.GetResTag();
+        }
+
+        private void AnnotSelectUpdate()
+        {
+            if (!AllowMultiSelect)
+            {
+                return;
+            }
+            if(Selector==null || Selector.SelectAnnots==null)
+            {
+                return;
+            }
+            if (PDFViewer == null || PDFViewer.CurrentRenderFrame == null)
+            {
+                Selector?.ClearItem();
+                Selector?.CleanDraw();
+                return;
+            }
+            List<AnnotData> annotDatas = PDFViewer.CurrentRenderFrame.AnnotDataList;
+            if (annotDatas == null || annotDatas.Count == 0)
+            {
+                return;
+            }
+            foreach (AnnotData checkItem in Selector.SelectAnnots.ToList())
+            {
+                AnnotData saveItem = annotDatas.Where(x => x.PageIndex == checkItem.PageIndex && x.AnnotIndex == checkItem.AnnotIndex).FirstOrDefault();
+                if (saveItem != null)
+                {
+                    Selector.RemoveItem(checkItem);
+                    Selector.AddItem(saveItem);
+                }
+            }
+        }
+
+        private void AnnotSelectDraw()
+        {
+            if (!AllowMultiSelect)
+            {
+                return;
+            }
+            if (DrawSelect==false)
+            {
+                return;
+            }
+            if (Selector?.GetSelectCount() > 1)
+            {
+                Selector?.Draw();
+                return;
+            }
+            Selector?.CleanDraw();
+        }
+
+        private void AnnotSelectAddItem(AnnotData annotData,bool clean=true)
+        {
+            if (!AllowMultiSelect || annotData==null)
+            {
+                return;
+            }
+
+            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+            {
+                if (Selector?.HasItem(annotData) == true)
+                {
+                    if(Selector?.GetSelectCount()!=1)
+                    {
+                        Selector?.RemoveItem(annotData);
+                    }
+                }
+                else
+                {
+                    Selector?.AddItem(annotData);
+                }
+                return;
+            }
+
+            if(clean)
+            {
+                Selector?.ClearItem();
+                Selector?.AddItem(annotData);
+            }
+        }
+
+        private void AnnotSelectAreaHit()
+        {
+            if (!AllowMultiSelect)
+            {
+                return;
+            }
+            AreaDrawData.HitPageIndex = -1;
+            if (PDFViewer == null || Selector==null)
+            {
+                return;
+            }
+            AreaDrawData.HitAreaPos = Mouse.GetPosition(PDFViewer);
+            AreaDrawData.MoveAreaPos = AreaDrawData.HitAreaPos;
+            PDFViewer.GetPointPageInfo(AreaDrawData.HitAreaPos,out int pageIndex,out Rect paintRect, out Rect pageBound);
+            if (pageIndex >= 0)
+            {
+                AreaDrawData.HitPageIndex = pageIndex;
+                AreaDrawData.HitAreaBound =pageBound;
+                PDFViewer.CanHorizontallyScroll = false;
+                PDFViewer.CanVerticallyScroll = false;
+                PDFViewer.EnableZoom(false);
+                DrawSelect = false;
+            }
+        }
+
+        private void AnnotSelectMoveHit()
+        {
+            if (!AllowMultiSelect)
+            {
+                return;
+            }
+            AreaMoveData.HitPageIndex = -1;
+            if (PDFViewer == null || Selector == null)
+            {
+                return;
+            }
+            AreaMoveData.HitAreaPos = Mouse.GetPosition(PDFViewer);
+            AreaMoveData.MoveAreaPos = AreaMoveData.HitAreaPos;
+            PDFViewer.GetPointPageInfo(AreaMoveData.HitAreaPos, out int pageIndex, out Rect paintRect, out Rect pageBound);
+            if (pageIndex >= 0)
+            {
+                AreaMoveData.HitPageIndex = pageIndex;
+                AreaMoveData.HitAreaBound = pageBound;
+            }
+        }
+
+        private void AnnotSelectAreaDraw()
+        {
+            if (!AllowMultiSelect)
+            {
+                return;
+            }
+            if (AreaDrawData.HitPageIndex <0 || PDFViewer==null || Selector==null)
+            {
+                return;
+            }
+            AreaDrawData.MoveAreaPos = Mouse.GetPosition(PDFViewer);
+            Rect moveRect = new Rect(AreaDrawData.HitAreaPos, AreaDrawData.MoveAreaPos);
+            moveRect.Intersect(AreaDrawData.HitAreaBound);
+            DrawingContext drawDC = Selector.RenderOpen();
+            drawDC.DrawRectangle(Selector.DrawBrush, Selector.DrawPen, moveRect);
+            drawDC.Close();
+        }
+
+        private void AnnotSelectAreaSelect()
+        {
+            if (!AllowMultiSelect)
+            {
+                return;
+            }
+
+            if (AreaDrawData.HitPageIndex < 0 || PDFViewer == null || Selector == null)
+            {
+                return;
+            }
+
+            PDFViewer.CanHorizontallyScroll = true;
+            PDFViewer.CanVerticallyScroll = true;
+            PDFViewer.EnableZoom(true);
+            Selector.ClearItem();
+            Selector.CleanDraw();
+            AreaDrawData.HitPageIndex = -1;
+            DrawSelect=true;
+
+            if (PDFViewer.CurrentRenderFrame != null && PDFViewer.CurrentRenderFrame.AnnotDataList != null)
+            {
+                Rect areaRect = new Rect(AreaDrawData.HitAreaPos, AreaDrawData.MoveAreaPos);
+                areaRect.Intersect(AreaDrawData.HitAreaBound);
+                foreach (AnnotData checkItem in PDFViewer.CurrentRenderFrame.AnnotDataList)
+                {
+                    if (areaRect.IntersectsWith(checkItem.PaintRect))
+                    {
+                        Selector.AddItem(checkItem);
+                    }
+                }
+
+                Selector.Draw();
+            }
+        }
+
+        private BaseAnnot AnnotSelectGetAnnot()
+        {
+            if (PDFViewer != null && Selector != null && Selector.SelectAnnots!=null && Selector.SelectAnnots.Count==1)
+            {
+                AnnotData annotData = Selector.SelectAnnots[0];
+                BaseLayer checkLayer = PDFViewer.GetViewForTag(PDFViewer.GetAnnotViewTag());
+                if (checkLayer != null && checkLayer is AnnotLayer)
+                {
+                    AnnotLayer annotLayer= (AnnotLayer)checkLayer;
+                   return  annotLayer.GetAnnotForIndex(annotData.PageIndex,annotData.AnnotIndex);
+                }
+            }
+           
+            return null;
+        }
+
+        private int AnnotSelectGetCount()
+        {
+            if (Selector != null)
+            {
+                return Selector.GetSelectCount();
+            }
+            return 0;
+        }
+
+        private void AnnotSelectAreaHover()
+        {
+            if (!AllowMultiSelect)
+            {
+                return;
+            }
+
+            if(PDFViewer==null ||  Selector==null || Selector.GetSelectCount()<2)
+            {
+                return;
+            }
+
+            Point movePos = Mouse.GetPosition(PDFViewer);
+            PointControlType hitPoint= Selector.GetHitControlIndex(movePos);
+            Cursor oldCursor = this.Cursor;
+            Cursor newCursor = this.Cursor;
+           
+            switch (hitPoint)
+            {
+                case PointControlType.LeftTop:
+                case PointControlType.RightBottom:
+                    newCursor= Cursors.SizeNWSE;
+                    break;
+                case PointControlType.LeftMiddle:
+                case PointControlType.RightMiddle:
+                    newCursor= Cursors.SizeWE;
+                    break;
+                case PointControlType.LeftBottom:
+                case PointControlType.RightTop:
+                    newCursor= Cursors.SizeNESW;
+                    break;
+                case PointControlType.MiddlBottom:
+                case PointControlType.MiddleTop:
+                    newCursor= Cursors.SizeNS;
+                    break;
+                case PointControlType.Body:
+                    newCursor= Cursors.SizeAll;
+                    break;
+                default:
+                    newCursor = Cursors.Arrow;
+                    break;
+            }
+
+            if (oldCursor != newCursor)
+            {
+                PDFViewer.Cursor = newCursor;
+                this.Cursor = newCursor;
+            }
+        }
+
+        private bool AnnotSelectAreaHitTest()
+        {
+            if (!AllowMultiSelect)
+            {
+                return false;
+            }
+
+            if (PDFViewer == null || Selector == null || Selector.GetSelectCount() < 2)
+            {
+                return false;
+            }
+
+            Point movePos = Mouse.GetPosition(PDFViewer);
+            HitTestResult hitResult = VisualTreeHelper.HitTest(PDFViewer, movePos);
+            if (hitResult != null && hitResult.VisualHit is AnnotSelector)
+            {
+                return true;
+            }
+            return false;
+        }
+
+        private bool AnnotSelectMoveDraw()
+        {
+            if (!AllowMultiSelect)
+            {
+                return false;
+            }
+           
+            if (AreaMoveData.HitPageIndex<0 || PDFViewer == null || Selector == null || Selector.GetSelectCount() < 2)
+            {
+                return false;
+            }
+            if (Mouse.LeftButton != MouseButtonState.Pressed)
+            {
+                return false;
+            }
+
+            Point movePos = Mouse.GetPosition(PDFViewer);
+            Point moveOffset = new Point(movePos.X - AreaMoveData.HitAreaPos.X, movePos.Y - AreaMoveData.HitAreaPos.Y);
+            PointControlType hitPointType = Selector.GetHitControlIndex(AreaMoveData.HitAreaPos);
+
+            switch(hitPointType)
+            {
+                default:
+                case PointControlType.None:
+                    return false;
+                case PointControlType.Body:
+                    AnnotSelectMove();
+                    return true;
+                case PointControlType.LeftTop:
+                case PointControlType.LeftMiddle:
+                case PointControlType.LeftBottom:
+                case PointControlType.MiddlBottom:
+                case PointControlType.RightBottom:
+                case PointControlType.RightMiddle:
+                case PointControlType.RightTop:
+                case PointControlType.MiddleTop:
+                    if(CanResize(hitPointType, moveOffset, Selector.GetOutBoundRect(),AreaMoveData.HitAreaBound))
+                    {
+                        AnnotSelectResize(hitPointType, moveOffset, Selector.GetOutBoundRect(), AreaMoveData.HitAreaBound);
+                    }
+                    return true;
+            }
+        }
+
+        private void AnnotSelectMove()
+        {
+            Point movePos = Mouse.GetPosition(PDFViewer);
+            Vector offset = movePos - AreaMoveData.HitAreaPos;
+
+            Rect boundRect = Selector.GetOutBoundRect();
+            Point boundCenter = new Point(boundRect.Left + boundRect.Width / 2, boundRect.Top + boundRect.Height / 2);
+            Point moveCenter = new Point(boundCenter.X + offset.X, boundCenter.Y + offset.Y);
+
+            moveCenter.X = Math.Max(AreaMoveData.HitAreaBound.Left + boundRect.Width / 2, moveCenter.X);
+            moveCenter.X = Math.Min(AreaMoveData.HitAreaBound.Right - boundRect.Width / 2, moveCenter.X);
+            moveCenter.Y = Math.Max(AreaMoveData.HitAreaBound.Top + boundRect.Height / 2, moveCenter.Y);
+            moveCenter.Y = Math.Min(AreaMoveData.HitAreaBound.Bottom - boundRect.Height / 2, moveCenter.Y);
+
+            Rect moveRect = new Rect(moveCenter.X - boundRect.Width / 2, moveCenter.Y - boundRect.Height / 2, boundRect.Width, boundRect.Height);
+            OffsetPos = new Point(moveCenter.X - boundCenter.X, moveCenter.Y - boundCenter.Y);
+
+            DrawingContext drawDC = Selector.RenderOpen();
+            Selector.DrawOutBound(drawDC);
+            Selector.DrawIndividual(drawDC);
+            Selector.DrawControlPoint(drawDC);
+            drawDC.DrawRectangle(Selector.DrawBrush, Selector.DrawPen, moveRect);
+            foreach (AnnotData checkItem in Selector.SelectAnnots)
+            {
+                Rect childRect=new Rect(checkItem.PaintRect.X+OffsetPos.X,checkItem.PaintRect.Y+OffsetPos.Y,checkItem.PaintRect.Width,checkItem.PaintRect.Height);
+                drawDC.DrawRectangle(Selector.DrawBrush, Selector.DrawPen, childRect);
+            }
+            drawDC.Close();
+        }
+
+        private void AnnotSelectResize(PointControlType controlType, Point moveOffset, Rect maxRect, Rect boundRect)
+        {
+            if (maxRect == Rect.Empty || maxRect.Width == 0 || maxRect.Height == 0)
+            {
+                return;
+            }
+
+            double ratioX = maxRect.Width > 0 ? maxRect.Height / maxRect.Width : 1;
+            double ratioY = maxRect.Height > 0 ? maxRect.Width / maxRect.Height : 1;
+            Point offsetPos = moveOffset;
+
+            switch (controlType)
+            {
+                case PointControlType.LeftTop:
+                case PointControlType.RightBottom:
+                    offsetPos = new Point(moveOffset.X, Math.Abs(moveOffset.X) * ratioX * (moveOffset.X < 0 ? -1 : 1));
+                    break;
+                case PointControlType.LeftBottom:
+                case PointControlType.RightTop:
+                    offsetPos = new Point(moveOffset.X, Math.Abs(moveOffset.X) * ratioX * (moveOffset.X < 0 ? 1 : -1));
+                    break;
+                case PointControlType.LeftMiddle:
+                    offsetPos = new Point(moveOffset.X, Math.Abs(moveOffset.X) * ratioX * (moveOffset.X < 0 ? 1 : -1));
+                    break;
+                case PointControlType.RightMiddle:
+                    offsetPos = new Point(moveOffset.X, Math.Abs(moveOffset.X) * ratioX * (moveOffset.X < 0 ? -1 : 1));
+                    break;
+                case PointControlType.MiddlBottom:
+                    offsetPos = new Point(Math.Abs(moveOffset.Y) * ratioY * (moveOffset.Y < 0 ? 1 : -1), moveOffset.Y);
+                    break;
+                case PointControlType.MiddleTop:
+                    offsetPos = new Point(Math.Abs(moveOffset.Y) * ratioY * (moveOffset.Y < 0 ? -1 : 1), moveOffset.Y);
+                    break;
+            }
+            OffsetPos = offsetPos;
+            double left = maxRect.Left;
+            double top = maxRect.Top;
+            double right = maxRect.Right;
+            double bottom = maxRect.Bottom;
+          
+            switch (controlType)
+            {
+                case PointControlType.LeftTop://左上
+                    left += offsetPos.X;
+                    top += offsetPos.Y;
+                    break;
+                case PointControlType.LeftMiddle://左中
+                    left += offsetPos.X;
+                    top -= offsetPos.Y / 2;
+                    bottom += offsetPos.Y / 2;
+                    break;
+                case PointControlType.LeftBottom://左下
+                    left += offsetPos.X;
+                    bottom += offsetPos.Y;
+                    break;
+                case PointControlType.MiddlBottom://中下
+                    bottom += offsetPos.Y;
+                    left += offsetPos.X / 2;
+                    right -= offsetPos.X / 2;
+                    break;
+                case PointControlType.RightBottom://右下
+                    right += offsetPos.X;
+                    bottom += offsetPos.Y;
+                    break;
+                case PointControlType.RightMiddle://右中
+                    right += offsetPos.X;
+                    top -= offsetPos.Y / 2;
+                    bottom += offsetPos.Y / 2;
+                    break;
+                case PointControlType.RightTop://右上
+                    right += offsetPos.X;
+                    top += offsetPos.Y;
+                    break;
+                case PointControlType.MiddleTop://中上
+                    top += offsetPos.Y;
+                    left += offsetPos.X / 2;
+                    right -= offsetPos.X / 2;
+                    break;
+                default:
+                    break;
+            }
+
+            Rect newRect = new Rect(new Point(left, top), new Point(right, bottom));
+
+            DrawingContext drawDC = Selector.RenderOpen();
+            Selector.DrawOutBound(drawDC);
+            Selector.DrawIndividual(drawDC);
+            Selector.DrawControlPoint(drawDC);
+            drawDC.DrawRectangle(Selector.DrawBrush, Selector.DrawPen, newRect);
+
+            foreach (AnnotData checkItem in Selector.SelectAnnots)
+            {
+                double distanceLeft = checkItem.PaintRect.Left - maxRect.Left;
+                double newLeft = newRect.Width * distanceLeft / maxRect.Width + newRect.Left;
+                double distanceRight = maxRect.Right - checkItem.PaintRect.Right;
+                double newRight = newRect.Right - newRect.Width / maxRect.Width * distanceRight;
+                double distanceTop = checkItem.PaintRect.Top - maxRect.Top;
+                double newTop = newRect.Height * distanceTop / maxRect.Height + newRect.Top;
+                double distanceBottom = maxRect.Bottom - checkItem.PaintRect.Bottom;
+                double newBottom = newRect.Bottom - newRect.Height / maxRect.Height * distanceBottom;
+
+                Rect newClient = new Rect(newLeft, newTop, newRight - newLeft, newBottom - newTop);
+
+                if (newClient != Rect.Empty && newClient.Width > 0 && newClient.Height > 0)
+                {
+                    drawDC.DrawRectangle(Selector.DrawBrush, Selector.DrawPen, newClient);
+                }
+            }
+           
+            drawDC.Close();
+        }
+
+        private bool CanResize(PointControlType controlType, Point moveOffset, Rect maxRect, Rect boundRect)
+        {
+            if (maxRect == Rect.Empty || maxRect.Width == 0 || maxRect.Height == 0)
+            {
+                return false;
+            }
+
+            double ratioX = maxRect.Width > 0 ? maxRect.Height / maxRect.Width : 1;
+            double ratioY = maxRect.Height > 0 ? maxRect.Width / maxRect.Height : 1;
+            Point offsetPos = moveOffset;
+
+            switch (controlType)
+            {
+                case PointControlType.LeftTop:
+                case PointControlType.RightBottom:
+                    offsetPos = new Point(moveOffset.X, Math.Abs(moveOffset.X) * ratioX * (moveOffset.X < 0 ? -1 : 1));
+                    break;
+                case PointControlType.LeftBottom:
+                case PointControlType.RightTop:
+                    offsetPos = new Point(moveOffset.X, Math.Abs(moveOffset.X) * ratioX * (moveOffset.X < 0 ? 1 : -1));
+                    break;
+                case PointControlType.LeftMiddle:
+                    offsetPos = new Point(moveOffset.X, Math.Abs(moveOffset.X) * ratioX * (moveOffset.X < 0 ? 1 : -1));
+                    break;
+                case PointControlType.RightMiddle:
+                    offsetPos = new Point(moveOffset.X, Math.Abs(moveOffset.X) * ratioX * (moveOffset.X < 0 ? -1 : 1));
+                    break;
+                case PointControlType.MiddlBottom:
+                    offsetPos = new Point(Math.Abs(moveOffset.Y) * ratioY * (moveOffset.Y < 0 ? 1 : -1), moveOffset.Y);
+                    break;
+                case PointControlType.MiddleTop:
+                    offsetPos = new Point(Math.Abs(moveOffset.Y) * ratioY * (moveOffset.Y < 0 ? -1 : 1), moveOffset.Y);
+                    break;
+            }
+
+            double left = maxRect.Left;
+            double top = maxRect.Top;
+            double right = maxRect.Right;
+            double bottom = maxRect.Bottom;
+
+            switch (controlType)
+            {
+                case PointControlType.LeftTop://左上
+                    left += offsetPos.X;
+                    top += offsetPos.Y;
+                    break;
+                case PointControlType.LeftMiddle://左中
+                    left += offsetPos.X;
+                    top -= offsetPos.Y / 2;
+                    bottom += offsetPos.Y / 2;
+                    break;
+                case PointControlType.LeftBottom://左下
+                    left += offsetPos.X;
+                    bottom += offsetPos.Y;
+                    break;
+                case PointControlType.MiddlBottom://中下
+                    bottom += offsetPos.Y;
+                    left += offsetPos.X / 2;
+                    right -= offsetPos.X / 2;
+                    break;
+                case PointControlType.RightBottom://右下
+                    right += offsetPos.X;
+                    bottom += offsetPos.Y;
+                    break;
+                case PointControlType.RightMiddle://右中
+                    right += offsetPos.X;
+                    top -= offsetPos.Y / 2;
+                    bottom += offsetPos.Y / 2;
+                    break;
+                case PointControlType.RightTop://右上
+                    right += offsetPos.X;
+                    top += offsetPos.Y;
+                    break;
+                case PointControlType.MiddleTop://中上
+                    top += offsetPos.Y;
+                    left += offsetPos.X / 2;
+                    right -= offsetPos.X / 2;
+                    break;
+                default:
+                    break;
+            }
+
+            Rect newRect = new Rect(new Point(left, top), new Point(right, bottom));
+
+            if (newRect.Left < boundRect.Left || newRect.Top < boundRect.Top || newRect.Bottom > boundRect.Bottom || newRect.Right > boundRect.Right)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        private void AnnotSelectSave()
+        {
+            if (!AllowMultiSelect)
+            {
+                return;
+            }
+
+            if (AreaMoveData.HitPageIndex < 0 || PDFViewer == null || Selector == null || Selector.GetSelectCount() < 2)
+            {
+                return;
+            }
+            if (!IsMoved)
+            {
+                return;
+            }
+            PointControlType hitPointType = Selector.GetHitControlIndex(AreaMoveData.HitAreaPos);
+
+            switch (hitPointType)
+            {
+                default:
+                case PointControlType.None:
+                    return;
+                case PointControlType.Body:
+                    AnnotSelectMoveSave();
+                    PDFViewer?.UpdateRenderFrame();
+                    return;
+                case PointControlType.LeftTop:
+                case PointControlType.LeftMiddle:
+                case PointControlType.LeftBottom:
+                case PointControlType.MiddlBottom:
+                case PointControlType.RightBottom:
+                case PointControlType.RightMiddle:
+                case PointControlType.RightTop:
+                case PointControlType.MiddleTop:
+                    AnnotSelectResizeSave(hitPointType, OffsetPos,Selector.GetOutBoundRect(), AreaMoveData.HitAreaBound);
+                    PDFViewer?.UpdateRenderFrame();
+                    return;
+            }
+        }
+
+        private void AnnotSelectMoveSave()
+        {
+            foreach (AnnotData checkItem in Selector.SelectAnnots)
+            {
+                if(checkItem.Annot==null || checkItem.Annot.IsValid()==false)
+                {
+                    continue;
+                }
+                Rect childRect = new Rect(checkItem.PaintRect.X + OffsetPos.X, checkItem.PaintRect.Y + OffsetPos.Y, checkItem.PaintRect.Width, checkItem.PaintRect.Height);
+                Rect saveRect = new Rect(
+                    childRect.Left - checkItem.PaintOffset.Left + checkItem.CropLeft * checkItem.CurrentZoom,
+                    childRect.Top - checkItem.PaintOffset.Top + checkItem.CropTop * checkItem.CurrentZoom,
+                    childRect.Width,
+                    childRect.Height);
+
+                Rect noZoomRect=new Rect(
+                    saveRect.Left/checkItem.CurrentZoom,
+                    saveRect.Top/checkItem.CurrentZoom,
+                    saveRect.Width/checkItem.CurrentZoom, 
+                    saveRect.Height/checkItem.CurrentZoom);
+                Rect rawRect= DpiHelper.StandardRectToPDFRect(noZoomRect);
+
+                checkItem.Annot.SetRect(new CRect((float)rawRect.Left, (float)rawRect.Bottom, (float)rawRect.Right, (float)rawRect.Top));
+            }
+        }
+
+        private void AnnotSelectResizeSave(PointControlType controlType, Point offsetPos, Rect maxRect, Rect boundRect)
+        {
+            double left = maxRect.Left;
+            double top = maxRect.Top;
+            double right = maxRect.Right;
+            double bottom = maxRect.Bottom;
+
+            switch (controlType)
+            {
+                case PointControlType.LeftTop://左上
+                    left += offsetPos.X;
+                    top += offsetPos.Y;
+                    break;
+                case PointControlType.LeftMiddle://左中
+                    left += offsetPos.X;
+                    top -= offsetPos.Y / 2;
+                    bottom += offsetPos.Y / 2;
+                    break;
+                case PointControlType.LeftBottom://左下
+                    left += offsetPos.X;
+                    bottom += offsetPos.Y;
+                    break;
+                case PointControlType.MiddlBottom://中下
+                    bottom += offsetPos.Y;
+                    left += offsetPos.X / 2;
+                    right -= offsetPos.X / 2;
+                    break;
+                case PointControlType.RightBottom://右下
+                    right += offsetPos.X;
+                    bottom += offsetPos.Y;
+                    break;
+                case PointControlType.RightMiddle://右中
+                    right += offsetPos.X;
+                    top -= offsetPos.Y / 2;
+                    bottom += offsetPos.Y / 2;
+                    break;
+                case PointControlType.RightTop://右上
+                    right += offsetPos.X;
+                    top += offsetPos.Y;
+                    break;
+                case PointControlType.MiddleTop://中上
+                    top += offsetPos.Y;
+                    left += offsetPos.X / 2;
+                    right -= offsetPos.X / 2;
+                    break;
+                default:
+                    break;
+            }
+
+            Rect newRect = new Rect(new Point(left, top), new Point(right, bottom));
+
+            foreach (AnnotData checkItem in Selector.SelectAnnots)
+            {
+                if (checkItem.Annot == null || checkItem.Annot.IsValid() == false)
+                {
+                    continue;
+                }
+
+                double distanceLeft = checkItem.PaintRect.Left - maxRect.Left;
+                double newLeft = newRect.Width * distanceLeft / maxRect.Width + newRect.Left;
+                double distanceRight = maxRect.Right - checkItem.PaintRect.Right;
+                double newRight = newRect.Right - newRect.Width / maxRect.Width * distanceRight;
+                double distanceTop = checkItem.PaintRect.Top - maxRect.Top;
+                double newTop = newRect.Height * distanceTop / maxRect.Height + newRect.Top;
+                double distanceBottom = maxRect.Bottom - checkItem.PaintRect.Bottom;
+                double newBottom = newRect.Bottom - newRect.Height / maxRect.Height * distanceBottom;
+
+                Rect newClient = new Rect(newLeft, newTop, newRight - newLeft, newBottom - newTop);
+
+                if (newClient != Rect.Empty && newClient.Width > 0 && newClient.Height > 0)
+                {
+                    Rect saveRect = new Rect(
+                      newClient.Left - checkItem.PaintOffset.Left + checkItem.CropLeft * checkItem.CurrentZoom,
+                      newClient.Top - checkItem.PaintOffset.Top + checkItem.CropTop * checkItem.CurrentZoom,
+                      newClient.Width,
+                      newClient.Height);
+
+                    Rect noZoomRect = new Rect(
+                        saveRect.Left / checkItem.CurrentZoom,
+                        saveRect.Top / checkItem.CurrentZoom,
+                        saveRect.Width / checkItem.CurrentZoom,
+                        saveRect.Height / checkItem.CurrentZoom);
+                    Rect rawRect = DpiHelper.StandardRectToPDFRect(noZoomRect);
+
+                    checkItem.Annot.SetRect(new CRect((float)rawRect.Left, (float)rawRect.Bottom, (float)rawRect.Right, (float)rawRect.Top));
+                }
+            }
+        }
+    }
+}

+ 6 - 1
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.SelectedRect.cs

@@ -146,11 +146,16 @@ namespace ComPDFKit.Tool
             {
                 return ;
             }
+            SelectAnnot(cacheHitTestAnnot.GetAnnotData());
+        }
+
+        private void SelectAnnot(AnnotData selectData)
+        {
             BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
             SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
             if (selectedRect != null)
             {
-                selectedRect.SetAnnotData(cacheHitTestAnnot.GetAnnotData());
+                selectedRect.SetAnnotData(selectData);
             }
         }
 

+ 76 - 8
Demo/Examples/ComPDFKit.Tool/CPDFViewerTool.xaml.cs

@@ -4,8 +4,10 @@ using ComPDFKit.PDFDocument;
 using ComPDFKit.PDFDocument.Action;
 using ComPDFKit.PDFPage;
 using ComPDFKit.Tool.DrawTool;
+using ComPDFKit.Tool.Help;
 using ComPDFKit.Tool.SettingParam;
 using ComPDFKit.Viewer.Helper;
+using ComPDFKit.Viewer.Layer;
 using ComPDFKitViewer;
 using ComPDFKitViewer.Annot;
 using ComPDFKitViewer.BaseObject;
@@ -92,6 +94,8 @@ namespace ComPDFKit.Tool
             InsertFrameSelectToolView();
             InsertTextEditView();
             InsertPageSelectedRectView();
+
+            AnnotSelectInsert();
         }
 
         private void Current_Exit(object sender, ExitEventArgs e)
@@ -470,14 +474,29 @@ namespace ComPDFKit.Tool
             // Form creation mode
             else if (currentModel == ToolType.WidgetEdit)
             {
-                if (AnnotWidgetHitTest())
+                if (!AnnotSelectAreaHitTest())
                 {
-                    cacheHitTestAnnot = PDFViewer?.AnnotHitTest() as BaseWidget;
-                    SelectedAnnot();
-                    mouseEventObject.hitTestType = MouseHitTestType.Annot;
+                    if (AnnotWidgetHitTest())
+                    {
+                        BaseWidget hitWidget = PDFViewer?.AnnotHitTest() as BaseWidget;
+                        cacheHitTestAnnot = null;
+                        if (AnnotSelectGetCount() == 0 && hitWidget != null)
+                        {
+                            AnnotSelectAddItem(hitWidget.GetAnnotData());
+                            cacheHitTestAnnot=hitWidget;
+                            SelectedAnnot();
+                        }
 
-                    mouseEventObject.annotType = cacheMoveWidget.GetAnnotData().AnnotType;
+                        mouseEventObject.hitTestType = MouseHitTestType.Annot;
+                        mouseEventObject.annotType = cacheMoveWidget.GetAnnotData().AnnotType;
+                    }
+                    else
+                    {
+                        AnnotSelectAreaHit();
+                    }
                 }
+
+                AnnotSelectMoveHit();
             }
             // Content editing mode
             else if (currentModel == ToolType.ContentEdit)
@@ -580,7 +599,39 @@ namespace ComPDFKit.Tool
             {
                 return;
             }
+            if (currentModel == ToolType.WidgetEdit)
+            {
+                AnnotSelectAreaSelect();
+                if (AnnotWidgetHitTest())
+                {
+                    BaseWidget checkItem = PDFViewer?.AnnotHitTest() as BaseWidget;
+                    AnnotSelectAddItem(checkItem?.GetAnnotData(), !AnnotSelectAreaHitTest());
+                }
 
+                if (AnnotSelectGetCount() == 1)
+                {
+                    cacheHitTestAnnot = AnnotSelectGetAnnot();
+                    if (cacheHitTestAnnot != null)
+                    {
+                        BaseLayer baseLayer = PDFViewer.GetViewForTag(selectedRectViewTag);
+                        SelectedRect selectedRect = CommonHelper.FindVisualChild<SelectedRect>(baseLayer as CustomizeLayer);
+                        if (selectedRect != null)
+                        {
+                            if (IsMoved)
+                            {
+                                selectedRect.UpdateAnnotData(cacheHitTestAnnot.GetAnnotData());
+                            }
+                            else
+                            {
+                                selectedRect.SetAnnotData(cacheHitTestAnnot.GetAnnotData());
+                            }
+                            selectedRect.Draw();
+                        }
+                        isDrawSelectRect = true;
+                    }
+                }
+            }
+          
             MouseEventObject mouseEventObject = new MouseEventObject
             {
                 mouseButtonEventArgs = e,
@@ -600,16 +651,22 @@ namespace ComPDFKit.Tool
                 {
                     mouseEventObject.annotType = C_ANNOTATION_TYPE.C_ANNOTATION_NONE;
                 }
-                MouseLeftButtonUpHandler?.Invoke(this, mouseEventObject);
-                ReleaseMouseCapture();
-                return;
             }
             MouseLeftButtonUpHandler?.Invoke(this, mouseEventObject);
             ReleaseMouseCapture();
+            if (currentModel == ToolType.WidgetEdit)
+            {
+                AnnotSelectUpdate();
+                AnnotSelectDraw();
+                AnnotSelectSave();
+            }
+            IsMoved = false;
         }
 
         protected override void OnMouseMove(MouseEventArgs e)
         {
+            IsMoved = e.LeftButton == MouseButtonState.Pressed;
+
             if (PDFViewer == null || PDFViewer.CurrentRenderFrame == null)
             {
                 return;
@@ -718,6 +775,15 @@ namespace ComPDFKit.Tool
                 }
             }
 
+            if(currentModel==ToolType.WidgetEdit)
+            {
+                AnnotSelectAreaDraw();
+                if (!AnnotSelectMoveDraw())
+                {
+                    AnnotSelectAreaHover();
+                }
+            }
+           
             MouseMoveHandler?.Invoke(this, mouseEventObject);
             PDFViewer.SetCustomMousePoint(Mouse.GetPosition(this).Y, Mouse.GetPosition(this).X);
             if (oldCursor != newCursor)
@@ -979,6 +1045,8 @@ namespace ComPDFKit.Tool
         private void PDFViewer_DrawChanged(object sender, EventArgs e)
         {
             SizeChangeds();
+            AnnotSelectUpdate();
+            AnnotSelectDraw();
             DrawChanged?.Invoke(this, e);
         }
 

+ 2 - 0
Demo/Examples/ComPDFKit.Tool/ComPDFKit.Tool.csproj

@@ -98,6 +98,7 @@
     <Compile Include="AlignmentsHelp.cs" />
     <Compile Include="CPDFViewerTool.Annot.cs" />
     <Compile Include="CPDFViewerTool.AnnotEdit.cs" />
+    <Compile Include="CPDFViewerTool.AnnotSelector.cs" />
     <Compile Include="CPDFViewerTool.Command.cs" />
     <Compile Include="CPDFViewerTool.CreateWidget.cs" />
     <Compile Include="CPDFViewerTool.CustomizeTool.cs" />
@@ -116,6 +117,7 @@
     </Compile>
     <Compile Include="CustomCommands.cs" />
     <Compile Include="DrawTool\AnnotEdit.cs" />
+    <Compile Include="DrawTool\AnnotSelector.cs" />
     <Compile Include="DrawTool\CaretVisual.cs" />
     <Compile Include="DrawTool\CreateAnnotTool.cs" />
     <Compile Include="DrawTool\CreateCustomizeTool.cs" />

+ 210 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/AnnotSelector.cs

@@ -0,0 +1,210 @@
+using ComPDFKitViewer;
+using ComPDFKitViewer.Layer;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+
+namespace ComPDFKit.Tool.DrawTool
+{
+    internal class AnnotSelector:BaseLayer
+    {
+        public CPDFViewer PDFViewer { get; set; }
+        public List<AnnotData> SelectAnnots { get; set; } = new List<AnnotData>();
+        public Pen DrawPen { get; set; }
+        public Brush DrawBrush { get; set; }
+        private List<Point> ControlPoints { get; set; } = new List<Point>();
+        private double RectPadding = 4;
+        public AnnotSelector()
+        {
+            DrawPen = new Pen(new SolidColorBrush(Color.FromRgb(119, 180, 227)), 2);
+            DrawBrush = new SolidColorBrush(Color.FromArgb(40, 119, 180, 227));
+        }
+        public override void Draw()
+        {
+            DrawingContext drawDC = Open();
+            DrawOutBound(drawDC);
+            DrawIndividual(drawDC);
+            DrawControlPoint(drawDC);
+            Present();
+        }
+
+        public int GetSelectCount()
+        {
+            if(SelectAnnots!=null)
+            {
+                return SelectAnnots.Count;
+            }
+            return 0;
+        }
+        public bool AddItem(AnnotData item)
+        {
+            if (item == null)
+            {
+                return false;
+            }
+            List<int> pageIndexList = SelectAnnots.Select(x => x.PageIndex).Distinct().ToList();
+            if(pageIndexList.Count > 1 )
+            {
+                SelectAnnots.Clear();
+            }
+
+            if( pageIndexList.Count==1 && pageIndexList[0]!=item.PageIndex)
+            {
+                SelectAnnots.Clear();
+            }
+
+            if (RemoveItem(item))
+            {
+                SelectAnnots.Add(item);
+                return true;
+            }
+            return false;
+        }
+        public bool HasItem(AnnotData item)
+        {
+            if (item == null)
+            {
+                return false;
+            }
+
+            if(SelectAnnots!=null && SelectAnnots.Count > 0)
+            {
+                return SelectAnnots.Where(x => x.AnnotIndex == item.AnnotIndex && x.PageIndex == item.PageIndex).Count() > 0;
+            }
+
+            return false;
+        }
+
+        public bool RemoveItem(AnnotData item)
+        {
+            if (item == null || item.PageIndex < 0 || item.AnnotIndex < 0)
+            {
+                return false;
+            }
+
+            List<AnnotData> checkList = SelectAnnots.Where(x => x.AnnotIndex == item.AnnotIndex && x.PageIndex == item.PageIndex).ToList();
+            if (checkList != null && checkList.Count > 0)
+            {
+                foreach (AnnotData removeItem in checkList)
+                {
+                    SelectAnnots.Remove(removeItem);
+                }
+            }
+            return true;
+        }
+
+        public void ClearItem()
+        { 
+            SelectAnnots?.Clear(); 
+        }
+        public Rect GetOutBoundRect()
+        {
+            if (SelectAnnots.Count > 0)
+            {
+                double left = 0;
+                double top = 0;
+                double right = 0;
+                double bottom = 0;
+
+                for (int i=0;i< SelectAnnots.Count;i++)
+                {
+                   AnnotData checkItem= SelectAnnots[i];
+                    if (i == 0) 
+                    {
+                        left=checkItem.PaintRect.Left;
+                        top=checkItem.PaintRect.Top;
+                        right=checkItem.PaintRect.Right;
+                        bottom=checkItem.PaintRect.Bottom;
+                        continue;
+                    }
+
+                    left = Math.Min(left, checkItem.PaintRect.Left);
+                    top = Math.Min(top, checkItem.PaintRect.Top);
+                    right = Math.Max(right, checkItem.PaintRect.Right);
+                    bottom = Math.Max(bottom, checkItem.PaintRect.Bottom);
+                }
+
+                if(right<left ||  bottom<top)
+                {
+                    return Rect.Empty;
+                }
+
+                return new Rect(left, top, right-left, bottom-top);
+            }
+           
+            return Rect.Empty;
+        }
+
+        public void DrawOutBound(DrawingContext drawDC) 
+        {
+            Rect boudRect = GetOutBoundRect();
+            if (boudRect.IsEmpty || boudRect.Width == 0 || boudRect.Height == 0)
+            { 
+                return;
+            }
+
+            drawDC.DrawRectangle(Brushes.Transparent, DrawPen, boudRect);
+        }
+
+        public void DrawIndividual(DrawingContext drawDC)
+        {
+            if(SelectAnnots==null || SelectAnnots.Count==0)
+            {
+                return;
+            }
+
+            foreach(AnnotData checkItem in SelectAnnots)
+            {
+                drawDC.DrawRectangle(null, DrawPen, checkItem.PaintRect);
+            }
+        }
+
+        public void DrawControlPoint(DrawingContext drawDC)
+        {
+            Rect boudRect = GetOutBoundRect();
+            ControlPoints.Clear();
+            double centerX = (boudRect.Left + boudRect.Right) / 2;
+            double centerY = (boudRect.Top + boudRect.Bottom) / 2;
+
+            ControlPoints.Add(new Point(boudRect.Left, boudRect.Top));
+            ControlPoints.Add(new Point(boudRect.Left, centerY));
+            ControlPoints.Add(new Point(boudRect.Left, boudRect.Bottom));
+            ControlPoints.Add(new Point(centerX, boudRect.Bottom));
+            ControlPoints.Add(new Point(boudRect.Right, boudRect.Bottom));
+            ControlPoints.Add(new Point(boudRect.Right, centerY));
+            ControlPoints.Add(new Point(boudRect.Right, boudRect.Top));
+            ControlPoints.Add(new Point(centerX, boudRect.Top));
+
+            foreach (Point point in ControlPoints)
+            {
+                drawDC.DrawRectangle(Brushes.White, DrawPen, new Rect(point.X- RectPadding,point.Y- RectPadding, RectPadding*2, RectPadding*2));
+            }
+        }
+
+        public PointControlType GetHitControlIndex(Point point)
+        {
+            for (int i = 0; i < ControlPoints.Count; i++)
+            {
+                Point checkPoint = ControlPoints[i];
+                Rect checkBound = new Rect(checkPoint.X - RectPadding, checkPoint.Y - RectPadding, RectPadding * 2, RectPadding * 2);
+                if (checkBound.Contains(point))
+                {
+                    return (PointControlType)i;
+                }
+            }
+
+            Rect outBound = GetOutBoundRect();
+            if (outBound.Contains(point))
+            {
+                return PointControlType.Body;
+            }
+
+            return PointControlType.None;
+        }
+    }
+}

+ 7 - 0
Demo/Examples/ComPDFKit.Tool/DrawTool/SelectedRect.cs

@@ -574,6 +574,13 @@ namespace ComPDFKit.Tool.DrawTool
             return maxRect;
         }
 
+        public void UpdateAnnotData(AnnotData annotData)
+        {
+            if(selectedRectData!=null)
+            {
+                selectedRectData.annotData = annotData;
+            }
+        }
         public void SetAnnotData(AnnotData annotData)
         {
             SetIgnorePoints(new List<PointControlType>());