From db7f62beafa09e14ecbab1146b157d5978088a4d Mon Sep 17 00:00:00 2001
From: xcd <834800634@qq.com>
Date: 星期三, 15 七月 2020 11:14:04 +0800
Subject: [PATCH] M071主界面添加批量添加测量项界面 修复CanvasImage复数显示时无法编辑标签问题

---
 src/Bro.M071.Process/UI/M071_MainForm.cs                        |   22 ++
 src/Bro.UI.Model.Winform/UI/CanvasImage.cs                      |    3 
 src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.resx        |  120 +++++++++++++
 src/Bro.UI.Model.Winform/ElementBase.cs                         |    2 
 src/Bro.M071.Process/UI/M071_MainForm.Designer.cs               |   20 +
 src/Bro.UI.Model.Winform/CommonHelper.cs                        |    7 
 src/Bro.M071.Process/Bro.M071.Process.csproj                    |    9 +
 src/Bro.M071.Process/M071Config.cs                              |    9 
 src/Bro.M071.Process/M071Models.cs                              |   26 ++
 src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.Designer.cs |  133 ++++++++++++++
 src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.cs          |  161 +++++++++++++++++
 11 files changed, 501 insertions(+), 11 deletions(-)

diff --git a/src/Bro.M071.Process/Bro.M071.Process.csproj b/src/Bro.M071.Process/Bro.M071.Process.csproj
index e8c6f29..60f4879 100644
--- a/src/Bro.M071.Process/Bro.M071.Process.csproj
+++ b/src/Bro.M071.Process/Bro.M071.Process.csproj
@@ -131,6 +131,12 @@
     <Compile Include="UI\M071_MainForm.Designer.cs">
       <DependentUpon>M071_MainForm.cs</DependentUpon>
     </Compile>
+    <Compile Include="UI\M071_PatchInsertMeasurement.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="UI\M071_PatchInsertMeasurement.Designer.cs">
+      <DependentUpon>M071_PatchInsertMeasurement.cs</DependentUpon>
+    </Compile>
     <Compile Include="UI\M071_ShortcutFrm.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -152,6 +158,9 @@
     <EmbeddedResource Include="UI\M071_MainForm.resx">
       <DependentUpon>M071_MainForm.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="UI\M071_PatchInsertMeasurement.resx">
+      <DependentUpon>M071_PatchInsertMeasurement.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="UI\M071_ShortcutFrm.resx">
       <DependentUpon>M071_ShortcutFrm.cs</DependentUpon>
     </EmbeddedResource>
diff --git a/src/Bro.M071.Process/M071Config.cs b/src/Bro.M071.Process/M071Config.cs
index fa7ad15..9edb448 100644
--- a/src/Bro.M071.Process/M071Config.cs
+++ b/src/Bro.M071.Process/M071Config.cs
@@ -41,12 +41,19 @@
         public List<KeyResult> KeyResultCollection { get; set; } = new List<KeyResult>();
         #endregion
 
-        [Category("鑳屾櫙鍥剧墖璁剧疆")]
+        [Category("鏄剧ず閰嶇疆")]
         [Description("杩愯鑳屾櫙鍥剧墖璺緞")]
         [Editor(typeof(FileDialogEditor), typeof(UITypeEditor))]
         [DisplayName("鑳屾櫙鍥剧墖")]
         public string BackgroundImagePath { get; set; }
 
+        [Category("鏄剧ず閰嶇疆")]
+        [Description("閿洏鍏抽敭閿綅甯冨眬鏄剧ず")]
+        [TypeConverter(typeof(CollectionCountConvert))]
+        [Editor(typeof(ComplexCollectionEditor<KeyLocation>), typeof(UITypeEditor))]
+        [DisplayName("閿綅鏄剧ず")]
+        public List<KeyLocation> KeyLocationCollection { get; set; } = new List<KeyLocation>();
+
         [Category("妫�娴嬭缃�")]
         [Description("鎷嶆憚鐐逛綅璁剧疆闆嗗悎")]
         [TypeConverter(typeof(CollectionCountConvert))]
