From dac3e12cc046aabeefd828b3f03794fe2bc0ba06 Mon Sep 17 00:00:00 2001
From: wells.liu <wells.liu@broconcentric.com>
Date: 星期四, 09 七月 2020 09:54:26 +0800
Subject: [PATCH] Merge branch 'master' of http://gitblit.broconcentric.com:8088/r/M071

---
 .gitignore                                              |    2 
 src/Bro.UI.Device.Winform/CtrlCameraRunBase.resx        |   15 
 src/Bro.Common.Device/Helper/AspectHelper.cs            |    2 
 src/Bro.M071.Process/M071Process_MotionCard.cs          |    2 
 src/Bro.M071.Process/UI/M071_MainForm.Designer.cs       |    1 
 src/Bro.UI.Config/MainFrm.cs                            |    2 
 src/Bro.M071.Process/M071Process.cs                     |   24 
 src/Bro.M071.Process/UI/M071_ShortcutFrm.cs             |   76 +++
 src/Bro.UI.Model.Winform/UI/Canvas.cs                   |   22 
 src/Bro.Device.Gocator/GocatorDriver.cs                 |   82 +++
 src/Bro.M071.Process/UI/M071_ShortcutFrm.resx           |  120 +++++
 src/Bro.M071.Process/UI/KeyIndicator.cs                 |   72 ++
 src/Bro.UI.Device.Winform/CtrlCameraRunBase.Designer.cs |   68 +++
 src/Bro.UI.Model.Winform/UI/DockContent/MenuFrmBase.cs  |    8 
 src/Bro.M071.Process/UI/M071_MainForm.cs                |   29 +
 src/Bro.UI.Model.Winform/UI/CanvasImage.cs              |  211 ++++-----
 src/Bro.Device.Gocator/Bro.Device.Gocator.csproj        |    1 
 src/Bro.UI.Config/AdvancedPwdFrm.designer.cs            |   23 
 src/Bro.UI.Device.Winform/CtrlCameraRunBase.cs          |   28 +
 src/Bro.Common.Device/DeviceBase/CameraBase.cs          |    8 
 src/Bro.Common.Model/Helper/EnumHelper.cs               |   34 +
 src/Bro.Process/Bro.Process.csproj                      |    5 
 src/Bro.UI.Config/AdvancedPwdFrm.cs                     |    6 
 src/Bro.M071.Process/UI/M071_ShortcutFrm.Designer.cs    |   93 ++++
 src/Bro.UI.Model.Winform/ElementBase.cs                 |  312 +++++++++++++-
 src/Bro.M071.Process/Bro.M071.Process.csproj            |    3 
 src/Bro.M071.Process/M071Models.cs                      |    6 
 27 files changed, 1,037 insertions(+), 218 deletions(-)

diff --git a/.gitignore b/.gitignore
index 345c9f4..7cc7f16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -188,3 +188,5 @@
 
 # Microsoft Fakes
 FakesAssemblies/
+/src/Images/20200706/Test/Origin
+/src/Images
diff --git a/src/Bro.Common.Device/DeviceBase/CameraBase.cs b/src/Bro.Common.Device/DeviceBase/CameraBase.cs
index a7f721c..440c7d0 100644
--- a/src/Bro.Common.Device/DeviceBase/CameraBase.cs
+++ b/src/Bro.Common.Device/DeviceBase/CameraBase.cs
@@ -152,7 +152,7 @@
 
             set.IsOriginSaved = !set.ImageSaveOption.IsSaveOriginImage;
             set.IsFitSaved = !set.ImageSaveOption.IsSaveFitImage;
-            set.IsAddtionalSaved = !string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
+            set.IsAddtionalSaved = string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
             set.OnImageSetTimeout += ImageSet_OnImageSetTimeout;
 
             _imageSetList.Add(set);
@@ -181,7 +181,7 @@
         {
             set.IsOriginSaved = !set.ImageSaveOption.IsSaveOriginImage;
             set.IsFitSaved = !set.ImageSaveOption.IsSaveFitImage;
-            set.IsAddtionalSaved = !string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
+            set.IsAddtionalSaved = string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
             set.OnImageSetTimeout += ImageSet_OnImageSetTimeout;
             _imageSetList.Add(set);
         }
@@ -539,7 +539,7 @@
             });
         }
 
-        protected async void Generate16GrayImageByPointer(int width, int height, IntPtr dataPtr, string imgSetId)
+        protected async Task Generate16GrayImageByPointer(int width, int height, IntPtr dataPtr, string imgSetId)
         {
             await Task.Run(() =>
             {
@@ -897,7 +897,7 @@
 
         public ImageSet()
         {
-            autoDisposeTimer = new Timer(OnAutoDispose, null, -1, 10 * 1000);
+            autoDisposeTimer = new Timer(OnAutoDispose, null, 10 * 1000, -1);
         }
 
         public virtual void Dispose()
diff --git a/src/Bro.Common.Device/Helper/AspectHelper.cs b/src/Bro.Common.Device/Helper/AspectHelper.cs
index b864177..6d9cf36 100644
--- a/src/Bro.Common.Device/Helper/AspectHelper.cs
+++ b/src/Bro.Common.Device/Helper/AspectHelper.cs
@@ -88,7 +88,7 @@
             //    new ProcessException(ex);
             //}
 
-            args.FlowBehavior = FlowBehavior.Return;
+            args.FlowBehavior = FlowBehavior.ThrowException;
         }
     }
     #endregion
diff --git a/src/Bro.Common.Model/Helper/EnumHelper.cs b/src/Bro.Common.Model/Helper/EnumHelper.cs
index fdbb5b7..f22bca9 100644
--- a/src/Bro.Common.Model/Helper/EnumHelper.cs
+++ b/src/Bro.Common.Model/Helper/EnumHelper.cs
@@ -392,6 +392,26 @@
             Measuring = 21,
             MeasureDoneOK = 22,
             MeasureDoneNG = 23,
+
+            CanStretchLeft = 41,
+            CanStretchRight = 42,
+            CanStretchTop = 43,
+            CanStretchBottom = 44,
+
+            CanStretchLeftUpperCorner = 45,
+            CanStretchLeftLowerCorner = 46,
+            CanStretchRightUpperCorner = 47,
+            CanStretchRightLowerCorner = 48,
+
+            StretchingLeft = 31,
+            StretchingRight = 32,
+            StretchingTop = 33,
+            StretchingBottom = 34,
+
+            StretchingLeftUpperCorner = 35,
+            StretchingLeftLowerCorner = 36,
+            StretchingRightUpperCorner = 37,
+            StretchingRightLowerCorner = 38,
         }
 
         public enum MouseState
@@ -400,15 +420,21 @@
             HoverElement = 2,
             InSideElement = 3,
 
+            MoveElement = 4,
+
             StretchingLeft = 11,
             StretchingRight = 12,
-            StretchingUp = 13,
-            StretchingDown = 14,
-            MoveElement = 15,
+            StretchingTop = 13,
+            StretchingBottom = 14,
+
+            StretchingLeftUpperCorner = 15,
+            StretchingLeftLowerCorner = 16,
+            StretchingRightUpperCorner =17,
+            StretchingRightLowerCorner = 18,
 
             New = 21,
             Editing = 22,
-            SelectedElement = 23,
+            //SelectedElement = 23,
 
             MovingAll = 31,
 
diff --git a/src/Bro.Device.Gocator/Bro.Device.Gocator.csproj b/src/Bro.Device.Gocator/Bro.Device.Gocator.csproj
index 8a7b1b1..43038f3 100644
--- a/src/Bro.Device.Gocator/Bro.Device.Gocator.csproj
+++ b/src/Bro.Device.Gocator/Bro.Device.Gocator.csproj
@@ -22,6 +22,7 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>x64</PlatformTarget>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
diff --git a/src/Bro.Device.Gocator/GocatorDriver.cs b/src/Bro.Device.Gocator/GocatorDriver.cs
index 39fb4a2..2a659df 100644
--- a/src/Bro.Device.Gocator/GocatorDriver.cs
+++ b/src/Bro.Device.Gocator/GocatorDriver.cs
@@ -86,18 +86,88 @@
                             long bufferSize = width * height;
                             IntPtr bufferPointer = surfaceMsg.Data;
 
+                            float zoomFactor = (float)((double)surfaceMsg.YResolution / (double)surfaceMsg.XResolution);
+                            int zoomHeight = (int)(zoomFactor * height);
+                            IntPtr zoomPtr = Marshal.AllocHGlobal(zoomHeight * (int)width * 2);
+
+                            //娌℃湁鎻掑�硷紝榛樿0
+                            //Parallel.For(0, height, h =>
+                            //  {
+                            //      CopyMemory((IntPtr)((long)zoomPtr + width * 2 * Math.Floor(h * zoomFactor)), (IntPtr)((long)bufferPointer + width * 2 * h), width * 2);
+                            //  });
+
+                            //浣跨敤涓婁竴琛屽師鏈夋暟鎹�
+                            Parallel.For(0, zoomHeight, h =>
+                            {
+                                int originHeightIndex = (int)Math.Floor((double)height * h / (double)zoomHeight);
+
+                                CopyMemory((IntPtr)((long)zoomPtr + width * 2 * h), (IntPtr)((long)bufferPointer + width * 2 * originHeightIndex), width * 2);
+                            });
+
                             if (imgSet != null)
                             {
                                 imgSet.HImage = new HImage();
-                                imgSet.HImage.GenImage1("uint2", (int)width, (int)height, bufferPointer);
+                                imgSet.HImage.GenImage1("uint2", (int)width, zoomHeight, zoomPtr);
+                                //imgSet.HImage = imgSet.HImage.ZoomImageSize((int)width, zoomHeight, "constant");
+
 
                                 imgSet.HImage_2 = new HImage();
-                                imgSet.HImage_2.GenImage1("uint2", (int)width, (int)height, bufferPointer);
+                                imgSet.HImage_2.GenImage1("uint2", (int)width, zoomHeight, zoomPtr);
+
+                                //imgSet.HImage_2 = imgSet.HImage_2.ZoomImageSize((int)width, zoomHeight, "constant");
                             }
 
