X3

RedSky

导航

WPF 让ScrollViewer支持鼠标中键滚动缩放内容

public class ScrollViewerMouseWheelScaleBehavior : Behavior<ScrollViewer>
{
    public static readonly DependencyProperty MaxScaleProperty = DependencyProperty.Register("MaxScale", typeof(double), typeof(ScrollViewerMouseWheelScaleBehavior), new PropertyMetadata(10.0));
    public static readonly DependencyProperty MinScaleProperty = DependencyProperty.Register("MinScale", typeof(double), typeof(ScrollViewerMouseWheelScaleBehavior), new PropertyMetadata(0.1d));
    public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale", typeof(double), typeof(ScrollViewerMouseWheelScaleBehavior), new FrameworkPropertyMetadata(1.0));
    
    public double MaxScale
    {
        get { return (double)GetValue(MaxScaleProperty); }
        set { SetValue(MaxScaleProperty, value); }
    }

    public double MinScale
    {
        get { return (double)GetValue(MinScaleProperty); }
        set { SetValue(MinScaleProperty, value); }
    }

    public double Scale
    {
        get { return (double)GetValue(ScaleProperty); }
        set { SetValue(ScaleProperty, value); }
    }


    public EventHandler<ScaleChangedEventArgs> ScaleChanged { get; set; }

    ScrollViewer scrollviewer;
    FrameworkElement Content;

    public ScrollViewerMouseWheelScaleBehavior() { }
    public ScrollViewerMouseWheelScaleBehavior(double scale, EventHandler<ScaleChangedEventArgs> scaleChanged)
    {
        Scale = scale;
        this.ScaleChanged = scaleChanged;
    }
    protected override void OnAttached()
    {
        base.OnAttached();
        scrollviewer = AssociatedObject;
        scrollviewer.PreviewMouseWheel += Target_PreviewMouseWheel;
        scrollviewer.PreviewKeyDown += Scrollviewer_PreviewKeyDown;
        Content = scrollviewer.Content as FrameworkElement;
    }

    private void Scrollviewer_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.NumPad0)
        {
            // 當 Ctrl + NumPad0 被按下時,重置縮放比例
            OnScaleChanged(1.0);
            e.Handled = true; // 阻止事件的傳播
        }
    }

    private void OnScaleChanged(double newScale)
    {
        newScale = Math.Max(MinScale, Math.Min(newScale, MaxScale));
        if (newScale == this.Scale) return;
        double oldScale = this.Scale;
        this.Scale = newScale;
        if (Content != null)
        {
            double oldWidth = Content.Width;
            double oldHeight = Content.Height;
            Content.Width = Content.Width / oldScale * newScale;
            Content.Height = Content.Height / oldScale * newScale;

            Point mousePos = Mouse.GetPosition(Content);
            double scrollh = scrollviewer.HorizontalOffset + (Content.Width - oldWidth) * mousePos.X / oldWidth;
            double scrollv = scrollviewer.VerticalOffset + (Content.Height - oldHeight) * mousePos.Y / oldHeight;
            if (scrollh > 0)
                scrollviewer.ScrollToHorizontalOffset(scrollh);
            if (scrollv > 0)
                scrollviewer.ScrollToVerticalOffset(scrollv);
            ScaleChanged?.Invoke(scrollviewer, new ScaleChangedEventArgs(oldScale, newScale));
        }
    }

    private void Target_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        if (Keyboard.IsKeyDown(Key.LeftCtrl))
        {
            double newScale = this.Scale;
            // 使用 Ctrl 鍵時縮放
            if (e.Delta > 0)
                newScale += 0.01; // 放大
            else
                newScale -= 0.01; // 縮小
                               // 限制縮放比例在一定範圍內
            newScale = Math.Max(MinScale, Math.Min(newScale, MaxScale));
            OnScaleChanged(newScale);
            e.Handled = true; // 阻止滾輪事件的傳播
        }
    }
}
public class ScaleChangedEventArgs : EventArgs
{
    public double OldScale { get; private set; }
    public double NewScale { get; private set; }
    public ScaleChangedEventArgs(double oldScale, double newScale)
    {
        OldScale = oldScale;
        NewScale = newScale;
    }
}

使用案例:

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    Interaction.GetBehaviors(scrollviewer).Add(new ScrollViewerMouseWheelScaleBehavior(scale, OnMesureScale));
}

void MesureScale(object sender, ScaleChangedEventArgs args)
{
    double oldscale = args.OldScale;
    double newScale = args.NewScale;
    //foreach (FrameworkElement child in paintAreaCanvas.Children)
    //{
    //    if (child == null || child.DataContext == null) continue;
    //    var ele = child.DataContext as PaintElement;
    //    if (ele == null) continue;
    //    child.SetValue(TextElement.FontSizeProperty, ele.FontSize * newScale); // 調整字體大小
    //    child.SetValue(Canvas.LeftProperty, ele.X * newScale);
    //    child.SetValue(Canvas.TopProperty, ele.Y * newScale);
    //    child.SetValue(FrameworkElement.WidthProperty, ele.Width * newScale);
    //    child.SetValue(FrameworkElement.HeightProperty, ele.Height * newScale);
    //}
    Console.WriteLine($"縮放比例: {scale:P0}");
}

 

posted on 2026-01-27 14:54  HotSky  阅读(5)  评论(0)    收藏  举报