diff --git a/src/Bro.M071.Process/M071Models.cs b/src/Bro.M071.Process/M071Models.cs
index beb5008..a56f47c 100644
--- a/src/Bro.M071.Process/M071Models.cs
+++ b/src/Bro.M071.Process/M071Models.cs
@@ -17,6 +17,28 @@
 
 namespace Bro.M071.Process
 {
+    public class KeyLocation : IComplexDisplay
+    {
+        [Category("閿悕閰嶇疆")]
+        [Description("鍗曢敭閿悕")]
+        [TypeConverter(typeof(KeyNameDictConverter))]
+        public string Key { get; set; }
+
+        [Category("浣嶇疆閰嶇疆")]
+        [Description("鍗曢敭鍦ㄩ敭鐩樺钩闈㈠浘涓婄殑浣嶇疆鏄剧ず")]
+        public Rectangle KeyRect { get; set; } = new Rectangle();
+
+        [Category("浣嶇疆閰嶇疆")]
+        [Description("鍗曢敭涔嬪悗闂撮殭浣嶇疆")]
+        public Rectangle IntervalRect { get; set; } = new Rectangle();
+
+        public string GetDisplayText()
+        {
+            return $"{Key}:{KeyRect.X},{KeyRect.Y},{KeyRect.Width},{KeyRect.Height}";
+        }
+    }
+
+
     public class KeyAlgorithem : IComplexDisplay
     {
         [Browsable(false)]
@@ -282,7 +304,7 @@
 
         #region 鏄剧ず
         [Browsable(false)]
-        public Rectangle DisplayLocation { get; set; } = new Rectangle();
+        public Rectangle DisplayLocation { get; set; } = new Rectangle(10, 10, 100, 100);
         #endregion
 
         #region 妫�娴嬬粨鏋�
@@ -391,7 +413,7 @@
             {
                 if (valuePairs == null || valuePairs.Count == 0)
                 {
-                    IsDone = false; 
+                    IsDone = false;
                     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsDone"));
                     return;
                 }
diff --git a/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs b/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
index f72a914..bbe64d2 100644
--- a/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
+++ b/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
@@ -48,6 +48,7 @@
             this.lvMeasures = new System.Windows.Forms.ListView();
             this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
             this.propGridKeyIndicator = new System.Windows.Forms.PropertyGrid();
+            this.tsmiRefreshLabels = new System.Windows.Forms.ToolStripMenuItem();
             this.contextMenuStrip1.SuspendLayout();
             this.plImage.SuspendLayout();
             this.tscEditLocation.ContentPanel.SuspendLayout();
@@ -64,15 +65,16 @@
             this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.tsmiShowToolBar,
             this.tsmiShowStatusBar,
-            this.tsmiShowEditor});
+            this.tsmiShowEditor,
+            this.tsmiRefreshLabels});
             this.contextMenuStrip1.Name = "contextMenuStrip1";
-            this.contextMenuStrip1.Size = new System.Drawing.Size(149, 70);
+            this.contextMenuStrip1.Size = new System.Drawing.Size(181, 114);
             // 
             // tsmiShowToolBar
             // 
             this.tsmiShowToolBar.CheckOnClick = true;
             this.tsmiShowToolBar.Name = "tsmiShowToolBar";
-            this.tsmiShowToolBar.Size = new System.Drawing.Size(148, 22);
+            this.tsmiShowToolBar.Size = new System.Drawing.Size(180, 22);
             this.tsmiShowToolBar.Text = "鏄剧ず宸ュ叿鏉�";
             this.tsmiShowToolBar.CheckedChanged += new System.EventHandler(this.tsmiShowToolBar_CheckedChanged);
             // 
@@ -80,7 +82,7 @@
             // 
             this.tsmiShowStatusBar.CheckOnClick = true;
             this.tsmiShowStatusBar.Name = "tsmiShowStatusBar";
-            this.tsmiShowStatusBar.Size = new System.Drawing.Size(148, 22);
+            this.tsmiShowStatusBar.Size = new System.Drawing.Size(180, 22);
             this.tsmiShowStatusBar.Text = "鏄剧ず鐘舵�佹爮";
             this.tsmiShowStatusBar.CheckedChanged += new System.EventHandler(this.tsmiShowStatusBar_CheckedChanged);
             // 
