How to restrict Path (Shape) stroke from zooming in C# WPF Canvas?

Pranav Kulkarni 0 Reputation points
2023-03-17T10:06:48.79+00:00

Hello,

how to restrict Path(Shape) stroke in C# WPF for zoom and pan.

Need to maintain Path stroke in all scale.

Detailed images are attached with query.

Capture1.PNG
Capture2.PNG

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,670 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,232 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
762 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Hui Liu-MSFT 38,191 Reputation points Microsoft Vendor
    2023-03-23T09:14:24.36+00:00

    Hi,@Pranav Kulkarni.

    I encountered an error using your code and was unable to reproduce the problem.

    I did the following workaround, so you could see if it helps you.

    I scaled by setting the StrokeThickness of the path relative to.

      <Window.Resources>
            <local:RectangleConverter x:Key="rectConverter"/>
        </Window.Resources>
        <Grid x:Name="mainGrid" MouseWheel="Canvas_MouseWheel" Width="{Binding Width}" Height="{Binding Height}">
            <Grid.Resources>
                <ScaleTransform x:Key="scale" ScaleX="1.5" ScaleY="1.5"/>
            </Grid.Resources>
    
            <Path Fill="blue" Stroke="red" StrokeThickness="1" x:Name="path" RenderTransform="{StaticResource ResourceKey=scale}">
    
                <Path.Data>
                    <RectangleGeometry>
                        <RectangleGeometry.Rect>
                            <MultiBinding Converter="{StaticResource rectConverter}">
                                <Binding Path="Width" ElementName="mainGrid" />
                                <Binding Path="Height" ElementName="mainGrid"/>
                            </MultiBinding>
                        </RectangleGeometry.Rect>
                    </RectangleGeometry>
                </Path.Data>
            </Path>
        </Grid>
    
    
     public partial class MainWindow : System.Windows.Window
        {
            public double Width { get; set; } = 10;
            public double Height { get; set; } = 10;
            public MainWindow()
            {
                InitializeComponent();
    
                DataContext = this;
    
            }
           
            private Double zoomMax = 5;
            private Double zoomMin = 0.5;
            private Double zoomSpeed = 0.001;
            private Double zoom = 1;
            private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
            {
    
                var transform = mainGrid.Resources["scale"] as ScaleTransform;
    
                zoom += zoomSpeed * e.Delta;
    
                if (zoom < zoomMin) { zoom = zoomMin; }
              
    
                System.Windows.Point mousePos = e.GetPosition(mainGrid);
    
                if (zoom > 1)
                {
                    mainGrid.RenderTransform = new ScaleTransform(zoom, zoom, mousePos.X, mousePos.Y);
                }
                else
                {
                    mainGrid.RenderTransform = new ScaleTransform(zoom, zoom);
                }
    
    
                path.StrokeThickness = 1.0 / zoom;
            }
        }
        public class RectangleConverter : IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                return new Rect(100, 100, (double)values[0]/5, (double)values[1]/5 );
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    
    

    The result:

    2

    -

    If the response is helpful, please click "Accept Answer" and upvote it. Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. Pranav Kulkarni 0 Reputation points
    2023-03-23T11:38:41.4733333+00:00

    Another method I tried from some of the example.
    xaml code

     <local:CanvasBorder x:Name="zoomBorder" Margin="5"  ClipToBounds="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                                   MinHeight="550" MinWidth="650" BorderBrush="Green" SnapsToDevicePixels="True">
                <Canvas Name="runDesign" Margin="5" MinWidth="440" MinHeight="600" Height="Auto" RenderOptions.EdgeMode="Aliased" VerticalAlignment="Bottom" SnapsToDevicePixels="True"/>
            </local:CanvasBorder>
    

    xaml.cs

    public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                DrawRectangle(10, 10, 150, 200, Brushes.Blue, Brushes.Red);
            }
            protected Path DrawRectangle(double x, double y, double width, double height, SolidColorBrush fillColor, Brush strokeColor)
            {
                Path p = new Path();
                GeometryGroup geomGroup = new GeometryGroup();
                RectangleGeometry rectGeom = CreateRectangleGeometry(x, y, width, height);
                geomGroup.Children.Add(rectGeom);
                p.Data = geomGroup;
                p.Fill = fillColor;
                p.Stroke = strokeColor;
                p.StrokeThickness = 1;
                runDesign.Children.Add(p);
                return p;
            }
            protected RectangleGeometry CreateRectangleGeometry(double x, double y, double width, double height)
            {
                RectangleGeometry rectGeom = new RectangleGeometry();
                Rect rct = new Rect();
                rct.X = x;
                rct.Y = y;
                rct.Width = width;
                rct.Height = height;
                rectGeom.Rect = rct;
                return rectGeom;
            }
    
        }
        public class CanvasBorder : Border
        {
            private UIElement _element;
            private Point _iniPoint;
            private Point _prevPoint;
            private Matrix _transMatrix;
            public double ZoomSpeed { get; set; }
            public Action<double, double, double, double> InvalidatedChild { get; set; }
    
            public CanvasBorder() : base()
            {
                _transMatrix = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
                ZoomSpeed = 1.2;
                Focusable = true;
                Background = Brushes.Transparent;
            }
            private void PanAndZoom_Unloaded(object sender, RoutedEventArgs e)
            {
                if (_element != null)
                {
                    Unload();
                }
            }
            public override UIElement Child
            {
                get { return base.Child; }
                set
                {
                    if (value != null && value != _element && _element != null)
                    {
                        Unload();
                    }
    
                    base.Child = value;
    
                    if (value != null && value != _element)
                    {
                        Initialize(value);
                    }
                }
            }
            private void Initialize(UIElement element)
            {
                if (element != null)
                {
                    _element = element;
                    this.Focus();
                    this.PreviewMouseWheel += Border_PreviewMouseWheel;
                    this.PreviewMouseRightButtonDown += Border_PreviewMouseRightButtonDown;
                    this.PreviewMouseRightButtonUp += Border_PreviewMouseRightButtonUp;
                    this.PreviewMouseMove += Border_PreviewMouseMove;
                }
            }
            private void Unload()
            {
                if (_element != null)
                {
                    this.PreviewMouseWheel -= Border_PreviewMouseWheel;
                    this.PreviewMouseRightButtonDown -= Border_PreviewMouseRightButtonDown;
                    this.PreviewMouseRightButtonUp -= Border_PreviewMouseRightButtonUp;
                    this.PreviewMouseMove -= Border_PreviewMouseMove;
                    _element.RenderTransform = null;
                    _element = null;
                }
            }
            private void Border_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
            {
                if (_element != null)
                {
                    Point point = e.GetPosition(_element);
                    ZoomDeltaTo(e.Delta, point);
                }
            }
            private void Border_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
            {
                if (_element != null)
                {
                    Point point = e.GetPosition(_element);
                    StartPan(point);
                    _element.CaptureMouse();
                }
            }
            private void Border_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
            {
                if (_element != null)
                {
                    _element.ReleaseMouseCapture();
                }
            }
            private void Border_PreviewMouseMove(object sender, MouseEventArgs e)
            {
                if (_element != null && _element.IsMouseCaptured)
                {
                    Point point = e.GetPosition(_element);
                    PanTo(point);
                }
            }
            public void Invalidate()
            {
                if (_element != null)
                {
                    this.InvalidatedChild?.Invoke(_transMatrix.M11, _transMatrix.M12, _transMatrix.OffsetX, _transMatrix.OffsetY);
                    _element.RenderTransformOrigin = new Point(0, 0);
                    _element.RenderTransform = new MatrixTransform(_transMatrix);
                    _element.InvalidateVisual();
                }
            }
            public void ZoomTo(double zoom, Point point)
            {
                _transMatrix = ScaleAtPrepend(_transMatrix, zoom, zoom, point.X, point.Y);
                Invalidate();
            }
            public static Matrix ScaleAtPrepend(Matrix matrix, double scaleX, double scaleY, double centerX, double centerY)
            {
                Matrix scale = new Matrix(scaleX, 0, 0, scaleY, centerX - scaleX * centerX, centerY - scaleY * centerY);
                return scale * matrix;
            }
            public void ZoomDeltaTo(int delta, Point point)
            {
                ZoomTo(delta > 0 ? ZoomSpeed : 1 / ZoomSpeed, point);
            }
            public void StartPan(Point point)
            {
                _iniPoint = new Point();
                _prevPoint = new Point(point.X, point.Y);
            }
            public void PanTo(Point point)
            {
                Point delta = new Point(point.X - _prevPoint.X, point.Y - _prevPoint.Y);
                _prevPoint = new Point(point.X, point.Y);
    
                _iniPoint = new Point(_iniPoint.X + delta.X, _iniPoint.Y + delta.Y);
                _transMatrix = TranslatePrepend(_transMatrix, _iniPoint.X, _iniPoint.Y);
    
                Invalidate();
            }
            public static Matrix TranslatePrepend(Matrix matrix, double offsetX, double offsetY)
            {
                Matrix translate = new Matrix(1.0, 0.0, 0.0, 1.0, offsetX, offsetY);
                return translate * matrix;
            }
        }