Search code examples
wpfcanvasdeepzoom

WPF Zoom without Canvas size change


I am using Framework version 4.0,

My problem when Zoom, Canvas should not re-size. or children only zoom IN / OUT?

please suggest me.

Thanks


Solution

  • I have done this before, the solution I found was when ever the zoom-in, make zoom out to the items I want keep the same size.

    So, the zoom is a scale transform, then always when the scale transform in the container zoom item increase, you need to apply a decreasing transform to the items (the items you want keep the size). Here I have a sample code of an attached property that you can use later in xaml code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Media;
    using Cepha.View.Converter;
    using Cepha.View.Util;
    using Microsoft.Practices.ServiceLocation;
    using WPFExtensions.Controls;
    
    namespace Cepha.View.AttachedProperty
    {
        public static class KeepSizeOnZoomBehavior
        {
            #region KeppSizeOnZoom
    
        public static bool GetKeppSizeOnZoom(DependencyObject obj)
        {
            return (bool) obj.GetValue(KeppSizeOnZoomProperty);
        }
    
        public static void SetKeppSizeOnZoom(DependencyObject obj, bool value)
        {
            obj.SetValue(KeppSizeOnZoomProperty, value);
        }
    
        // Using a DependencyProperty as the backing store for KeppSizeOnZoom.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty KeppSizeOnZoomProperty =
            DependencyProperty.RegisterAttached("KeppSizeOnZoom", typeof (bool), typeof (KeepSizeOnZoomBehavior),
                                                new PropertyMetadata(false, OnKeepSizeOnZoomPropertyChanged));
    
        private static void OnKeepSizeOnZoomPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var uiElement = d as UIElement;
            if (uiElement == null)
                return;
    
            if ((bool)e.NewValue)
            {
    
                var zoomContentPresenter = ViewUtils.GetParent(d, p => p is ZoomContentPresenter) as ZoomContentPresenter;
                if (zoomContentPresenter == null)
                    return;
                if (zoomContentPresenter.RenderTransform == null || !(zoomContentPresenter.RenderTransform is TransformGroup))
                    return;
    
                var sourceScaleTransform =
                    (zoomContentPresenter.RenderTransform as TransformGroup).Children.FirstOrDefault(
                        c => c is ScaleTransform) as ScaleTransform;
    
                if (sourceScaleTransform == null)
                    return;
    
    
    
                if (uiElement.RenderTransform == null || !(uiElement.RenderTransform is TransformGroup))
                {
                    uiElement.RenderTransform = new TransformGroup();
                }
                var scaleTransform =
                    (uiElement.RenderTransform as TransformGroup).Children.FirstOrDefault(c => c is ScaleTransform) as
                    ScaleTransform;
    
    
                var inverseConverter = ServiceLocator.Current.GetInstance<InverseConverter>();
    
                if (scaleTransform == null)
                {
                    scaleTransform =
                        new ScaleTransform(
                            (double) inverseConverter.Convert(sourceScaleTransform.ScaleX, typeof (double), null, null),
                            (double) inverseConverter.Convert(sourceScaleTransform.ScaleY, typeof (double), null, null), 0,
                            0);
                    (uiElement.RenderTransform as TransformGroup).Children.Add(scaleTransform);
                }
    
                BindingOperations.SetBinding(scaleTransform, ScaleTransform.ScaleXProperty,
                                             new Binding("ScaleX")
                                                 {Source = sourceScaleTransform, Converter = inverseConverter});
                BindingOperations.SetBinding(scaleTransform, ScaleTransform.ScaleYProperty,
                                             new Binding("ScaleY")
                                                 {Source = sourceScaleTransform, Converter = inverseConverter});
                if (d is FrameworkElement)
                {
                    (d as FrameworkElement).Unloaded += OnElementUnloaded;
                }
            }
            else
            {
                ClearScaleYXBinding(uiElement);
            }
        }
    
        private static void OnElementUnloaded(object sender, RoutedEventArgs e)
        {
            var uiElement = sender as UIElement;
            if (uiElement == null)
                return;
            ClearScaleYXBinding(uiElement);
            ((FrameworkElement) sender).Unloaded -= OnElementUnloaded;
        }
    
        private static void ClearScaleYXBinding(UIElement uiElement)
        {
            if (!(uiElement.RenderTransform is TransformGroup))
                return;
            var scaleTransform =
                (uiElement.RenderTransform as TransformGroup).Children.FirstOrDefault(c => c is ScaleTransform) as
                ScaleTransform;
            if (scaleTransform == null)
                return;
            BindingOperations.ClearBinding(scaleTransform, ScaleTransform.ScaleXProperty);
            BindingOperations.ClearBinding(scaleTransform, ScaleTransform.ScaleYProperty);
        }
    
        #endregion
    }
    }
    

    The inverse converter:

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Windows.Data;
    
    namespace Cepha.View.Converter
    {
        public class InverseConverter:IValueConverter
        {
            #region Implementation of IValueConverter
    
        /// <summary>
        /// Converts a value. 
        /// </summary>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        /// <param name="value">The value produced by the binding source.</param><param name="targetType">The type of the binding target property.</param><param name="parameter">The converter parameter to use.</param><param name="culture">The culture to use in the converter.</param>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double)
                return 1/(double) value;
            return 1/(float) value;
        }
    
        /// <summary>
        /// Converts a value. 
        /// </summary>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        /// <param name="value">The value that is produced by the binding target.</param><param name="targetType">The type to convert to.</param><param name="parameter">The converter parameter to use.</param><param name="culture">The culture to use in the converter.</param>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double)
                return 1 / (double)value;
            return 1 / (float)value;
        }
    
        #endregion
    }
    }
    

    You can use this in styles:

    <Style x:Key="PointListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="Transparent"/>
            ...
            <Setter Property="AttachedProperty:KeepSizeOnZoomBehavior.KeppSizeOnZoom" Value="True"/>
    ...
    

    Or you can use it directly on visual items:

    <Buttom AttachedProperty:KeepSizeOnZoomBehavior.KeppSizeOnZoom="True" .../>
    

    Try this, maybe helps you...


    EDIT


    The ViewUtil is a simple static class for helping in some manage things, here is the code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Media;
    
    namespace Cepha.View.Util
    {
        public static class ViewUtils
        {
            public static bool AnyParent(DependencyObject item, Func<DependencyObject, bool> condition)
            {
                if (item == null)
                    return false;
    
                var logicalParent = LogicalTreeHelper.GetParent(item);
                var visualParent = VisualTreeHelper.GetParent(item);
    
                return condition(item) || AnyParent(visualParent, condition);
            }
    
            public static DependencyObject GetParent(DependencyObject item, Func<DependencyObject, bool> condition)
            {
                if (item == null)
                    return null;
    
                var logicalParent = LogicalTreeHelper.GetParent(item);
                var visualParent = VisualTreeHelper.GetParent(item);
    
                return condition(item) ? item : GetParent(visualParent, condition);
            }
    
            public static DependencyObject GetVisualChild(DependencyObject item, Func<DependencyObject, bool> condition)
            {
                if (item == null)
                    return null;
    
                var q = new Queue<DependencyObject>();
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(item); i++)
                {
                    var t = VisualTreeHelper.GetChild(item, i);
                    if (condition(t))
                        return t;
                    q.Enqueue(t);
                }
    
                while (q.Count > 0)
                {
                    var subchild = q.Dequeue();
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(subchild); i++)
                    {
                        var t = VisualTreeHelper.GetChild(subchild, i);
                        if (condition(t))
                            return t;
                        q.Enqueue(t);
                    }
                }
                return null;
            }
    
            public static DependencyObject GetLogicalChild(DependencyObject item, Func<DependencyObject, bool> condition)
            {
                if (item == null)
                    return null;
    
                var q = new Queue<DependencyObject>();
                foreach (var w in LogicalTreeHelper.GetChildren(item))
                {
                    var t = w as DependencyObject;
                    if (condition(t))
                        return t;
                    q.Enqueue(t);
                }
    
                while (q.Count > 0)
                {
                    var subchild = q.Dequeue();
                    foreach (var w in LogicalTreeHelper.GetChildren(subchild))
                    {
                        var t = w as DependencyObject;
                        if (condition(t))
                            return t;
                        q.Enqueue(t);
                    }
                }
                return null;
            }
        }
    }