@@ -88,7 +90,7 @@
             // 
             this.tsmiShowEditor.CheckOnClick = true;
             this.tsmiShowEditor.Name = "tsmiShowEditor";
-            this.tsmiShowEditor.Size = new System.Drawing.Size(148, 22);
+            this.tsmiShowEditor.Size = new System.Drawing.Size(180, 22);
             this.tsmiShowEditor.Text = "缂栬緫鏄剧ず鐐逛綅";
             this.tsmiShowEditor.CheckedChanged += new System.EventHandler(this.tsmiShowEditor_CheckedChanged);
             // 
@@ -291,6 +293,13 @@
             this.propGridKeyIndicator.TabIndex = 0;
             this.propGridKeyIndicator.ToolbarVisible = false;
             // 
+            // tsmiRefreshLabels
+            // 
+            this.tsmiRefreshLabels.Name = "tsmiRefreshLabels";
+            this.tsmiRefreshLabels.Size = new System.Drawing.Size(180, 22);
+            this.tsmiRefreshLabels.Text = "鍒锋柊鏄剧ず鐐逛綅";
+            this.tsmiRefreshLabels.Click += new System.EventHandler(this.tsmiRefreshLabels_Click);
+            // 
             // M071_MainForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -343,5 +352,6 @@
         private System.Windows.Forms.Button btnStartMeasure;
         private System.Windows.Forms.Button btnReset;
         private System.Windows.Forms.ColumnHeader columnHeader1;
+        private System.Windows.Forms.ToolStripMenuItem tsmiRefreshLabels;
     }
 }
\ No newline at end of file
diff --git a/src/Bro.M071.Process/UI/M071_MainForm.cs b/src/Bro.M071.Process/UI/M071_MainForm.cs
index 6aa59b8..3f2ce31 100644
--- a/src/Bro.M071.Process/UI/M071_MainForm.cs
+++ b/src/Bro.M071.Process/UI/M071_MainForm.cs
@@ -136,6 +136,27 @@
             tscEditLocation.Visible = tsmiShowEditor.Checked;
             tscEditLocation.BringToFront();
         }
+
+        private void tsmiRefreshLabels_Click(object sender, EventArgs e)
+        {
+            cvImage.Elements.Clear();
+            lvMeasures.Items.Clear();
+
+            Config.MeasurementUnitCollection.ForEach(u =>
+            {
+                if (!u.IsEnabled)
+                    return;
+
+                var ele = new KeyIndicator(u.Id, u.DisplayLocation);
+                cvImage.Elements.Add(ele);
+
+                ListViewItem item = new ListViewItem(u.GetDisplayText());
+                item.Tag = u.Id;
+                lvMeasures.Items.Add(item);
+            });
+
+            this.Invalidate();
+        }
         #endregion
 
         #region 鏍囩缂栬緫鍖�
@@ -453,5 +474,6 @@
             Process_M071.ResetTimer.Change(-1, -1);
         }
         #endregion
+
     }
 }