-                            Generate16GrayImageByPointer((int)width, (int)height, bufferPointer, imgSet?.Id);
+                            Generate16GrayImageByPointer((int)width, zoomHeight, zoomPtr, imgSet?.Id).ContinueWith(t =>
+                            {
+                                Marshal.FreeHGlobal(zoomPtr);
+                            });
+
+                            //int zoomHeight = (int)(((double)surfaceMsg.YResolution / (double)surfaceMsg.XResolution) * (double)surfaceMsg.Length);
+
+                            //if (imgSet != null)
+                            //{
+                            //    imgSet.HImage = new HImage();
+                            //    imgSet.HImage.GenImage1("uint2", (int)width, (int)height, bufferPointer);
+                            //    imgSet.HImage = imgSet.HImage.ZoomImageSize((int)width, zoomHeight, "constant");
+
+
+                            //    imgSet.HImage_2 = new HImage();
+                            //    imgSet.HImage_2.GenImage1("uint2", (int)width, (int)height, bufferPointer);
+
+                            //    imgSet.HImage_2 = imgSet.HImage_2.ZoomImageSize((int)width, zoomHeight, "constant");
+                            //}
+
+                            //Generate16GrayImageByPointer((int)width, (int)height, bufferPointer, imgSet?.Id);
                         }
                         break;
+                        //case GoDataMessageType.SurfaceIntensity:
+                        //    {
+                        //        GoSurfaceIntensityMsg surfaceMsg = (GoSurfaceIntensityMsg)dataObj;
+                        //        long width = surfaceMsg.Width;
+                        //        long height = surfaceMsg.Length;
+                        //        long bufferSize = width * height;
+                        //        IntPtr bufferPointer = surfaceMsg.Data;
+
+                        //        //int zoomHeight = (surfaceMsg.YResolution * (int)surfaceMsg.Length * 10) / surfaceMsg.XResolution;
+
+                        //        int zoomHeight = (int)(((double)surfaceMsg.YResolution / (double)surfaceMsg.XResolution) * (double)surfaceMsg.Length);
+
+                        //        if (imgSet != null)
+                        //        {
+                        //            imgSet.HImage = new HImage();
+                        //            imgSet.HImage.GenImage1("uint2", (int)width, (int)height, bufferPointer);
+                        //            imgSet.HImage = imgSet.HImage.ZoomImageSize((int)width, zoomHeight, "constant");
+
+
+                        //            imgSet.HImage_2 = new HImage();
+                        //            imgSet.HImage_2.GenImage1("uint2", (int)width, (int)height, bufferPointer);
+
+                        //            imgSet.HImage_2 = imgSet.HImage_2.ZoomImageSize((int)width, zoomHeight, "constant");
+                        //        }
+
+                        //        Generate16GrayImageByPointer((int)width, (int)height, bufferPointer, imgSet?.Id);
+                        //    }
+                        //    break;
                 }
             }
         }
@@ -336,9 +406,9 @@
         public override async void SaveOriginImage(Bitmap map, Bitmap showImage, string imgSetId)
         {
             Task.Run(() =>
-            {
-                OnImageUpdated?.Invoke(this, showImage, imgSetId);
-            });
+             {
+                 OnImageUpdated?.Invoke(this, showImage, imgSetId);
+             });
 
             GoImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId) as GoImageSet;
 
diff --git a/src/Bro.M071.Process/Bro.M071.Process.csproj b/src/Bro.M071.Process/Bro.M071.Process.csproj
index c81ab99..eb681d3 100644
--- a/src/Bro.M071.Process/Bro.M071.Process.csproj
+++ b/src/Bro.M071.Process/Bro.M071.Process.csproj
@@ -152,6 +152,9 @@
     <EmbeddedResource Include="UI\M071_MainForm.resx">
       <DependentUpon>M071_MainForm.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="UI\M071_ShortcutFrm.resx">
+      <DependentUpon>M071_ShortcutFrm.cs</DependentUpon>
+    </EmbeddedResource>
     <None Include="App.config">
       <SubType>Designer</SubType>
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
diff --git a/src/Bro.M071.Process/M071Models.cs b/src/Bro.M071.Process/M071Models.cs
index 0d94bd0..bd98beb 100644
--- a/src/Bro.M071.Process/M071Models.cs
+++ b/src/Bro.M071.Process/M071Models.cs
@@ -137,13 +137,13 @@
             get => key;
             set
             {
-                AlignName = key = value;
+                AliasName = key = value;
             }
         }
 
         [Category("閿悕閰嶇疆")]
         [Description("鍒悕")]
-        public string AlignName { get; set; }
+        public string AliasName { get; set; }
 
         [Category("鍚敤璁剧疆")]
         [Description("true锛氬惎鐢� false锛氱鐢�")]
@@ -178,7 +178,7 @@
 
         public string GetDisplayText()
         {
-            return $"{AlignName}";
+            return $"{AliasName}";
         }
 
         public List<string> GetHalconToolPathList()
diff --git a/src/Bro.M071.Process/M071Process.cs b/src/Bro.M071.Process/M071Process.cs
index 52d0f48..8f08ba9 100644
--- a/src/Bro.M071.Process/M071Process.cs
+++ b/src/Bro.M071.Process/M071Process.cs
@@ -58,8 +58,8 @@
 
         private void InitialSetting()
         {
-            //鏁版嵁搴撹縼绉绘鏌�
-            DatabaseInitialize.Initialize();
+            ////鏁版嵁搴撹縼绉绘鏌�
+            //DatabaseInitialize.Initialize();
 
             MotionCardSettingCheck();
 
@@ -78,21 +78,21 @@
 
                 var snapshotPoint = Config.SnapshotPointCollection.FirstOrDefault(s => s.Id == u.SnapshotPointId && s.IsEnabled);
                 if (snapshotPoint == null)
-                    throw new ProcessException($"{u.AlignName}鏈缃彲鐢ㄦ媿鐓х偣浣�");
+                    throw new ProcessException($"{u.AliasName}鏈缃彲鐢ㄦ媿鐓х偣浣�");
 
                 if (u.ImageSeq < 1)
-                    throw new ProcessException($"{u.AlignName}鍥剧墖搴忓彿灏忎簬1");
+                    throw new ProcessException($"{u.AliasName}鍥剧墖搴忓彿灏忎簬1");
 
                 var algo = Config.KeyAlgorithemCollection.FirstOrDefault(a => a.Id == u.KeyAlgorithemId);
                 if (algo == null)
-                    throw new ProcessException($"{u.AlignName}鏈缃娴嬬畻娉�");
+                    throw new ProcessException($"{u.AliasName}鏈缃娴嬬畻娉�");
 
                 u.KeyAlgorithemPath = algo.AlgorithemPath;
-                LoadHalconTool(u.KeyAlgorithemPath, u.AlignName);
+                LoadHalconTool(u.KeyAlgorithemPath, u.AliasName);
 
                 var resultSet = Config.KeyResultCollection.FirstOrDefault(r => r.Id == u.KeyResultId);
                 if (resultSet == null)
-                    throw new ProcessException($"{u.AlignName}鏈缃娴嬬粨鏋滈厤缃�");
+                    throw new ProcessException($"{u.AliasName}鏈缃娴嬬粨鏋滈厤缃�");
 
                 u.KeyResultList = new List<string>(resultSet.Results);
             });
