WPF 自定义放大镜控件

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

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"/>

参考资料

[1]

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

以上是关于WPF 自定义放大镜控件的主要内容,如果未能解决你的问题,请参考以下文章

C# wpf 如何实现自定义控件,布局时,大小发生变化,内部绘制的曲线跟随变化?

C# wpf怎么在grid添加自定义控件时显示滚动条

WPF自定义控件的制作

WPF 如何实现简单放大镜

WPF自定义控件の自定义控件

WPF自定义控件の扩展控件