diff --git a/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.Designer.cs b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.Designer.cs
new file mode 100644
index 0000000..9a393c9
--- /dev/null
+++ b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.Designer.cs
@@ -0,0 +1,133 @@
+锘縩amespace Bro.M071.Process.UI
+{
+    partial class M071_PatchInsertMeasurement
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.label1 = new System.Windows.Forms.Label();
+            this.cboMeasureType = new System.Windows.Forms.ComboBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.cboStartKey = new System.Windows.Forms.ComboBox();
+            this.label3 = new System.Windows.Forms.Label();
+            this.cboEndKey = new System.Windows.Forms.ComboBox();
+            this.btnInsertMeasurement = new System.Windows.Forms.Button();
+            this.SuspendLayout();
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(22, 14);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(55, 13);
+            this.label1.TabIndex = 0;
+            this.label1.Text = "娴嬮噺绫诲瀷";
+            // 
+            // cboMeasureType
+            // 
+            this.cboMeasureType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cboMeasureType.FormattingEnabled = true;
+            this.cboMeasureType.Location = new System.Drawing.Point(95, 11);
+            this.cboMeasureType.Name = "cboMeasureType";
+            this.cboMeasureType.Size = new System.Drawing.Size(121, 21);
+            this.cboMeasureType.TabIndex = 1;
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(22, 53);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(43, 13);
+            this.label2.TabIndex = 0;
+            this.label2.Text = "璧峰閿�";
+            // 
+            // cboStartKey
+            // 
+            this.cboStartKey.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cboStartKey.FormattingEnabled = true;
+            this.cboStartKey.Location = new System.Drawing.Point(95, 50);
+            this.cboStartKey.Name = "cboStartKey";
+            this.cboStartKey.Size = new System.Drawing.Size(121, 21);
+            this.cboStartKey.TabIndex = 1;
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(22, 92);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(43, 13);
+            this.label3.TabIndex = 0;
+            this.label3.Text = "缁撴潫閿�";
+            // 
+            // cboEndKey
+            // 
+            this.cboEndKey.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cboEndKey.FormattingEnabled = true;
+            this.cboEndKey.Location = new System.Drawing.Point(95, 89);
+            this.cboEndKey.Name = "cboEndKey";
+            this.cboEndKey.Size = new System.Drawing.Size(121, 21);
+            this.cboEndKey.TabIndex = 1;
+            // 
+            // btnInsertMeasurement
+            // 
+            this.btnInsertMeasurement.Location = new System.Drawing.Point(25, 133);
+            this.btnInsertMeasurement.Name = "btnInsertMeasurement";
+            this.btnInsertMeasurement.Size = new System.Drawing.Size(192, 36);
+            this.btnInsertMeasurement.TabIndex = 2;
+            this.btnInsertMeasurement.Text = "鐢熸垚妫�娴嬮」";
+            this.btnInsertMeasurement.UseVisualStyleBackColor = true;
+            this.btnInsertMeasurement.Click += new System.EventHandler(this.btnInsertMeasurement_Click);
+            // 
+            // M071_PatchInsertMeasurement
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(800, 488);
+            this.Controls.Add(this.btnInsertMeasurement);
+            this.Controls.Add(this.cboEndKey);
+            this.Controls.Add(this.cboStartKey);
+            this.Controls.Add(this.cboMeasureType);
+            this.Controls.Add(this.label3);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.label1);
+            this.Name = "M071_PatchInsertMeasurement";
+            this.Text = "M071_PatchInsertMeasurement";
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.ComboBox cboMeasureType;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.ComboBox cboStartKey;
+        private System.Windows.Forms.Label label3;
+        private System.Windows.Forms.ComboBox cboEndKey;
+        private System.Windows.Forms.Button btnInsertMeasurement;
+    }
+}
\ No newline at end of file
diff --git a/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.cs b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.cs
new file mode 100644
index 0000000..59f538a
--- /dev/null
+++ b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.cs
@@ -0,0 +1,161 @@
+锘縰sing Bro.Common.Helper;
+using Bro.UI.Model.Winform;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace Bro.M071.Process.UI
+{
+    [MenuNode("M071_PatchInsertMeasurement", "鎵归噺鐢熸垚妫�娴嬮」", 4, "M071Node", true)]
+    public partial class M071_PatchInsertMeasurement : MenuFrmBase
+    {
+        public M071_PatchInsertMeasurement()
+        {
+            InitializeComponent();
+        }
+
+        M071Config Config => Process?.IConfig as M071Config;
+        M071Process Process_M071 => Process as M071Process;
+
+        public override void OnProcessUpdated()
+        {
+            base.OnProcessUpdated();
+
+            if (Config == null)
+                return;
+
+            this.Invoke(new Action(() =>
+            {
+                UIHelper.SetCombo(cboMeasureType, Config.MeasureTypeCollection, "Code", "Code");
+                UIHelper.SetCombo(cboStartKey, new List<string>(Config.KeyNameCollection), "", "");
+                UIHelper.SetCombo(cboEndKey, new List<string>(Config.KeyNameCollection), "", "");
+            }));
+        }
+
+        private async void btnInsertMeasurement_Click(object sender, EventArgs e)
+        {
+            btnInsertMeasurement.Enabled = false;
+            btnInsertMeasurement.Text = "鐢熸垚涓�傘�傘��";
+
+            string measureType = cboMeasureType.Text;
+            string startKey = cboStartKey.Text;
+            string endKey = cboEndKey.Text;
+
+            await PatchInsertMeasurement(measureType, startKey, endKey).ContinueWith(t =>
+            {
+                MessageBox.Show(t.Result);
+            });
+
+            btnInsertMeasurement.Enabled = true;
+            btnInsertMeasurement.Text = "鐢熸垚妫�娴嬮」";
+        }
+
+        private async Task<string> PatchInsertMeasurement(string measureType, string startKey, string endKey)
+        {
+            return await Task.Run(() =>
+             {
+                 int startIndex = Config.KeyNameCollection.IndexOf(startKey);
+                 int endIndex = Config.KeyNameCollection.IndexOf(endKey);
+
+                 KeyLocation startRect = Config.KeyLocationCollection.FirstOrDefault(u => u.Key == startKey);
+                 //KeyLocation endRect = Config.KeyLocationCollection.FirstOrDefault(u => u.Key == endKey);
+                 if (startRect == null)
+                 {
+                     return $"鏈厤缃畕(startRect == null ? startKey : "")}鐨勬樉绀轰綅缃�";
+                 }
+
+                 if (startIndex > endIndex)
+                 {
+                     return "璧峰閿笉鑳藉皬浜庣粨鏉熼敭";
+                 }
+
+                 switch (measureType)
+                 {
+                     case "Slant":
+                         {
+                             for (int i = startIndex; i <= endIndex; i++)
+                             {
+                                 int curKeyEdge_X = (i - startIndex + 1) * (startRect.KeyRect.Width + startRect.IntervalRect.Width) + startRect.KeyRect.X - startRect.IntervalRect.Width;
+
+                                 int lableX = (curKeyEdge_X + curKeyEdge_X - startRect.KeyRect.Width - 25) / 2;
+                                 int lable_Up_Y = startRect.KeyRect.Y + (startRect.KeyRect.Height - 25) / 2;
+
+                                 MeasurementUnit mUnitUp = new MeasurementUnit();
+                                 mUnitUp.MeasureType = measureType;
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "All" });
+                                 mUnitUp.DisplayLocation = new Rectangle(lableX, lable_Up_Y, 25, 25);
+                                 Config.MeasurementUnitCollection.Add(mUnitUp);
+                             }
+                         }
+                         break;
+                     case "Alignment":
+                         {
+                             for (int i = startIndex; i < endIndex; i++)
+                             {
+                                 int curKeyEdge_X = (i - startIndex + 1) * (startRect.KeyRect.Width + startRect.IntervalRect.Width) + startRect.KeyRect.X - startRect.IntervalRect.Width;
+                                 int nextKeyStart_X = curKeyEdge_X + startRect.IntervalRect.Width;
+
+                                 int lableX = (curKeyEdge_X + nextKeyStart_X - 25) / 2;
+                                 int lable_Up_Y = startRect.KeyRect.Y + 4;
+                                 int lable_Down_y = startRect.KeyRect.Y + startRect.KeyRect.Height - 4 - 12;
+
+                                 MeasurementUnit mUnitUp = new MeasurementUnit();
+                                 mUnitUp.MeasureType = measureType;
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "Z2" });
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i + 1], KeyResultId = "Z1" });
+                                 mUnitUp.DisplayLocation = new Rectangle(lableX, lable_Up_Y, 25, 12);
+                                 Config.MeasurementUnitCollection.Add(mUnitUp);
+
+                                 MeasurementUnit mUnitDown = new MeasurementUnit();
+                                 mUnitDown.MeasureType = measureType;
+                                 mUnitDown.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "Z4" });
+                                 mUnitDown.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i + 1], KeyResultId = "Z3" });
+                                 mUnitDown.DisplayLocation = new Rectangle(lableX, lable_Down_y, 25, 12);
+                                 Config.MeasurementUnitCollection.Add(mUnitDown);
+                             }
+                         }
+                         break;
+                     case "RowAlignment":
+                         {
+                             int start_X = startRect.KeyRect.X + startRect.KeyRect.Width / 3;
+                             int end_X = (startRect.KeyRect.Width + startRect.IntervalRect.Width) * (endIndex - startIndex) + startRect.KeyRect.X + startRect.KeyRect.Width * 2 / 3;
+
+                             int lable_Up_Y = startRect.KeyRect.Y - 6;
+                             int lable_Down_Y = startRect.KeyRect.Y + startRect.KeyRect.Height - 6;
+
+                             MeasurementUnit mUnitUp = new MeasurementUnit();
+                             mUnitUp.MeasureType = measureType;
+                             for (int i = startIndex; i < endIndex; i++)
+                             {
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "Z2" });
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i + 1], KeyResultId = "Z1" });
+                             }
+
+                             mUnitUp.DisplayLocation = new Rectangle(start_X, lable_Up_Y, end_X - start_X, 12);
+                             Config.MeasurementUnitCollection.Add(mUnitUp);
+
+                             MeasurementUnit mUnitDown = new MeasurementUnit();
+                             mUnitDown.MeasureType = measureType;
+                             for (int i = startIndex; i < endIndex; i++)
+                             {
+                                 mUnitDown.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "Z4" });
+                                 mUnitDown.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i + 1], KeyResultId = "Z3" });
+                             }
+
+                             mUnitDown.DisplayLocation = new Rectangle(start_X, lable_Down_Y, end_X - start_X, 12);
+                             Config.MeasurementUnitCollection.Add(mUnitDown);
+                         }
+                         break;
+                 }
+
+                 return "宸叉壒閲忕敓鎴愭娴嬮」锛岃鍒伴厤缃晫闈㈡煡鐪嬪拰淇濆瓨";
+             });
+        }
+    }
+}
diff --git a/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.resx b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.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.UI.Model.Winform/CommonHelper.cs b/src/Bro.UI.Model.Winform/CommonHelper.cs
index ad96dbc..386a179 100644
--- a/src/Bro.UI.Model.Winform/CommonHelper.cs
+++ b/src/Bro.UI.Model.Winform/CommonHelper.cs
@@ -60,7 +60,12 @@
         /// 1st MouseState 鍒濆鐘舵��
         /// 2nd MouseState 鍙樺寲鐘舵��
         /// </summary>