@@ -511,7 +511,7 @@
                  var excludeKeys = keys.Where(u => u.ImageSeq > count).ToList();
                  if (excludeKeys.Count > 0)
                  {
-                     LogAsync(DateTime.Now, $"{string.Join(" ", excludeKeys.Select(u => u.AlignName))}鏈湪鍥剧墖鑾峰彇搴忓垪涓�", "");
+                     LogAsync(DateTime.Now, $"{string.Join(" ", excludeKeys.Select(u => u.AliasName))}鏈湪鍥剧墖鑾峰彇搴忓垪涓�", "");
                      keyBindCollection.Where(k => excludeKeys.Any(u => u.Key == k.Key)).ToList().ForEach(k =>
                           {
                               k.FillKeyValues(null);
@@ -528,24 +528,24 @@
 
                              var keyBindList = keyBindCollection.Where(u => u.Key == k.Key).ToList();
 
-                             string keyToolKey = k.AlignName + "|" + k.KeyAlgorithemPath;
+                             string keyToolKey = k.AliasName + "|" + k.KeyAlgorithemPath;
                              if (!_halconToolDict.ContainsKey(keyToolKey))
                              {
-                                 LogAsync(DateTime.Now, $"{k.AlignName}妫�娴嬬畻娉曟湭鍒濆鍖�", "");
+                                 LogAsync(DateTime.Now, $"{k.AliasName}妫�娴嬬畻娉曟湭鍒濆鍖�", "");
                              }
                              else
                              {
                                  _halconToolDict[keyToolKey].InputImageDic["INPUT_Image"] = image;
                                  if (!_halconToolDict[keyToolKey].RunProcedure(out string error))
                                  {
-                                     LogAsync(DateTime.Now, $"{k.AlignName}妫�娴嬬畻娉曞紓甯革紝{error}", "");
+                                     LogAsync(DateTime.Now, $"{k.AliasName}妫�娴嬬畻娉曞紓甯革紝{error}", "");
                                  }
                                  else
                                  {
                                      var results = _halconToolDict[keyToolKey].GetResultTuple("OUTPUT_Results").HTupleToDouble();
                                      if (results.Count == 0 || results.Any(u => u < 0))
                                      {
-                                         LogAsync(DateTime.Now, $"{k.AlignName}妫�娴嬬粨鏋滃紓甯�", "");
+                                         LogAsync(DateTime.Now, $"{k.AliasName}妫�娴嬬粨鏋滃紓甯�", "");
                                      }
                                      else
                                      {
diff --git a/src/Bro.M071.Process/M071Process_MotionCard.cs b/src/Bro.M071.Process/M071Process_MotionCard.cs
index 29b2055..62fd994 100644
--- a/src/Bro.M071.Process/M071Process_MotionCard.cs
+++ b/src/Bro.M071.Process/M071Process_MotionCard.cs
@@ -125,7 +125,7 @@
         private void MotionCardSettingCheck()
         {
             IDevice device = DeviceCollection.FirstOrDefault(u => u is IMotionCard);
-            if (device.InitialConfig is MotionCardInitialConfigBase iConfig)
+            if (device?.InitialConfig is MotionCardInitialConfigBase iConfig)
             {
                 outputCtrlCard = device as MotionCardBase;
 
diff --git a/src/Bro.M071.Process/UI/KeyIndicator.cs b/src/Bro.M071.Process/UI/KeyIndicator.cs
index 3e66348..75271a0 100644
--- a/src/Bro.M071.Process/UI/KeyIndicator.cs
+++ b/src/Bro.M071.Process/UI/KeyIndicator.cs
@@ -30,20 +30,6 @@
         [Browsable(false)]
         public override bool IsEnabled { get => base.IsEnabled; set => base.IsEnabled = value; }
 
-
-        //[Category("鏄剧ず璁剧疆")]
-        //[Description("宸︿笂/寮�濮嬬偣浣�")]
-        //[TypeConverter(typeof(ComplexObjectConvert))]
-        //[Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
-        //[Browsable(false)]
-        //public CustomizedPoint StartPoint { get; set; } = new CustomizedPoint();
-
-        //[Category("鏄剧ず璁剧疆")]
-        //[Description("鏄剧ず鍖哄煙澶у皬")]
-        //[TypeConverter(typeof(ComplexObjectConvert))]
-        //[Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
-        //public CustomizedPoint Size { get; set; } = new CustomizedPoint();
-
         [Category("鏄剧ず璁剧疆")]
         [Description("鏍囩鏄剧ず鍖哄煙")]
         [DisplayName("鏍囩鏄剧ず鍖哄煙")]
@@ -68,6 +54,11 @@
             {
                 DisplayRect = this.DisplayRect,
             };
+        }
+
+        protected override void SetSelectedPen()
+        {
+            Pen = new Pen(Color.Red, 6);
         }
 
         public override void Draw(Graphics g)
@@ -108,7 +99,12 @@
 
         public override bool IsMouseInSide(Point p)
         {
-            return false;
+            return p.X >= DisplayRect.X && p.X <= DisplayRect.X + DisplayRect.Width && p.Y >= DisplayRect.Y && p.Y <= DisplayRect.Y + DisplayRect.Height;
+        }
+
+        public override bool IsMouseCanMoveElement(Point p)
+        {
+            return IsMouseInSide(p);
         }
 
         public override void OnKeyDown(object sender, KeyEventArgs e)
@@ -133,6 +129,52 @@
 
         public override void Translate(int x, int y)
         {
+            DisplayRect = new Rectangle(DisplayRect.X + x, DisplayRect.Y + y, DisplayRect.Width, DisplayRect.Height);
+        }
+
+        public override void Relocate(Point point)
+        {
+            DisplayRect = new Rectangle(point.X - DisplayRect.Width / 2, point.Y - DisplayRect.Height / 2, DisplayRect.Width, DisplayRect.Height);
+        }
+
+        public override bool IsMouseCanStretchBottom(Point p)
+        {
+            return Math.Abs(p.X - (DisplayRect.X + DisplayRect.Width / 2)) < 10 && Math.Abs(p.Y - DisplayRect.Y - DisplayRect.Height) < 10;
+        }
+
+        public override bool IsMouseCanStretchRight(Point p)
+        {
+            return Math.Abs(p.X - (DisplayRect.X + DisplayRect.Width)) < 10 && Math.Abs(p.Y - (DisplayRect.Y + DisplayRect.Height / 2)) < 10;
+        }
+
+        public override bool IsMouseCanStretchRightLowerCorner(Point p)
+        {
+            return Math.Abs(p.X - (DisplayRect.X + DisplayRect.Width)) < 10 && Math.Abs(p.Y - (DisplayRect.Y + DisplayRect.Height)) < 10;
+        }
+
+        int x, y = 0;
+        public override void StretchBottom(Point p)
+        {
+            if (p.Y > DisplayRect.Y)
+            {
+                DisplayRect = new Rectangle(DisplayRect.X, DisplayRect.Y, DisplayRect.Width, p.Y - DisplayRect.Y);
+            }
+        }
+
+        public override void StretchRight(Point p)
+        {
+            if (p.X > DisplayRect.X)
+            {
+                DisplayRect = new Rectangle(DisplayRect.X, DisplayRect.Y, p.X - DisplayRect.X, DisplayRect.Height);
+            }
+        }
+
+        public override void StretchRightLowerCorner(Point p)
+        {
+            if (p.X > DisplayRect.X && p.Y > DisplayRect.Y)
+            {
+                DisplayRect = new Rectangle(DisplayRect.X, DisplayRect.Y, p.X - DisplayRect.X, p.Y - DisplayRect.Y);
+            }
         }
 
         protected override void DrawResult(Graphics g)
diff --git a/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs b/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
index 25f0b07..a7e2e8d 100644
--- a/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
+++ b/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
@@ -240,6 +240,7 @@
             this.lvMeasures.UseCompatibleStateImageBehavior = false;
             this.lvMeasures.View = System.Windows.Forms.View.List;
             this.lvMeasures.SelectedIndexChanged += new System.EventHandler(this.lvMeasures_SelectedIndexChanged);
+            this.lvMeasures.DoubleClick += new System.EventHandler(this.lvMeasures_DoubleClick);
             // 
             // propGridKeyIndicator
             // 
diff --git a/src/Bro.M071.Process/UI/M071_MainForm.cs b/src/Bro.M071.Process/UI/M071_MainForm.cs
index 8623ac8..df918c4 100644
--- a/src/Bro.M071.Process/UI/M071_MainForm.cs
+++ b/src/Bro.M071.Process/UI/M071_MainForm.cs
@@ -32,6 +32,13 @@
             plImage.Controls.Add(cvImage);
 
             tscEditLocation.Visible = tsmiShowEditor.Checked = false;
+
+            this.Load += async (s, e) =>
+               {
+                   await Task.Delay(300);
+
+                   cvImage.SetScreenSize();
+               };
         }
 
         public override void OnProcessUpdated()
@@ -48,6 +55,7 @@
             {
                 Bitmap image = (Bitmap)Image.FromFile(Config.BackgroundImagePath);
                 cvImage.LoadImage(image);
+                cvImage.SetScreenSize();
             }
             catch (Exception ex)
             {
@@ -70,7 +78,8 @@
                 lvMeasures.Items.Add(item);
             });
 
-            txtBarcode.ReadOnly = Config.IsBarcodeManulInputBlocked;
+            if (txtBarcode.IsHandleCreated)
+                txtBarcode.BeginInvoke(new Action(() => txtBarcode.ReadOnly = Config.IsBarcodeManulInputBlocked));
 
             Process_M071.OnClearBarcode -= M071_MainForm_OnClearBarcode;
             Process_M071.OnClearBarcode += M071_MainForm_OnClearBarcode;
@@ -150,6 +159,19 @@
 
             MessageBox.Show("鏍囩淇敼瀹屾垚");
         }
+
+        private void lvMeasures_DoubleClick(object sender, EventArgs e)
+        {
+            if (lvMeasures.SelectedItems.Count <= 0)
+                return;
+
+            var ele = cvImage.Elements.FirstOrDefault(u => u.ID == lvMeasures.SelectedItems[0].Tag.ToString());
+            if (ele != null)
+            {
+                ele.State = ElementState.Selected;
+                cvImage.Invalidate();
+            }
+        }
         #endregion
 
         #region 涓婃柟鐘舵�佸尯
@@ -169,6 +191,11 @@
                 txtBarcode.Text = _barcode;
                 _barcode = "";
             }
+
+            if (e.KeyValue == 27)
+            {
+                cvImage.Elements.ToList().ForEach(ele => ele.State = ElementState.Normal);
+            }
         }
 
         private void txtBarcode_TextChanged(object sender, EventArgs e)
diff --git a/src/Bro.M071.Process/UI/M071_ShortcutFrm.Designer.cs b/src/Bro.M071.Process/UI/M071_ShortcutFrm.Designer.cs
index 6ed449b..eca6a77 100644
--- a/src/Bro.M071.Process/UI/M071_ShortcutFrm.Designer.cs
+++ b/src/Bro.M071.Process/UI/M071_ShortcutFrm.Designer.cs
@@ -28,12 +28,103 @@
         /// </summary>
         private void InitializeComponent()
         {
-            this.components = new System.ComponentModel.Container();
+            this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
+            this.chkBlockSaftyDoor = new System.Windows.Forms.CheckBox();
+            this.chkBlockSafetyBeam = new System.Windows.Forms.CheckBox();
+            this.chkBlockBeep = new System.Windows.Forms.CheckBox();
+            this.chkManualInputForbidden = new System.Windows.Forms.CheckBox();
+            this.btnLightSwitch = new System.Windows.Forms.Button();
+            this.flowLayoutPanel1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // flowLayoutPanel1
+            // 
+            this.flowLayoutPanel1.Controls.Add(this.chkBlockSaftyDoor);
+            this.flowLayoutPanel1.Controls.Add(this.chkBlockSafetyBeam);
+            this.flowLayoutPanel1.Controls.Add(this.chkBlockBeep);
+            this.flowLayoutPanel1.Controls.Add(this.chkManualInputForbidden);
+            this.flowLayoutPanel1.Controls.Add(this.btnLightSwitch);
+            this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
+            this.flowLayoutPanel1.Name = "flowLayoutPanel1";
+            this.flowLayoutPanel1.Size = new System.Drawing.Size(800, 450);
+            this.flowLayoutPanel1.TabIndex = 0;
+            // 
+            // chkBlockSaftyDoor
+            // 
+            this.chkBlockSaftyDoor.AutoSize = true;
+            this.chkBlockSaftyDoor.Location = new System.Drawing.Point(3, 3);
+            this.chkBlockSaftyDoor.Name = "chkBlockSaftyDoor";
+            this.chkBlockSaftyDoor.Size = new System.Drawing.Size(86, 17);
+            this.chkBlockSaftyDoor.TabIndex = 0;
+            this.chkBlockSaftyDoor.Text = "灞忚斀瀹夊叏闂�";
+            this.chkBlockSaftyDoor.UseVisualStyleBackColor = true;
+            this.chkBlockSaftyDoor.CheckedChanged += new System.EventHandler(this.chkBlockSaftyDoor_CheckedChanged);
+            // 
+            // chkBlockSafetyBeam
+            // 
+            this.chkBlockSafetyBeam.AutoSize = true;
+            this.chkBlockSafetyBeam.Location = new System.Drawing.Point(95, 3);
+            this.chkBlockSafetyBeam.Name = "chkBlockSafetyBeam";
+            this.chkBlockSafetyBeam.Size = new System.Drawing.Size(98, 17);
+            this.chkBlockSafetyBeam.TabIndex = 1;
+            this.chkBlockSafetyBeam.Text = "灞忚斀瀹夊叏鍏夌嚎";
+            this.chkBlockSafetyBeam.UseVisualStyleBackColor = true;
+            this.chkBlockSafetyBeam.CheckedChanged += new System.EventHandler(this.chkBlockSafetyBeam_CheckedChanged);
+            // 
+            // chkBlockBeep
+            // 
+            this.chkBlockBeep.AutoSize = true;
+            this.chkBlockBeep.Location = new System.Drawing.Point(199, 3);
+            this.chkBlockBeep.Name = "chkBlockBeep";
+            this.chkBlockBeep.Size = new System.Drawing.Size(86, 17);
+            this.chkBlockBeep.TabIndex = 2;
+            this.chkBlockBeep.Text = "灞忚斀铚傞福鍣�";
+            this.chkBlockBeep.UseVisualStyleBackColor = true;
+            this.chkBlockBeep.CheckedChanged += new System.EventHandler(this.chkBlockBeep_CheckedChanged);
+            // 
+            // chkManualInputForbidden
+            // 
+            this.chkManualInputForbidden.AutoSize = true;
+            this.chkManualInputForbidden.Location = new System.Drawing.Point(291, 3);
+            this.chkManualInputForbidden.Name = "chkManualInputForbidden";
+            this.chkManualInputForbidden.Size = new System.Drawing.Size(122, 17);
+            this.chkManualInputForbidden.TabIndex = 4;
+            this.chkManualInputForbidden.Text = "绂佹鎵嬪姩杈撳叆鏉$爜";
+            this.chkManualInputForbidden.UseVisualStyleBackColor = true;
+            this.chkManualInputForbidden.CheckedChanged += new System.EventHandler(this.chkManualInputForbidden_CheckedChanged);
+            // 
+            // btnLightSwitch
+            // 
+            this.btnLightSwitch.Location = new System.Drawing.Point(419, 3);
+            this.btnLightSwitch.Name = "btnLightSwitch";
+            this.btnLightSwitch.Size = new System.Drawing.Size(75, 36);
+            this.btnLightSwitch.TabIndex = 5;
+            this.btnLightSwitch.Text = "鏃ュ厜鐏紑鍏�";
+            this.btnLightSwitch.UseVisualStyleBackColor = true;
+            this.btnLightSwitch.Click += new System.EventHandler(this.btnLightSwitch_Click);
+            // 
+            // M071_ShortcutFrm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.ClientSize = new System.Drawing.Size(800, 450);
+            this.Controls.Add(this.flowLayoutPanel1);
+            this.Name = "M071_ShortcutFrm";
             this.Text = "M071_ShortcutFrm";
+            this.flowLayoutPanel1.ResumeLayout(false);
+            this.flowLayoutPanel1.PerformLayout();
+            this.ResumeLayout(false);
+
         }
 
         #endregion
+
+        private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
+        private System.Windows.Forms.CheckBox chkBlockSaftyDoor;
+        private System.Windows.Forms.CheckBox chkBlockSafetyBeam;
+        private System.Windows.Forms.CheckBox chkBlockBeep;
+        private System.Windows.Forms.CheckBox chkManualInputForbidden;
+        private System.Windows.Forms.Button btnLightSwitch;
     }
 }
