ソースを参照

优化DXF缩放体验及PLC订阅健壮性提升

- XYZU_Robot.cs 仅在PLC连接时订阅轴位置节点,并增加异常捕获,提升健壮性
- DxfView.xaml 调整按钮区域Margin,界面更美观
- DxfView.xaml.cs 引入动画,重构缩放逻辑,支持平滑缩放、锚点居中、鼠标滚轮缩放及适应屏幕,极大提升DXF预览交互体验
孝锋 徐 8 ヶ月 前
コミット
7101bfa416

+ 27 - 18
TeamAAS-VM/Core/Robots/XYZU_Robot.cs

@@ -88,30 +88,39 @@ namespace TeamAAS_VP.Core.Robots
                 Brand = RobotBrand.XYZU_Platform;
             }
 
-            //所有轴的位置节点
-            
-            var nodeIds = Axes.Where(a => !string.IsNullOrWhiteSpace(a.State.ActPositionNode)).Select(a => a.State.ActPositionNode).ToArray();
-            List<string> positionNodes = new List<string>(nodeIds);
-            pLC.SubscribeNodes("AxisPosition",positionNodes, (res) =>
+            if (Plc.IsConnected)
             {
-                if (res.key!= "AxisPosition") return;
-
-                for (int i = 0; i < Axes.Count; i++)
+                try
                 {
-                    var axis = Axes[i];
-                    if (axis.State.ActPositionNode.Contains(res.nodeId))
+                    //所有轴的位置节点
+                    var nodeIds = Axes.Where(a => !string.IsNullOrWhiteSpace(a.State.ActPositionNode)).Select(a => a.State.ActPositionNode).ToArray();
+                    List<string> positionNodes = new List<string>(nodeIds);
+                    pLC.SubscribeNodes("AxisPosition", positionNodes, (res) =>
                     {
-                        float v = Convert.ToSingle(res.value);
-                        switch (axis.Name)
+                        if (res.key != "AxisPosition") return;
+
+                        for (int i = 0; i < Axes.Count; i++)
                         {
-                            case "X": CurrentPosition.X = v; break;
-                            case "Y": CurrentPosition.Y = v; break;
-                            case "Z": CurrentPosition.Z = v; break;
-                            case "U": CurrentPosition.U = v; break;
+                            var axis = Axes[i];
+                            if (axis.State.ActPositionNode.Contains(res.nodeId))
+                            {
+                                float v = Convert.ToSingle(res.value);
+                                switch (axis.Name)
+                                {
+                                    case "X": CurrentPosition.X = v; break;
+                                    case "Y": CurrentPosition.Y = v; break;
+                                    case "Z": CurrentPosition.Z = v; break;
+                                    case "U": CurrentPosition.U = v; break;
+                                }
+                            }
                         }
-                    }
+                    });
                 }
-            });
+                catch (Exception)
+                {
+
+                }
+            }
         }
 
         private void Plc_ConnectChangedEvent(object arg1, bool arg2)

+ 1 - 1
TeamAAS-VM/DxfModule/DxfView.xaml

@@ -157,7 +157,7 @@
         <StackPanel Grid.Row="4"
                     Orientation="Horizontal"
                     HorizontalAlignment="Right"
-                    Margin="0,18,0,0">
+                    Margin="5">
             <Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
                     CommandParameter="{Binding}"
                     IsDefault="True"

+ 178 - 6
TeamAAS-VM/DxfModule/DxfView.xaml.cs

@@ -11,6 +11,7 @@ using System.Windows.Data;
 using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
+using System.Windows.Media.Animation;
 using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
 using System.Windows.Shapes;