-        public static Action<ElementBase, ElementState, ElementState> ChangeElementsMouseState;
+        public static event Action<ElementBase, ElementState, ElementState> ChangeElementsMouseState;
+
+        public static void TriggerElementsMouseStateChanged(ElementBase ele, ElementState preState, ElementState curState)
+        {
+            ChangeElementsMouseState?.Invoke(ele, preState, curState);
+        }
     }
 
     public class NoticedPoints : List<Point>
diff --git a/src/Bro.UI.Model.Winform/ElementBase.cs b/src/Bro.UI.Model.Winform/ElementBase.cs
index c0feb9d..669a378 100644
--- a/src/Bro.UI.Model.Winform/ElementBase.cs
+++ b/src/Bro.UI.Model.Winform/ElementBase.cs
@@ -191,7 +191,7 @@
                 {
                     ElementState preState = state;
                     Set(ref state, value);
-                    EventRouter.ChangeElementsMouseState?.Invoke(this, preState, state);
+                    EventRouter.TriggerElementsMouseStateChanged(this, preState, state);
                     switch (state)
                     {
                         case ElementState.MouseHover:
diff --git a/src/Bro.UI.Model.Winform/UI/CanvasImage.cs b/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
index 3bd6941..76beeac 100644
--- a/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
+++ b/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
@@ -31,7 +31,8 @@
             MouseMove += Canvas_MouseMove;
             MouseUp += Canvas_MouseUp;
 
-            EventRouter.ChangeElementsMouseState = OnElementChangeMouseState;
+            EventRouter.ChangeElementsMouseState -= OnElementChangeMouseState;
+            EventRouter.ChangeElementsMouseState += OnElementChangeMouseState;
         }
 
         #region Event

--
Gitblit v1.8.0