\ No newline at end of file
diff --git a/src/Bro.M071.Process/UI/M071_ShortcutFrm.cs b/src/Bro.M071.Process/UI/M071_ShortcutFrm.cs
index 245d16d..d5961a9 100644
--- a/src/Bro.M071.Process/UI/M071_ShortcutFrm.cs
+++ b/src/Bro.M071.Process/UI/M071_ShortcutFrm.cs
@@ -6,6 +6,7 @@
 using System.Drawing;
 using System.Linq;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Windows.Forms;
 
@@ -17,6 +18,81 @@
         public M071_ShortcutFrm()
         {
             InitializeComponent();
+
+            this.Load += (s, e) =>
+              {
+                  loadHandle.Set();
+              };
+        }
+
+        ManualResetEventSlim loadHandle = new ManualResetEventSlim(false);
+        M071Config Config => Process?.IConfig as M071Config;
+        M071Process Process_M071 => Process as M071Process;
+
+        protected override bool IsLogin 
+        { 
+            get => base.IsLogin;
+            set
+            {
+                base.IsLogin = value;
+
+                Task.Run(() =>
+                {
+                    if (loadHandle.Wait(1000))
+                    {
+                        this.Invoke(new Action(() =>
+                        {
+                            chkBlockSafetyBeam.Enabled = chkBlockSaftyDoor.Enabled = IsLogin;
+                        }));
+                    }
+                });
+            }
+        }
+
+        public override void OnProcessUpdated()
+        {
+            base.OnProcessUpdated();
+
+            Task.Run(() =>
+            {
+                if (loadHandle.Wait(1000))
+                {
+                    this.Invoke(new Action(() =>
+                    {
+                        chkBlockBeep.Checked = Config.IsBeepBlocked;
+                        chkBlockSafetyBeam.Checked = Config.IsSafetyBeamBlocked;
+                        chkBlockSaftyDoor.Checked = Config.IsSafetyDoorBlocked;
+                        chkManualInputForbidden.Checked = Config.IsBarcodeManulInputBlocked;
+                    }));
+                }
+            });
+        }
+
+        private void chkBlockSaftyDoor_CheckedChanged(object sender, EventArgs e)
+        {
+            Config.IsSafetyDoorBlocked = chkBlockSaftyDoor.Checked;
+        }
+
+        private void chkBlockSafetyBeam_CheckedChanged(object sender, EventArgs e)
+        {
+            Config.IsSafetyBeamBlocked = chkBlockSafetyBeam.Checked;
+        }
+
+        private void chkBlockBeep_CheckedChanged(object sender, EventArgs e)
+        {
+            Config.IsBeepBlocked = chkBlockBeep.Checked;
+        }
+
+        private void chkManualInputForbidden_CheckedChanged(object sender, EventArgs e)
+        {
+            Config.IsBarcodeManulInputBlocked = chkManualInputForbidden.Checked;
+        }
+
+        bool isLightOn = false;
+        private void btnLightSwitch_Click(object sender, EventArgs e)
+        {
+            isLightOn = !isLightOn;
+            Process_M071.SwitchLight(isLightOn);
         }
     }
 }
diff --git a/src/Bro.M071.Process/UI/M071_ShortcutFrm.resx b/src/Bro.M071.Process/UI/M071_ShortcutFrm.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/src/Bro.M071.Process/UI/M071_ShortcutFrm.resx
@@ -0,0 +1,120 @@
+锘�<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/src/Bro.Process/Bro.Process.csproj b/src/Bro.Process/Bro.Process.csproj
index 9f6d276..c705958 100644
--- a/src/Bro.Process/Bro.Process.csproj
+++ b/src/Bro.Process/Bro.Process.csproj
@@ -168,4 +168,9 @@
     <Error Condition="!Exists('..\..\packages\PostSharp.6.2.7\build\PostSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\PostSharp.6.2.7\build\PostSharp.targets'))" />
   </Target>
   <Import Project="..\..\packages\PostSharp.6.2.7\build\PostSharp.targets" Condition="Exists('..\..\packages\PostSharp.6.2.7\build\PostSharp.targets')" />
+  <ProjectExtensions>
+    <VisualStudio>
+      <UserProperties setting_1json__JsonSchema="https://batect.dev/configSchema.json" />
+    </VisualStudio>
+  </ProjectExtensions>
 </Project>
\ No newline at end of file
diff --git a/src/Bro.UI.Config/AdvancedPwdFrm.cs b/src/Bro.UI.Config/AdvancedPwdFrm.cs
index f710f0b..275c534 100644
--- a/src/Bro.UI.Config/AdvancedPwdFrm.cs
+++ b/src/Bro.UI.Config/AdvancedPwdFrm.cs
@@ -76,5 +76,11 @@
                 CheckInputPassword();
             }
         }
+
+        private void btnExitLogin_Click(object sender, EventArgs e)
+        {
+            OnLoginOK?.Invoke(false);
+            this.DialogResult = DialogResult.Abort;
+        }
     }
 }
diff --git a/src/Bro.UI.Config/AdvancedPwdFrm.designer.cs b/src/Bro.UI.Config/AdvancedPwdFrm.designer.cs
index 8c385ef..bf7e6b6 100644
--- a/src/Bro.UI.Config/AdvancedPwdFrm.designer.cs
+++ b/src/Bro.UI.Config/AdvancedPwdFrm.designer.cs
@@ -32,11 +32,12 @@
             this.label1 = new System.Windows.Forms.Label();
             this.btnCancel = new System.Windows.Forms.Button();
             this.btnOK = new System.Windows.Forms.Button();
+            this.btnExitLogin = new System.Windows.Forms.Button();
             this.SuspendLayout();
             // 
             // txtPwd
             // 