@@ -302,25 +303,196 @@ namespace TeamAAS_VP.DxfModule
 
         private void BtnZoomIn_Click(object sender, RoutedEventArgs e)
         {
-            Zoom(_zoomFactor);
+            // Smooth zoom centered at viewport center
+            var sv = ScrollViewer;
+            if (sv != null)
+            {
+                double prevScale = CanvasScaleTransform.ScaleX;
+                double target = prevScale * _zoomFactor;
+                target = Math.Max(0.05, Math.Min(target, 50.0));
+                AnimateScale(target, null);
+            }
+            else
+            {
+                Zoom(_zoomFactor);
+            }
         }
 
         private void BtnZoomOut_Click(object sender, RoutedEventArgs e)
         {
-            Zoom(1.0 / _zoomFactor);
+            var sv = ScrollViewer;
+            if (sv != null)
+            {
+                double prevScale = CanvasScaleTransform.ScaleX;
+                double target = prevScale / _zoomFactor;
+                target = Math.Max(0.05, Math.Min(target, 50.0));
+                AnimateScale(target, null);
+            }
+            else
+            {
+                Zoom(1.0 / _zoomFactor);
+            }
         }
 
         private void BtnFitToScreen_Click(object sender, RoutedEventArgs e)
         {
-            CanvasScaleTransform.ScaleX = 1.0;
-            CanvasScaleTransform.ScaleY = 1.0;
-            UpdateZoomDisplay();
+            var sv = ScrollViewer;
+            if (sv == null)
+            {
+                CanvasScaleTransform.ScaleX = 1.0;
+                CanvasScaleTransform.ScaleY = 1.0;
+                UpdateZoomDisplay();
+                return;
+            }
+
+            // Compute scale to fit the canvas into viewport with some margin
+            double marginFactor = 0.95;
+            double targetScaleX = (sv.ViewportWidth * marginFactor) / DrawingCanvas.Width;
+            double targetScaleY = (sv.ViewportHeight * marginFactor) / DrawingCanvas.Height;
+            double target = Math.Min(targetScaleX, targetScaleY);
+            if (double.IsNaN(target) || target <= 0)
+                target = 1.0;
+
+            target = Math.Max(0.05, Math.Min(target, 50.0));
+
+            // Animate to target and center
+            AnimateScale(target, null);
         }
 
         private void DrawingCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
         {
+            // Zoom centered at current mouse position inside the ScrollViewer viewport
+            var sv = ScrollViewer;
+            if (sv == null) // fallback to previous behavior
+            {
+                double zoom1 = e.Delta > 0 ? _zoomFactor : 1.0 / _zoomFactor;
+                Zoom(zoom1);
+                return;
+            }
+
+            // Mouse position relative to the ScrollViewer (viewport)
+            System.Windows.Point mousePosInViewport = e.GetPosition(sv);
+            // Mouse position relative to the content (unscaled content coordinates)
+            System.Windows.Point mousePosInContent = e.GetPosition(DrawingCanvas);
+
+            double prevScale = CanvasScaleTransform.ScaleX;
             double zoom = e.Delta > 0 ? _zoomFactor : 1.0 / _zoomFactor;
-            Zoom(zoom);
+            double newScale = prevScale * zoom;
+
+            // Clamp scale to reasonable range
+            newScale = Math.Max(0.05, Math.Min(newScale, 50.0));
+
+            // Absolute position of the content point after scaling
+            double absX = mousePosInContent.X * newScale;
+            double absY = mousePosInContent.Y * newScale;
+
+            // Apply scale
+            CanvasScaleTransform.ScaleX = newScale;
+            CanvasScaleTransform.ScaleY = newScale;
+
+            // Ensure layout updated so ScrollViewer extents are refreshed
+            sv.UpdateLayout();
+
+            // Calculate target offsets so that the content point stays under the mouse cursor
+            double targetOffsetX = absX - mousePosInViewport.X;
+            double targetOffsetY = absY - mousePosInViewport.Y;
+
+            // Clamp offsets to valid scrollable range
+            double maxOffsetX = Math.Max(0, sv.ExtentWidth - sv.ViewportWidth);
+            double maxOffsetY = Math.Max(0, sv.ExtentHeight - sv.ViewportHeight);
+
+            targetOffsetX = Math.Max(0, Math.Min(targetOffsetX, maxOffsetX));
+            targetOffsetY = Math.Max(0, Math.Min(targetOffsetY, maxOffsetY));
+
+            sv.ScrollToHorizontalOffset(targetOffsetX);
+            sv.ScrollToVerticalOffset(targetOffsetY);
+
+            UpdateZoomDisplay();
+            e.Handled = true;
+        }
+
+        /// <summary>
+        /// Animate scale transform to targetScale. If anchorInContent is null, use viewport center as anchor.
+        /// anchorInContent, when provided, is in unscaled canvas coordinates (content space).
+        /// </summary>
+        private void AnimateScale(double targetScale, System.Windows.Point? anchorInContent)
+        {
+            var sv = ScrollViewer;
+            if (sv == null)
+            {
+                CanvasScaleTransform.ScaleX = targetScale;
+                CanvasScaleTransform.ScaleY = targetScale;
+                UpdateZoomDisplay();
+                return;
+            }
+
+            double prevScale = CanvasScaleTransform.ScaleX;
+            if (Math.Abs(prevScale - targetScale) < 1e-6)
+                return;
+
+            // Determine anchor in content space and its position in viewport
+            System.Windows.Point anchorContent;
+            System.Windows.Point anchorViewport;
+
+            if (anchorInContent.HasValue)
+            {
+                anchorContent = anchorInContent.Value;
+                anchorViewport = new System.Windows.Point(anchorContent.X * prevScale - sv.HorizontalOffset, anchorContent.Y * prevScale - sv.VerticalOffset);
+            }
+            else
+            {
+                // use viewport center
+                anchorViewport = new System.Windows.Point(sv.ViewportWidth / 2.0, sv.ViewportHeight / 2.0);
+                anchorContent = new System.Windows.Point((sv.HorizontalOffset + anchorViewport.X) / prevScale, (sv.VerticalOffset + anchorViewport.Y) / prevScale);
+            }
+
+            // Calculate target offsets so that the anchor stays in the same viewport position after scaling
+            double targetOffsetX = anchorContent.X * targetScale - anchorViewport.X;
+            double targetOffsetY = anchorContent.Y * targetScale - anchorViewport.Y;
+
+            double maxOffsetX = Math.Max(0, sv.ExtentWidth - sv.ViewportWidth);
+            double maxOffsetY = Math.Max(0, sv.ExtentHeight - sv.ViewportHeight);
+
+            targetOffsetX = Math.Max(0, Math.Min(targetOffsetX, maxOffsetX));
+            targetOffsetY = Math.Max(0, Math.Min(targetOffsetY, maxOffsetY));
+
+            var duration = TimeSpan.FromMilliseconds(200);
+
+            var animX = new DoubleAnimation(prevScale, targetScale, duration, FillBehavior.Stop) { EasingFunction = new QuadraticEase() };
+            var animY = new DoubleAnimation(prevScale, targetScale, duration, FillBehavior.Stop) { EasingFunction = new QuadraticEase() };
+
+            int completed = 0;
+            EventHandler whenDone = (s, e) =>
+            {
+                completed++;
+                if (completed >= 2)
+                {
+                    // Ensure final values
+                    CanvasScaleTransform.ScaleX = targetScale;
+                    CanvasScaleTransform.ScaleY = targetScale;
+
+                    // Update layout so extents reflect final scale
+                    sv.UpdateLayout();
+
+                    // Recalculate clamped offsets based on final extents
+                    double maxX = Math.Max(0, sv.ExtentWidth - sv.ViewportWidth);
+                    double maxY = Math.Max(0, sv.ExtentHeight - sv.ViewportHeight);
+
+                    double finalOffsetX = Math.Max(0, Math.Min(targetOffsetX, maxX));
+                    double finalOffsetY = Math.Max(0, Math.Min(targetOffsetY, maxY));
+
+                    sv.ScrollToHorizontalOffset(finalOffsetX);
+                    sv.ScrollToVerticalOffset(finalOffsetY);
+
+                    UpdateZoomDisplay();
+                }
+            };
+
+            animX.Completed += whenDone;
+            animY.Completed += whenDone;
+
+            CanvasScaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, animX);
+            CanvasScaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, animY);
         }
 
         private void Zoom(double factor)