WPF 自定义放大镜控件

控件名:Magnifier

作   者:WPFDevelopersOrg - 驚鏵

原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers

  • 框架使用.NET40

  • Visual Studio 2019;

  • 实现此功能需要用到 VisualBrush ,放大镜展现使用 Canvas -> Ellipse .

    • 可以使用 VisualBrush 创建放大效果。

    • 设置 Visual 获取或设置画笔的内容。

    • 设置 ViewboxUnits Absolute 坐标系与边界框无关。

    • 设置 Viewbox 获取或设置 TileBrush 图块中内容的位置和尺寸。

  • 当鼠标移动获取当前坐标点修改 VisualBrushViewbox

  • 鼠标移动修改 EllipseCanvas.LeftCanvas.Top 跟随鼠标。

  • 接着上一篇把放大镜做成控件方便使用。

  • 新建Magnifier获取父控件,为父控件创建装饰器把Magnifier添加到装饰器Child

  • 需注意当鼠标移动获取在父控件上获取 Visual 的偏移量 VisualTreeHelper.GetOffset

cf0231884abbb30c9b33bef10f804f58.png

1) Magnifier.xaml 代码如下:

<Style TargetType="{x:Type controls:Magnifier}" BasedOn="{StaticResource ControlBasicStyle}">
        <Setter Property="HorizontalAlignment" Value="Left"/>
        <Setter Property="VerticalAlignment" Value="Top"/>
        <Setter Property="IsHitTestVisible" Value="False" />
        <Setter Property="Width" Value="200"/>
        <Setter Property="Height" Value="200"/>
        <Setter Property="BorderThickness" Value="8"/>
        <Setter Property="BorderBrush" Value="{DynamicResource PrimaryNormalSolidColorBrush}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:Magnifier}">
                    <Canvas Name="PART_Canvas">
                        <Border
                            x:Name="PART_Border"
                            BorderBrush="{TemplateBinding BorderBrush}" 
                            BorderThickness="{TemplateBinding BorderThickness}" 
                            Background="{TemplateBinding Background}"
                            Height="{TemplateBinding Height}" 
                            Width="{TemplateBinding Height}"
                            CornerRadius="{TemplateBinding CornerRadius}">
                            <Ellipse>
                                <Ellipse.Fill>
                                    <VisualBrush 
                                            x:Name="PART_VisualBrush"
                                            Visual="{Binding ParentTarget,RelativeSource={RelativeSource TemplatedParent}}" 
                                            ViewboxUnits="Absolute" />
                                </Ellipse.Fill>
                            </Ellipse>
                        </Border>
                    </Canvas>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

2) Magnifier.xaml.cs 代码如下:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using WPFDevelopers.Utilities;

namespace WPFDevelopers.Controls
{
    [TemplatePart(Name = BorderTemplateName, Type = typeof(Border))]
    [TemplatePart(Name = VisualBrushTemplateName, Type = typeof(VisualBrush))]
    public class Magnifier : Control
    {
        private const string BorderTemplateName = "PART_Border";
        private const string VisualBrushTemplateName = "PART_VisualBrush";

        public static Magnifier Default = new Magnifier();

        public static readonly DependencyProperty CornerRadiusProperty =
            DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(Magnifier), new PropertyMetadata(new CornerRadius(0)));

        public static readonly DependencyProperty ParentTargetProperty =
            DependencyProperty.Register("ParentTarget", typeof(FrameworkElement), typeof(Magnifier),
                new PropertyMetadata(default, OnParentTargetChanged));

        public static readonly DependencyProperty AddProperty =
            DependencyProperty.RegisterAttached("Add", typeof(Magnifier), typeof(Magnifier),
                new PropertyMetadata(default, OnAddChanged));

        private AdornerContainer _adornerContainer;
        private Border _border;
        private double _factor = 0.5;
        private VisualBrush _visualBrush = new VisualBrush();

        public CornerRadius CornerRadius
        {
            get => (CornerRadius)GetValue(CornerRadiusProperty);
            set => SetValue(CornerRadiusProperty, value);
        }
        public FrameworkElement ParentTarget
        {
            get => (FrameworkElement)GetValue(ParentTargetProperty);
            set => SetValue(ParentTargetProperty, value);
        }