-            this.txtPwd.Font = new System.Drawing.Font("SimSun", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.txtPwd.Font = new System.Drawing.Font("瀹嬩綋", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
             this.txtPwd.Location = new System.Drawing.Point(126, 28);
             this.txtPwd.Name = "txtPwd";
             this.txtPwd.PasswordChar = '*';
@@ -47,7 +48,7 @@
             // label1
             // 
             this.label1.AutoSize = true;
-            this.label1.Font = new System.Drawing.Font("SimSun", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.label1.Font = new System.Drawing.Font("瀹嬩綋", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
             this.label1.Location = new System.Drawing.Point(32, 32);
             this.label1.Name = "label1";
             this.label1.Size = new System.Drawing.Size(52, 14);
@@ -57,7 +58,7 @@
             // btnCancel
             // 
             this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-            this.btnCancel.Font = new System.Drawing.Font("SimSun", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.btnCancel.Font = new System.Drawing.Font("瀹嬩綋", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
             this.btnCancel.Location = new System.Drawing.Point(208, 63);
             this.btnCancel.Name = "btnCancel";
             this.btnCancel.Size = new System.Drawing.Size(75, 23);
@@ -68,7 +69,7 @@
             // 
             // btnOK
             // 
-            this.btnOK.Font = new System.Drawing.Font("SimSun", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.btnOK.Font = new System.Drawing.Font("瀹嬩綋", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
             this.btnOK.Location = new System.Drawing.Point(289, 63);
             this.btnOK.Name = "btnOK";
             this.btnOK.Size = new System.Drawing.Size(75, 23);
@@ -76,6 +77,18 @@
             this.btnOK.Text = "OK";
             this.btnOK.UseVisualStyleBackColor = true;
             this.btnOK.Click += new System.EventHandler(this.button2_Click);
+            // 
+            // btnExitLogin
+            // 
+            this.btnExitLogin.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.btnExitLogin.Font = new System.Drawing.Font("瀹嬩綋", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+            this.btnExitLogin.Location = new System.Drawing.Point(12, 63);
+            this.btnExitLogin.Name = "btnExitLogin";
+            this.btnExitLogin.Size = new System.Drawing.Size(75, 23);
+            this.btnExitLogin.TabIndex = 2;
+            this.btnExitLogin.Text = "閫�鍑虹櫥褰�";
+            this.btnExitLogin.UseVisualStyleBackColor = true;
+            this.btnExitLogin.Click += new System.EventHandler(this.btnExitLogin_Click);
             // 
             // AdvancedPwdFrm
             // 
@@ -85,6 +98,7 @@
             this.ClientSize = new System.Drawing.Size(388, 98);
             this.ControlBox = false;
             this.Controls.Add(this.btnOK);
+            this.Controls.Add(this.btnExitLogin);
             this.Controls.Add(this.btnCancel);
             this.Controls.Add(this.label1);
             this.Controls.Add(this.txtPwd);
@@ -103,5 +117,6 @@
         private System.Windows.Forms.Label label1;
         private System.Windows.Forms.Button btnCancel;
         private System.Windows.Forms.Button btnOK;
+        private System.Windows.Forms.Button btnExitLogin;
     }
 }
\ No newline at end of file
diff --git a/src/Bro.UI.Config/MainFrm.cs b/src/Bro.UI.Config/MainFrm.cs
index f35297d..39c483d 100644
--- a/src/Bro.UI.Config/MainFrm.cs
+++ b/src/Bro.UI.Config/MainFrm.cs
@@ -317,7 +317,7 @@
 
         private void OnLoginOK(bool isLogin)
         {
-            IsLogin = true;
+            IsLogin = isLogin;
         }
         #endregion
 
diff --git a/src/Bro.UI.Device.Winform/CtrlCameraRunBase.Designer.cs b/src/Bro.UI.Device.Winform/CtrlCameraRunBase.Designer.cs
index 784f96b..c83ffd7 100644
--- a/src/Bro.UI.Device.Winform/CtrlCameraRunBase.Designer.cs
+++ b/src/Bro.UI.Device.Winform/CtrlCameraRunBase.Designer.cs
@@ -27,8 +27,14 @@
             this.tsmiShowOpBar = new System.Windows.Forms.ToolStripMenuItem();
             this.tsmiShowElements = new System.Windows.Forms.ToolStripMenuItem();
             this.tsmiShowStatusBar = new System.Windows.Forms.ToolStripMenuItem();
+            this.tsmiBtnExecuteOpConfig = new System.Windows.Forms.ToolStripButton();
+            this.tscOpConfig = new System.Windows.Forms.ToolStripContainer();
+            this.tsmiShowOpConfig = new System.Windows.Forms.ToolStripMenuItem();
+            this.propGridOpConfig = new System.Windows.Forms.PropertyGrid();
             this.toolStripOperation.SuspendLayout();
             this.ctmsVisibleControl.SuspendLayout();
+            this.tscOpConfig.ContentPanel.SuspendLayout();
+            this.tscOpConfig.SuspendLayout();
             this.SuspendLayout();
             // 
             // toolStripOperation
@@ -41,7 +47,8 @@
             this.toolStripOperation.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.tssBtnCameraOp,
             this.tsTxtExposure,
-            this.tsBtnModifyExposure});
+            this.tsBtnModifyExposure,
+            this.tsmiBtnExecuteOpConfig});
             this.toolStripOperation.Location = new System.Drawing.Point(38, 2);
             this.toolStripOperation.Name = "toolStripOperation";
             this.toolStripOperation.Size = new System.Drawing.Size(440, 28);
@@ -76,7 +83,7 @@
             // 
             this.tsTxtExposure.Font = new System.Drawing.Font("Microsoft YaHei UI", 9F);
             this.tsTxtExposure.Name = "tsTxtExposure";
-            this.tsTxtExposure.Size = new System.Drawing.Size(100, 25);
+            this.tsTxtExposure.Size = new System.Drawing.Size(100, 28);
             // 
             // tsBtnModifyExposure
             // 
@@ -84,7 +91,7 @@
             this.tsBtnModifyExposure.Image = ((System.Drawing.Image)(resources.GetObject("tsBtnModifyExposure.Image")));
             this.tsBtnModifyExposure.ImageTransparentColor = System.Drawing.Color.Magenta;
             this.tsBtnModifyExposure.Name = "tsBtnModifyExposure";
-            this.tsBtnModifyExposure.Size = new System.Drawing.Size(60, 22);
+            this.tsBtnModifyExposure.Size = new System.Drawing.Size(60, 25);
             this.tsBtnModifyExposure.Text = "淇敼鏇濆厜";
             this.tsBtnModifyExposure.Click += new System.EventHandler(this.tsBtnModifyExposure_Click);
             // 
@@ -94,9 +101,10 @@
             this.tsmiShowToolBar,
             this.tsmiShowOpBar,
             this.tsmiShowElements,
-            this.tsmiShowStatusBar});
+            this.tsmiShowStatusBar,
+            this.tsmiShowOpConfig});
             this.ctmsVisibleControl.Name = "ctmsVisibleControl";
-            this.ctmsVisibleControl.Size = new System.Drawing.Size(149, 92);
+            this.ctmsVisibleControl.Size = new System.Drawing.Size(149, 114);
             // 
             // tsmiShowToolBar
             // 
@@ -130,11 +138,54 @@
             this.tsmiShowStatusBar.Text = "鏄剧ず鐘舵�佹爮";
             this.tsmiShowStatusBar.CheckedChanged += new System.EventHandler(this.tsmiShowStatusBar_CheckedChanged);
             // 
+            // tsmiBtnExecuteOpConfig
+            // 
+            this.tsmiBtnExecuteOpConfig.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+            this.tsmiBtnExecuteOpConfig.Image = ((System.Drawing.Image)(resources.GetObject("tsmiBtnExecuteOpConfig.Image")));
+            this.tsmiBtnExecuteOpConfig.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.tsmiBtnExecuteOpConfig.Name = "tsmiBtnExecuteOpConfig";
+            this.tsmiBtnExecuteOpConfig.Size = new System.Drawing.Size(84, 25);
+            this.tsmiBtnExecuteOpConfig.Text = "鎵ц鎿嶄綔閰嶇疆";
+            this.tsmiBtnExecuteOpConfig.Click += new System.EventHandler(this.tsmiBtnExecuteOpConfig_Click);
+            // 
+            // tscOpConfig
+            // 
+            // 
+            // tscOpConfig.ContentPanel
+            // 
+            this.tscOpConfig.ContentPanel.Controls.Add(this.propGridOpConfig);
+            this.tscOpConfig.ContentPanel.Size = new System.Drawing.Size(478, 175);
+            this.tscOpConfig.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.tscOpConfig.Location = new System.Drawing.Point(0, 265);
+            this.tscOpConfig.Name = "tscOpConfig";
+            this.tscOpConfig.Size = new System.Drawing.Size(478, 175);
+            this.tscOpConfig.TabIndex = 1;
+            this.tscOpConfig.Text = "toolStripContainer1";
+            this.tscOpConfig.Visible = false;
+            // 
+            // tsmiShowOpConfig
+            // 
+            this.tsmiShowOpConfig.CheckOnClick = true;
+            this.tsmiShowOpConfig.Name = "tsmiShowOpConfig";
+            this.tsmiShowOpConfig.Size = new System.Drawing.Size(148, 22);
+            this.tsmiShowOpConfig.Text = "鏄剧ず鎿嶄綔閰嶇疆";
+            this.tsmiShowOpConfig.CheckedChanged += new System.EventHandler(this.tsmiShowOpConfig_CheckedChanged);
+            // 
+            // propGridOpConfig
+            // 
+            this.propGridOpConfig.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.propGridOpConfig.Location = new System.Drawing.Point(0, 0);
+            this.propGridOpConfig.Name = "propGridOpConfig";
+            this.propGridOpConfig.Size = new System.Drawing.Size(478, 175);
+            this.propGridOpConfig.TabIndex = 0;
+            this.propGridOpConfig.ToolbarVisible = false;
+            // 
             // CtrlCameraRunBase
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.ContextMenuStrip = this.ctmsVisibleControl;
+            this.Controls.Add(this.tscOpConfig);
             this.Controls.Add(this.toolStripOperation);
             this.Name = "CtrlCameraRunBase";
             this.Size = new System.Drawing.Size(478, 440);
@@ -142,6 +193,9 @@
             this.toolStripOperation.ResumeLayout(false);
             this.toolStripOperation.PerformLayout();
             this.ctmsVisibleControl.ResumeLayout(false);
+            this.tscOpConfig.ContentPanel.ResumeLayout(false);
+            this.tscOpConfig.ResumeLayout(false);
+            this.tscOpConfig.PerformLayout();
             this.ResumeLayout(false);
 
         }
@@ -158,5 +212,9 @@
         private System.Windows.Forms.ToolStripMenuItem tsmiShowOpBar;
         private System.Windows.Forms.ToolStripMenuItem tsmiShowElements;
         private System.Windows.Forms.ToolStripMenuItem tsmiShowStatusBar;
+        private System.Windows.Forms.ToolStripButton tsmiBtnExecuteOpConfig;
+        private System.Windows.Forms.ToolStripMenuItem tsmiShowOpConfig;
+        private System.Windows.Forms.ToolStripContainer tscOpConfig;
+        private System.Windows.Forms.PropertyGrid propGridOpConfig;
     }
 }
