惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
H
Hackread – Cybersecurity News, Data Breaches, AI and More
WordPress大学
WordPress大学
博客园_首页
美团技术团队
H
Help Net Security
MongoDB | Blog
MongoDB | Blog
宝玉的分享
宝玉的分享
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
J
Java Code Geeks
A
About on SuperTechFans
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
IT之家
IT之家
T
The Blog of Author Tim Ferriss
Microsoft Azure Blog
Microsoft Azure Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog
雷峰网
雷峰网
爱范儿
爱范儿

博客园 - HotSky

WPF游标直尺 Chrome启用CDP(chrome-devtools-protocol)进行远程操控 WPF 让ScrollViewer支持按住鼠标中键拖拽滚动内容 Nodejs使用 nodejs中写sql需要用in时的写法 C#城市最短路径 C#Animation Sqlite PDF附录A: 内容流操作码 WPF FPS类 生命模拟 C# Sql帮助类,可扩展 WPF WriteableBitmap通过GDI+绘制帮助类 Alpha混合 Bmp读写二值图 WPF支持任意快捷键+鼠标组合的绑定类 WPF阻止窗体被系统缩放,使用显示器DPI WPF DataGrid自动增长序号列 C#访问或修改私有类、函数、变量、属性 WPF一个简单的属性编辑控件
WPF 让ScrollViewer支持鼠标中键滚动缩放内容
HotSky · 2026-01-27 · via 博客园 - HotSky
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}");
}