        private static void OnParentTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var magnifier = (Magnifier)d;
            magnifier.OnParentTargetChanged((FrameworkElement)e.NewValue);
        }

        private void OnParentTargetChanged(FrameworkElement element)
        {
            if (element == null) return;
            element.Unloaded -= Element_Unloaded;
            element.Unloaded += Element_Unloaded;
            element.MouseEnter -= Element_MouseEnter;
            element.MouseEnter += Element_MouseEnter;
            element.MouseLeave -= Element_MouseLeave;
            element.MouseLeave += Element_MouseLeave;
            element.MouseMove -= Element_MouseMove;
            element.MouseMove += Element_MouseMove;
            element.MouseWheel -= Element_MouseWheel;
            element.MouseWheel += Element_MouseWheel;
        }

        private void Element_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (e.Delta > 0)
                _factor -= 0.2;
            else
                _factor += 0.2;
            _factor = _factor < 0.2 ? 0.2 : _factor;
            _factor = _factor > 1 * 4 ? 4 : _factor;
            MoveMagnifier();
        }

        private void Element_MouseLeave(object sender, MouseEventArgs e)
        {
            if (_adornerContainer == null) return;
            var layer = AdornerLayer.GetAdornerLayer(ParentTarget);
            if (layer != null) layer.Remove(_adornerContainer);
            if (_adornerContainer != null)
            {
                _adornerContainer.Child = null;
                _adornerContainer = null;
            }
        }

        private void Element_Unloaded(object sender, RoutedEventArgs e)
        {
            if (sender is FrameworkElement element)
                element.Unloaded -= Element_Unloaded;
        }

        private void Element_MouseMove(object sender, MouseEventArgs e)
        {
            MoveMagnifier();
        }

        private void MoveMagnifier()
        {
            if (_border == null) return;
            var length = Width * _factor;
            var radius = length / 2;
            var parentTargetPoint = Mouse.GetPosition(ParentTarget);
            var parentTargetVector = VisualTreeHelper.GetOffset(ParentTarget);
            var size = new Size(length, length);
            var viewboxRect =
                new Rect(
                    new Point(parentTargetPoint.X - radius + parentTargetVector.X,
                        parentTargetPoint.Y - radius + parentTargetVector.Y), size);
            _visualBrush.Viewbox = viewboxRect;
            var adornerPoint = Mouse.GetPosition(_adornerContainer);
            _border.SetValue(Canvas.LeftProperty, adornerPoint.X - Width / 2);
            _border.SetValue(Canvas.TopProperty, adornerPoint.Y - Height / 2);
        }

        private void Element_MouseEnter(object sender, MouseEventArgs e)
        {
            ParentTarget.Cursor = Cursors.Cross;
            if (_adornerContainer == null)
            {
                var layer = AdornerLayer.GetAdornerLayer(ParentTarget);
                if (layer == null) return;
                _adornerContainer = new AdornerContainer(layer)
                {
                    Child = this
                };
                layer.Add(_adornerContainer);
            }
        }


        public static Magnifier GetAdd(DependencyObject obj)
        {
            return (Magnifier)obj.GetValue(AddProperty);
        }

        public static void SetAdd(DependencyObject obj, int value)
        {
            obj.SetValue(AddProperty, value);
        }

        private static void OnAddChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is FrameworkElement parent)
            {
                var element = (Magnifier)e.NewValue;
                element.OnAddChanged(parent);
            }
        }

        private void OnAddChanged(FrameworkElement parent)
        {
            ParentTarget = parent;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            CornerRadius = new CornerRadius(Width / 2);
            _border = GetTemplateChild(BorderTemplateName) as Border;
            _visualBrush = GetTemplateChild(VisualBrushTemplateName) as VisualBrush ?? new VisualBrush();
        }
    }
}

1) MagnifierExample.xaml 代码如下:

<Image Source="/Images/Craouse/0.jpg" Stretch="None"
                   wpfdev:Magnifier.Add="{x:Static wpfdev:Magnifier.Default}"/>
0dff2df6225825f4442c9e7b6c540a9f.gif

参考资料

[1]

原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值