diff --git a/src/Bro.UI.Device.Winform/CtrlCameraRunBase.cs b/src/Bro.UI.Device.Winform/CtrlCameraRunBase.cs
index c990ef1..84f45c6 100644
--- a/src/Bro.UI.Device.Winform/CtrlCameraRunBase.cs
+++ b/src/Bro.UI.Device.Winform/CtrlCameraRunBase.cs
@@ -1,8 +1,11 @@
-锘縰sing Bro.Common.Interface;
+锘縰sing Bro.Common.Factory;
+using Bro.Common.Helper;
+using Bro.Common.Interface;
 using Bro.UI.Model.Winform;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
+using System.Reflection;
 using System.Windows.Forms;
 
 namespace Bro.Common.Base.UI
@@ -46,6 +49,17 @@
             Camera.OnCameraOpModeChanged += Camera_OnCameraOpModeChanged;
 
             InitialToolBarVisible();
+
+            string typeCode = Camera.GetType().GetCustomAttribute<DeviceAttribute>()?.TypeCode;
+
+            if (string.IsNullOrWhiteSpace(typeCode))
+            {
+                propGridOpConfig.SelectedObject = OpConfig = new CameraOprerationConfigBase();
+            }
+            else
+            {
+                propGridOpConfig.SelectedObject = OpConfig = ConfigFactory.GetOperationConfig(typeCode);
+            }
         }
 
         private readonly Dictionary<string, List<IShapeElement>> _eleBufferDict = new Dictionary<string, List<IShapeElement>>();
@@ -248,8 +262,13 @@
         {
             IsShowStatusBar = tsmiShowStatusBar.Checked;
         }
+
+        private void tsmiShowOpConfig_CheckedChanged(object sender, EventArgs e)
+        {
+            tscOpConfig.Visible = tsmiShowOpConfig.Checked;
+        }
         #endregion
-        
+
         public IOperationConfig OpConfig { get; set; }
 
         public void UploadOperationConfig(IOperationConfig opConfig)
@@ -261,5 +280,10 @@
         {
             throw new NotImplementedException();
         }
+
+        private void tsmiBtnExecuteOpConfig_Click(object sender, EventArgs e)
+        {
+            Camera.Snapshot(OpConfig);
+        }
     }
 }
diff --git a/src/Bro.UI.Device.Winform/CtrlCameraRunBase.resx b/src/Bro.UI.Device.Winform/CtrlCameraRunBase.resx
index 39b02af..d9da94d 100644
--- a/src/Bro.UI.Device.Winform/CtrlCameraRunBase.resx
+++ b/src/Bro.UI.Device.Winform/CtrlCameraRunBase.resx
@@ -151,6 +151,21 @@
         TgDQASA1MVpwzwAAAABJRU5ErkJggg==
 </value>
   </data>
+  <data name="tsmiBtnExecuteOpConfig.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
   <metadata name="ctmsVisibleControl.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>127, 17</value>
   </metadata>
diff --git a/src/Bro.UI.Model.Winform/ElementBase.cs b/src/Bro.UI.Model.Winform/ElementBase.cs
index eb02a2f..899ff14 100644
--- a/src/Bro.UI.Model.Winform/ElementBase.cs
+++ b/src/Bro.UI.Model.Winform/ElementBase.cs
@@ -243,16 +243,20 @@
             if (State == ElementState.MeasureDoneNG || State == ElementState.MeasureDoneOK)
                 return;
 
-            if (State == ElementState.MouseInSide)
+            //if (State == ElementState.MouseInSide)
+            //{
+            //    State = ElementState.Selected;
+            //}
+            //else if (State == ElementState.Selected || State == ElementState.Moving)
+            //{
+            //    if (IsMouseInSide(p))
+            //    {
+            //        State = ElementState.MouseInSide;
+            //    }
+            //}
+            if (IsMouseInSide(p))
             {
                 State = ElementState.Selected;
-            }
-            else if (State == ElementState.Selected || State == ElementState.Moving)
-            {
-                if (IsMouseInSide(p))
-                {
-                    State = ElementState.MouseInSide;
-                }
             }
         }
 
@@ -262,22 +266,59 @@
 
         public virtual void OnMouseDown(Point p)
         {
-            switch (State)
+            //switch (State)
+            //{
+            //    case ElementState.New:
+            //        OnMouseDownWhenNew(p);
+            //        break;
+            //    case ElementState.MouseHover:
+            //        break;
+            //    case ElementState.MouseInSide:
+            //        State = ElementState.Selected;
+            //        break;
+            //    case ElementState.Selected:
+            //        _startPoint = p;
+            //        State = ElementState.Moving;
+            //        break;
+            //    case ElementState.Normal:
+            //        break;
+            //}
+
+            if (IsMouseCanMoveElement(p) && State == ElementState.Selected)
             {
-                case ElementState.New:
-                    OnMouseDownWhenNew(p);
-                    break;
-                case ElementState.MouseHover:
-                    break;
-                case ElementState.MouseInSide:
-                    State = ElementState.Selected;
-                    break;
-                case ElementState.Selected:
-                    _startPoint = p;
-                    State = ElementState.Moving;
-                    break;
-                case ElementState.Normal:
-                    break;
+                State = ElementState.Moving;
+            }
+            else if (IsMouseCanStretchBottom(p) && State == ElementState.CanStretchBottom)
+            {
+                State = ElementState.StretchingBottom;
+            }
+            else if (IsMouseCanStretchTop(p) && State == ElementState.CanStretchTop)
+            {
+                State = ElementState.StretchingTop;
+            }
+            else if (IsMouseCanStretchLeft(p) && State == ElementState.CanStretchLeft)
+            {
+                State = ElementState.StretchingLeft;
+            }
+            else if (IsMouseCanStretchRight(p) && State == ElementState.CanStretchRight)
+            {
+                State = ElementState.StretchingRight;
+            }
+            else if (IsMouseCanStretchLeftLowerCorner(p) && State == ElementState.CanStretchLeftLowerCorner)
+            {
+                State = ElementState.StretchingLeftLowerCorner;
+            }
+            else if (IsMouseCanStretchLeftUpperCorner(p) && State == ElementState.CanStretchLeftUpperCorner)
+            {
+                State = ElementState.StretchingLeftUpperCorner;
+            }
+            else if (IsMouseCanStretchRightLowerCorner(p) && State == ElementState.CanStretchRightLowerCorner)
+            {
+                State = ElementState.StretchingRightLowerCorner;
+            }
+            else if (IsMouseCanStretchRightUpperCorner(p) && State == ElementState.CanStretchRightUpperCorner)
+            {
+                State = ElementState.StretchingRightUpperCorner;
             }
         }
 
@@ -289,27 +330,133 @@
                     OnMouseMoveWhenNew(p);
                     break;
                 case ElementState.Selected:
+                    if (IsMouseCanStretchLeft(p))
+                    {
+                        State = ElementState.CanStretchLeft;
+                    }
+                    else if (IsMouseCanStretchRight(p))
+                    {
+                        State = ElementState.CanStretchRight;
+                    }
+                    else if (IsMouseCanStretchTop(p))
+                    {
+                        State = ElementState.CanStretchTop;
+                    }
+                    else if (IsMouseCanStretchBottom(p))
+                    {
+                        State = ElementState.CanStretchBottom;
+                    }
+                    else if (IsMouseCanStretchLeftLowerCorner(p))
+                    {
+                        State = ElementState.CanStretchLeftLowerCorner;
+                    }
+                    else if (IsMouseCanStretchLeftUpperCorner(p))
+                    {
+                        State = ElementState.CanStretchLeftUpperCorner;
+                    }
+                    else if (IsMouseCanStretchRightLowerCorner(p))
+                    {
+                        State = ElementState.CanStretchRightLowerCorner;
+                    }
+                    else if (IsMouseCanStretchRightUpperCorner(p))
+                    {
+                        State = ElementState.CanStretchRightUpperCorner;
+                    }
+
                     break;
                 case ElementState.Moving:
                     _currentPoint = p;
-                    Translate(_currentPoint.X - _startPoint.X, _currentPoint.Y - _startPoint.Y);
-                    _startPoint = _currentPoint;
+                    //Translate(_currentPoint.X - _startPoint.X, _currentPoint.Y - _startPoint.Y);
+                    //_startPoint = _currentPoint;
+                    Relocate(p);
                     break;
                 case ElementState.MouseHover:
                 case ElementState.MouseInSide:
                 case ElementState.Normal:
-                    if (IsMouseInSide(p))
+                    //if (IsMouseInSide(p))
+                    //{
+                    //    State = ElementState.MouseInSide;
+                    //}
+                    //else if (IsMouseHover(p))
+                    //{
+                    //    State = ElementState.MouseHover;
+                    //}
+                    //else
+                    //{
+                    //    State = ElementState.Normal;
+                    //}
+                    break;
+                case ElementState.CanStretchBottom:
+                    if (!IsMouseCanStretchBottom(p))
                     {
-                        State = ElementState.MouseInSide;
+                        State = ElementState.Selected;
                     }
-                    else if (IsMouseHover(p))
+                    break;
+                case ElementState.CanStretchTop:
+                    if (!IsMouseCanStretchTop(p))
                     {
-                        State = ElementState.MouseHover;
+                        State = ElementState.Selected;
                     }
-                    else
+                    break;
+                case ElementState.CanStretchLeft:
+                    if (!IsMouseCanStretchLeft(p))
                     {
-                        State = ElementState.Normal;
+                        State = ElementState.Selected;
                     }
+                    break;
+                case ElementState.CanStretchRight:
+                    if (!IsMouseCanStretchRight(p))
+                    {
+                        State = ElementState.Selected;
+                    }
+                    break;
+                case ElementState.CanStretchLeftUpperCorner:
+                    if (!IsMouseCanStretchLeftUpperCorner(p))
+                    {
+                        State = ElementState.Selected;
+                    }
+                    break;
+                case ElementState.CanStretchLeftLowerCorner:
+                    if (!IsMouseCanStretchLeftLowerCorner(p))
+                    {
+                        State = ElementState.Selected;
+                    }
+                    break;
+                case ElementState.CanStretchRightLowerCorner:
+                    if (!IsMouseCanStretchRightLowerCorner(p))
+                    {
+                        State = ElementState.Selected;
+                    }
+                    break;
+                case ElementState.CanStretchRightUpperCorner:
+                    if (!IsMouseCanStretchRightUpperCorner(p))
+                    {
+                        State = ElementState.Selected;
+                    }
+                    break;
+                case ElementState.StretchingTop:
+                    StretchTop(p);
+                    break;
+                case ElementState.StretchingBottom:
+                    StretchBottom(p);
+                    break;
+                case ElementState.StretchingLeft:
+                    StretchLeft(p);
+                    break;
+                case ElementState.StretchingRight:
+                    StretchRight(p);
+                    break;
+                case ElementState.StretchingLeftLowerCorner:
+                    StretchLeftLowerCorner(p);
+                    break;
+                case ElementState.StretchingLeftUpperCorner:
+                    StretchLeftUpperCorner(p);
+                    break;
+                case ElementState.StretchingRightLowerCorner:
+                    StretchRightLowerCorner(p);
+                    break;
+                case ElementState.StretchingRightUpperCorner:
+                    StretchRightUpperCorner(p);
                     break;
             }
         }
@@ -319,15 +466,48 @@
             switch (State)
             {
                 case ElementState.Moving:
-                    //Calculate(null);
                     State = ElementState.Selected;
                     break;
                 case ElementState.Selected:
-                    //Calculate(null);
+                    if (!IsMouseInSide(p))
+                    {
+                        State = ElementState.Normal;
+                    }
+                    break;
+                case ElementState.StretchingBottom:
+                    State = ElementState.CanStretchBottom;
+                    break;
+                case ElementState.StretchingLeft:
+                    State = ElementState.CanStretchLeft;
+                    break;
+                case ElementState.StretchingRight:
+                    State = ElementState.CanStretchRight;
+                    break;
+                case ElementState.StretchingTop:
+                    State = ElementState.CanStretchTop;
+                    break;
+                case ElementState.StretchingLeftLowerCorner:
+                    State = ElementState.CanStretchLeftLowerCorner;
+                    break;
+                case ElementState.StretchingLeftUpperCorner:
+                    State = ElementState.CanStretchLeftUpperCorner;
+                    break;
+                case ElementState.StretchingRightUpperCorner:
+                    State = ElementState.CanStretchRightUpperCorner;
+                    break;
+                case ElementState.StretchingRightLowerCorner:
+                    State = ElementState.CanStretchRightLowerCorner;
                     break;
                 default:
+                    State = ElementState.Normal;
                     break;
             }
+
+            AfterTranformOp();
+        }
+
+        public virtual void AfterTranformOp()
+        {
         }
 
         #region 褰撳熀鍏冪姸鎬佷负鏂板缓鎴栧彲缂栬緫鏃剁殑榧犳爣鎿嶄綔鍔ㄤ綔
@@ -366,32 +546,84 @@
 
         public abstract bool IsIntersect(Rectangle rect);
         #region Move & Stretch & Move Anchor
-        public virtual bool IsMouseCanMoveElement()
+        public virtual bool IsMouseCanMoveElement(Point p)
         {
             return false;
         }
 
-        public virtual bool IsMouseCanStretchLeft()
+        public virtual bool IsMouseCanStretchLeft(Point p)
         {
             return false;
         }
 
-        public virtual bool IsMouseCanStretchRight()
+        public virtual bool IsMouseCanStretchRight(Point p)
         {
             return false;
         }
 
-        public virtual bool IsMouseCanStretchTop()
+        public virtual bool IsMouseCanStretchTop(Point p)
         {
             return false;
         }
 
-        public virtual bool IsMouseCanStretchBottom()
+        public virtual bool IsMouseCanStretchBottom(Point p)
         {
             return false;
         }
 
-        public virtual bool IsMouseCanMoveAnchor()
+        public virtual void StretchLeft(Point p)
+        {
+        }
+
+        public virtual void StretchRight(Point p)
+        {
+        }
+
+        public virtual void StretchTop(Point p)
+        {
+        }
+
+        public virtual void StretchBottom(Point p)
+        {
+        }
+
+        public virtual bool IsMouseCanStretchLeftUpperCorner(Point p)
+        {
+            return false;
+        }
+
+        public virtual bool IsMouseCanStretchRightUpperCorner(Point p)
+        {
+            return false;
+        }
+
+        public virtual bool IsMouseCanStretchLeftLowerCorner(Point p)
+        {
+            return false;
+        }
+
+        public virtual bool IsMouseCanStretchRightLowerCorner(Point p)
+        {
+            return false;
+        }
+
+        public virtual void StretchLeftUpperCorner(Point p)
+        {
+        }
+
+        public virtual void StretchRightUpperCorner(Point p)
+        {
+        }
+
+        public virtual void StretchLeftLowerCorner(Point p)
+        {
+        }
+
+        public virtual void StretchRightLowerCorner(Point p)
+        {
+        }
+
+        public virtual bool IsMouseCanMoveAnchor(Point p)
         {
             return false;
         }
@@ -402,6 +634,8 @@
         #region 鍙樺舰鎿嶄綔
         public abstract void Translate(int x, int y);
 
+        public virtual void Relocate(Point point) { }
+
         //public abstract void RotateAt(int x, int y, float degree);
         #endregion
 
diff --git a/src/Bro.UI.Model.Winform/UI/Canvas.cs b/src/Bro.UI.Model.Winform/UI/Canvas.cs
index 40f2b3c..16ae7be 100644
--- a/src/Bro.UI.Model.Winform/UI/Canvas.cs
+++ b/src/Bro.UI.Model.Winform/UI/Canvas.cs
@@ -77,7 +77,7 @@
             set => stsStatus.Visible = value;
         }
 
-        private async void OnMouseLocationUpdated(Point screenPoint, Point imagePoint, string colorDesc)
+        private void OnMouseLocationUpdated(Point screenPoint, Point imagePoint, string colorDesc)
         {
             //await Task.Run(() => tsslLocation.Text = $"灞忓箷鍧愭爣X锛歿screenPoint.X}锛孻锛歿screenPoint.Y}    鍥剧墖鍧愭爣X锛歿imagePoint.X}锛孻锛歿imagePoint.Y}   棰滆壊锛歿colorDesc}");
             this.Invoke(new Action(() =>
@@ -381,17 +381,17 @@
 
         private void tsmiUnselectElements_Click(object sender, EventArgs e)
         {
-            if (MouseState == MouseState.SelectedElement)
-            {
-                MouseState = MouseState.Normal;
+            //if (MouseState == MouseState.SelectedElement)
+            //{
+            //    MouseState = MouseState.Normal;
 
-                //Elements.ForEach(ele =>
-                foreach (IShapeElement ele in Elements)
-                {
-                    ele.State = ElementState.Normal;
-                }
-                //);
-            }
+            //    //Elements.ForEach(ele =>
+            //    foreach (IShapeElement ele in Elements)
+            //    {
+            //        ele.State = ElementState.Normal;
+            //    }
+            //    //);
+            //}
         }
         #endregion
 
diff --git a/src/Bro.UI.Model.Winform/UI/CanvasImage.cs b/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
index 55af243..0de094a 100644
--- a/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
+++ b/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
@@ -30,6 +30,8 @@
             MouseDown += Canvas_MouseDown;
             MouseMove += Canvas_MouseMove;
             MouseUp += Canvas_MouseUp;
+
+            EventRouter.ChangeElementsMouseState = OnElementChangeMouseState;
         }
 
         #region Event
@@ -261,10 +263,12 @@
                         MouseState = MouseState.New;
                         break;
                     case ElementState.Selected:
-                        MouseState = MouseState.SelectedElement;
+                        CurrentElementId = ele.ID;
+                        Cursor = Cursors.Default;
                         break;
                     case ElementState.Moving:
                         MouseState = MouseState.MoveElement;
+                        Cursor = Cursors.NoMove2D;
                         break;
                     case ElementState.MouseInSide:
                         MouseState = MouseState.InSideElement;
@@ -272,14 +276,67 @@
                     case ElementState.MouseHover:
                         MouseState = MouseState.HoverElement;
                         break;
+
+                    case ElementState.CanStretchLeft:
+                        Cursor = Cursors.SizeWE;
+                        break;
+                    case ElementState.StretchingLeft:
+                        MouseState = MouseState.StretchingLeft;
+                        Cursor = Cursors.SizeWE;
+                        break;
+                    case ElementState.CanStretchBottom:
+                        Cursor = Cursors.SizeNS;
+                        break;
+                    case ElementState.StretchingBottom:
+                        MouseState = MouseState.StretchingBottom;
+                        Cursor = Cursors.SizeNS;
+                        break;
+                    case ElementState.CanStretchRight:
+                        Cursor = Cursors.SizeWE;
+                        break;
+                    case ElementState.StretchingRight:
+                        MouseState = MouseState.StretchingRight;
+                        Cursor = Cursors.SizeWE;
+                        break;
+                    case ElementState.CanStretchTop:
+                        Cursor = Cursors.SizeNS;
+                        break;
+                    case ElementState.StretchingTop:
+                        MouseState = MouseState.StretchingTop;
+                        Cursor = Cursors.SizeNS;
+                        break;
+
+                    case ElementState.CanStretchLeftLowerCorner:
+                        Cursor = Cursors.SizeNESW;
+                        break;
+                    case ElementState.StretchingLeftLowerCorner:
+                        MouseState = MouseState.StretchingLeftLowerCorner;
+                        Cursor = Cursors.SizeNESW;
+                        break;
+                    case ElementState.CanStretchLeftUpperCorner:
+                        Cursor = Cursors.SizeNWSE;
+                        break;
+                    case ElementState.StretchingLeftUpperCorner:
+                        MouseState = MouseState.StretchingLeftUpperCorner;
+                        Cursor = Cursors.SizeNWSE;
+                        break;
+                    case ElementState.CanStretchRightLowerCorner:
+                        Cursor = Cursors.SizeNWSE;
+                        break;
+                    case ElementState.StretchingRightLowerCorner:
+                        MouseState = MouseState.StretchingRightLowerCorner;
+                        Cursor = Cursors.SizeNWSE;
+                        break;
+                    case ElementState.CanStretchRightUpperCorner:
+                        Cursor = Cursors.SizeNESW;
+                        break;
+                    case ElementState.StretchingRightUpperCorner:
+                        MouseState = MouseState.StretchingRightUpperCorner;
+                        Cursor = Cursors.SizeNESW;
+                        break;
                     default:
                         MouseState = MouseState.Normal;
                         break;
-                }
-
-                if (MouseState == MouseState.SelectedElement)
-                {
-                    CurrentElementId = ele.ID;
                 }
             }
             else
@@ -328,6 +385,7 @@
         }
 
         Point startPoint, currentPoint;
+        bool _isMouseBtnPressing = false;
 
         private void Canvas_MouseDown(object sender, MouseEventArgs e)
         {
@@ -335,49 +393,22 @@
 
             if (e.Button == MouseButtons.Left)
             {
+                _isMouseBtnPressing = true;
+
                 switch (MouseState)
                 {
                     case MouseState.Normal:
-                        //if (DrawTemplate == null && string.IsNullOrWhiteSpace(CurrentElementId))
-                        //{
-                        //    this.Cursor = Cursors.Hand;
-                        //    startPoint = e.Location;
-                        //}
-                        //else
-                        //{
-                        //    MouseState = MouseState.New;
-                        //}
-                        MouseState = MouseState.MovingAll;
                         startPoint = e.Location;
-
-                        //Elements.ForEach(ele =>
-                        foreach (IShapeElement ele in Elements)
-                        {
-                            ele.State = ElementState.Normal;
-                            ele.OnMouseDown(p);
-                        }
-                        //);
                         break;
                     case MouseState.StretchingLeft:
                         break;
                     case MouseState.StretchingRight:
                         break;
-                    case MouseState.StretchingUp:
+                    case MouseState.StretchingTop:
                         break;
-                    case MouseState.StretchingDown:
+                    case MouseState.StretchingBottom:
                         break;
                     case MouseState.MoveElement:
-                        break;
-                    case MouseState.SelectedElement:
-                        //MouseState = MouseState.MoveElement;
-                        startPoint = e.Location;
-
-                        //Elements.ForEach(ele =>
-                        foreach (IShapeElement ele in Elements)
-                        {
-                            ele.OnMouseDown(p);
-                        }
-                        //);
                         break;
                     case MouseState.HoverElement:
                     case MouseState.InSideElement:
@@ -392,26 +423,16 @@
                         break;
                 }
 
-                //if (DrawTemplate == null)
-                //{
-                //    this.Cursor = Cursors.Hand;
-                //    startPoint = e.Location;
-                //}
-
-                //Point p = ToMapPoint(e.Location);
-
-                //Elements.ForEach(ele =>
-                //{
-                //    ele.State = ElementState.Normal;
-                //    ele.OnMouseDown(p);
-                //});
-
-                //DrawTemplate?.OnMouseDown(p);
+                foreach (IShapeElement ele in Elements)
+                {
+                    ele.OnMouseDown(p);
+                }
             }
         }
 
         private void Canvas_MouseUp(object sender, MouseEventArgs e)
         {
+            _isMouseBtnPressing = false;
             switch (MouseState)
             {
                 case MouseState.Normal:
@@ -424,9 +445,9 @@
                     break;
                 case MouseState.StretchingRight:
                     break;
-                case MouseState.StretchingUp:
+                case MouseState.StretchingTop:
                     break;
-                case MouseState.StretchingDown:
+                case MouseState.StretchingBottom:
                     break;
                 case MouseState.MoveElement:
                     //MouseState = MouseState.SelectedElement;
@@ -443,7 +464,6 @@
                 case MouseState.SelectionZoneDoing:
                     MouseState = MouseState.SelectionZone;
 
-                    //Elements.ForEach(ele =>
                     foreach (IShapeElement ele in Elements)
                     {
                         if (ele.IsIntersect(_selectionRect))
@@ -451,7 +471,6 @@
                             ele.State = ElementState.Selected;
                         }
                     }
-                    //);
 
                     break;
             }
@@ -460,12 +479,10 @@
 
             Point p = ToMapPoint(e.Location);
             DrawTemplate?.OnMouseUp(p);
-            //Elements.ForEach(ele =>
             foreach (IShapeElement ele in Elements)
             {
                 ele.OnMouseUp(p);
             }
-            //);
         }
 
         private void Canvas_MouseMove(object sender, MouseEventArgs e)
@@ -475,26 +492,26 @@
             switch (MouseState)
             {
                 case MouseState.Normal:
-                    //Elements.ForEach(u =>
-                    //{
-                    //    u.OnMouseMove(p);
-                    //});
+                    {
+                        if (_isMouseBtnPressing)
+                        {
+                            currentPoint = e.Location;
+                            Point p1 = ToMapPoint(startPoint);
+                            Point p2 = ToMapPoint(currentPoint);
+                            Matrix.Translate(p2.X - p1.X, p2.Y - p1.Y);
+                            startPoint = e.Location;
+                        }
+                    }
                     break;
                 case MouseState.StretchingLeft:
                     break;
                 case MouseState.StretchingRight:
                     break;
-                case MouseState.StretchingUp:
+                case MouseState.StretchingTop:
                     break;
-                case MouseState.StretchingDown:
+                case MouseState.StretchingBottom:
                     break;
                 case MouseState.MoveElement:
-                    //Elements.ForEach(ele =>
-                    foreach (IShapeElement ele in Elements)
-                    {
-                        ele.OnMouseMove(p);
-                    }
-                    //);
                     break;
                 case MouseState.HoverElement:
                 case MouseState.InSideElement:
@@ -504,13 +521,6 @@
                 case MouseState.Editing:
                     break;
                 case MouseState.MovingAll:
-                    {
-                        currentPoint = e.Location;
-                        Point p1 = ToMapPoint(startPoint);
-                        Point p2 = ToMapPoint(currentPoint);
-                        Matrix.Translate(p2.X - p1.X, p2.Y - p1.Y);
-                        startPoint = e.Location;
-                    }
                     break;
                 case MouseState.SelectionZoneDoing:
                     {
@@ -529,17 +539,6 @@
                     break;
             }
 
-            //if (this.Cursor == Cursors.Hand)
-            //{
-            //    currentPoint = e.Location;
-
-            //    Point p1 = ToMapPoint(startPoint);
-            //    Point p2 = ToMapPoint(currentPoint);
-            //    Matrix.Translate(p2.X - p1.X, p2.Y - p1.Y);
-            //    startPoint = e.Location;
-            //}
-
-            //DisplayMouseLocation(e.Location);
             Point mapPoint = ToMapPoint(e.Location);
             Color color = Color.Transparent;
             if (MAP != null && mapPoint.X > 0 && mapPoint.X < MAP.Width && mapPoint.Y > 0 && mapPoint.Y < MAP.Height)
@@ -550,12 +549,10 @@
 
             if (MouseState != MouseState.SelectionZoneDoing)
             {
-                //Elements.ForEach(u =>
                 foreach (IShapeElement ele in Elements)
                 {
                     ele.OnMouseMove(p);
                 }
-                //);
             }
 
             Invalidate();
@@ -569,7 +566,7 @@
             {
                 switch (MouseState)
                 {
-                    case MouseState.SelectedElement:
+                    //case MouseState.SelectedElement:
                     case MouseState.HoverElement:
                     case MouseState.InSideElement:
                     case MouseState.MoveElement:
@@ -587,7 +584,7 @@
             }
             else
             {
-                if (MouseState == MouseState.SelectedElement)
+                //if (MouseState == MouseState.SelectedElement)
                 {
                     MouseState = MouseState.Normal;
 
@@ -702,22 +699,22 @@
         #region 鎸夐敭鎿嶄綔
         public void OnCanvasKeyPressed(object sender, KeyPressEventArgs e)
         {
-            if (e.KeyChar == 27)  //Esc
-            {
-                if (MouseState == MouseState.SelectedElement)
-                {
-                    MouseState = MouseState.Normal;
+            //if (e.KeyChar == 27)  //Esc
+            //{
+            //    //if (MouseState == MouseState.SelectedElement)
+            //    {
+            //        MouseState = MouseState.Normal;
 
-                    //Elements.ForEach(ele =>
-                    foreach (IShapeElement ele in Elements)
-                    {
-                        ele.State = ElementState.Normal;
-                    }
-                    //);
-                }
-            }
+            //        //Elements.ForEach(ele =>
+            //        foreach (IShapeElement ele in Elements)
+            //        {
+            //            ele.State = ElementState.Normal;
+            //        }
+            //        //);
+            //    }
+            //}
 
-            Invalidate();
+            //Invalidate();
         }
 
         public void OnCanvasKeyDown(object sender, KeyEventArgs e)
@@ -741,7 +738,7 @@
                     Elements.Remove(Elements.FirstOrDefault(u => u.ID == CurrentElementId));
                     break;
                 case Keys.Escape:
-                    if (MouseState == MouseState.SelectedElement)
+                    //if (MouseState == MouseState.SelectedElement)
                     {
                         MouseState = MouseState.Normal;
 
diff --git a/src/Bro.UI.Model.Winform/UI/DockContent/MenuFrmBase.cs b/src/Bro.UI.Model.Winform/UI/DockContent/MenuFrmBase.cs
index c472541..007a6db 100644
--- a/src/Bro.UI.Model.Winform/UI/DockContent/MenuFrmBase.cs
+++ b/src/Bro.UI.Model.Winform/UI/DockContent/MenuFrmBase.cs
@@ -2,6 +2,7 @@
 using Bro.Common.Interface;
 using System;
 using System.Reflection;
+using System.Threading;
 using System.Windows.Forms;
 using WeifenLuo.WinFormsUI.Docking;
 
@@ -23,11 +24,14 @@
                 //if (process != value)
                 {
                     process = value;
+
+                    _constructionDoneHandle.Wait();
                     OnProcessUpdated();
                 }
             }
         }
 
+        ManualResetEventSlim _constructionDoneHandle = new ManualResetEventSlim(false);
         public MenuFrmBase()
         {
             InitializeComponent();
@@ -37,6 +41,8 @@
             {
                 FormClosing += MenuFrmBase_FormClosing;
             }
+
+            _constructionDoneHandle.Set();
         }
 
         protected virtual void MenuFrmBase_FormClosing(object sender, FormClosingEventArgs e)
@@ -75,7 +81,7 @@
         #endregion
 
         #region Login
-        protected bool IsLogin { get; set; }
+        protected virtual bool IsLogin { get; set; }
 
         public virtual void SetLoginStatus(bool isLogin)
         {

--
Gitblit v1.8.0