基于 WPFDevelopers 的拖动案例实现二次拓展

 基于 WPFDevelopers 的拖动案例实现二次拓展

控件名:DrapView

作   者:WPFDevelopersOrg - 关关 | 驚鏵

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

码云链接[2]:https://gitee.com/WPFDevelopersOrg/WPFDevelopers

  • 框架支持.NET4 至 .NET8

  • Visual Studio 2022;

基于 WPFDevelopers 中的 TransformLayout[3]实现拖动案例。

源码在 dev 分支 https://github.com/WPFDevelopersOrg/WPFDevelopers/tree/dev

5d8420febdc51659d07a493824897825.png e5779574ec2593c12b760ecb2c07bdf7.png
  • 以下代码请跳转源码[4]ca9e9d6118ac720c31349c6a6fce8efc.png

1)新增 TransformThumb.cs 代码如下:

  • IsSeletedProperty:定义 IsSelected 的依赖属性,表示该控件是否被选中。当属性值改变时,会触发一个回调函数,该函数会根据新值来更新控件的外观,以反映选中状态。

  • ShapeTypeProperty:定义 ShapeType 的依赖属性,表示控件的形状类型(未完成)。

  • ThumbTypeProperty:定义 ThumbType 的依赖属性,表示控件的装饰器类型。当属性改变时,会调用 onPropertyChanged 方法来更新控件的装饰器。

  • TransformThumb_IsVisibleChanged 方法:如果控件变为可见时,调用 CreateAdorner 方法创建装饰器。

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

namespace WPFDevelopers.Samples.ExampleViews
{
    public class TransformThumb : ContentControl
    {
        public Guid Id { get; private set; }
        public TransformThumb()
        {
            Id = Guid.NewGuid();
        }
        public Type ContentType
        {
            get { return (Type)GetValue(ContentTypeProperty); }
            set { SetValue(ContentTypeProperty, value); }
        }

        public static readonly DependencyProperty ContentTypeProperty =
            DependencyProperty.Register("ContentType", typeof(Type), typeof(TransformThumb));

        // 选中事件

        // 点击事件

        public event EventHandler Click
        {
            add { AddHandler(ClickEvent, value); }
            remove { RemoveHandler(ClickEvent, value); }
        }

        public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
        "Click", RoutingStrategy.Bubble, typeof(EventHandler), typeof(TransformThumb));


        public PrintState PrintState
        {
            get { return (PrintState)GetValue(PrintStateProperty); }
            set { SetValue(PrintStateProperty, value); }
        }

        public static readonly DependencyProperty PrintStateProperty =
            DependencyProperty.Register("PrintState", typeof(PrintState), typeof(TransformThumb), new PropertyMetadata(PrintState.Printing));

        public bool IsSeleted
        {
            get { return (bool)GetValue(IsSeletedProperty); }
            set { SetValue(IsSeletedProperty, value); }
        }

        public static readonly DependencyProperty IsSeletedProperty =
            DependencyProperty.Register("IsSeleted", typeof(bool), typeof(TransformThumb), new FrameworkPropertyMetadata(false, (s, e) => {

                // 显示遮罩
                TransformThumb transform = s as TransformThumb;
                if (transform != null)
                {
                    // 判定为选中还是非选中
                    bool selected = (bool)e.NewValue;
                    string status = selected ? "选中" : "非选中";
                    // 控制选中效果
                    Debug.WriteLine($"节点:{transform.Id} 节点状态:{status}");
                    //transform.SetValue(ThumbTypeProperty, ThumbType.Style3);
                    UIElement element = transform as UIElement;
                    if (element == null)
                    {
                        return;
                    }
                    var adornerLayer = AdornerLayer.GetAdornerLayer(element);
                    if (adornerLayer != null)
                    {
                        var adorners = adornerLayer.GetAdorners(element);
                        Type oldtype = null;
                        switch (transform.ThumbType)
                        {
                            case ThumbType.Style1:
                                oldtype = typeof(ElementAdorner);
                                break;
                            case ThumbType.Style2:
                                oldtype = typeof(ThumbAdorner);
                                break;
                            case ThumbType.Style3:
                                oldtype = typeof(ThumbAdorner2);
                                break;
                            default:
                                oldtype = typeof(ThumbAdorner2);
                                break;
                        }
                        foreach (var adorner in adorners)
                        {
                            if (adorner.GetType() == oldtype)
                            {
                                adorner.Visibility = selected? Visibility.Visible:Visibility.Hidden;
                            }
                        }
                    }
                }
            }));


        public ShapeType ShapeType
        {
            get { return (ShapeType)GetValue(ShapeTypeProperty); }
            set { SetValue(ShapeTypeProperty, value); }
        }

        public static readonly DependencyProperty ShapeTypeProperty =
            DependencyProperty.Register("ShapeType", typeof(ShapeType), typeof(TransformThumb));

        public ThumbType ThumbType
        {
            get { return (ThumbType)GetValue(ThumbTypeProperty); }
            set { SetValue(ThumbTypeProperty, value); }
        }

        public static readonly DependencyProperty ThumbTypeProperty =
            DependencyProperty.Register("ThumbType", typeof(ThumbType), typeof(TransformThumb),new FrameworkPropertyMetadata(ThumbType.Style1, onPropertyChanged));

        private static void onPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateAdorner(d, e);
        }

        static TransformThumb()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TransformThumb), new FrameworkPropertyMetadata(typeof(TransformThumb)));
        }
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            IsVisibleChanged += TransformThumb_IsVisibleChanged;
            CreateAdorner();
        }
        void CreateAdorner()
        {
            var adornerLayer = AdornerLayer.GetAdornerLayer(this);
            if (adornerLayer != null)
            {
                Adorner adorner;
                switch (ThumbType)
                {
                    case ThumbType.Style1:
                        adorner = new ElementAdorner(this);
                        break;
                    case ThumbType.Style2:
                        adorner = new ThumbAdorner(this);
                        break;
                    case ThumbType.Style3:
                        adorner = new ThumbAdorner2(this);
                        break;
                    default:
                        adorner = new ThumbAdorner2(this);
                        break;
                }
                if (adorner != null)
                {
                    adorner.Visibility = Visibility.Hidden;
                    adornerLayer.Add(adorner);
                }
            }
        }

        static void UpdateAdorner(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = d as UIElement;
            if (element == null)
            {
                return;
            }
            var adornerLayer = AdornerLayer.GetAdornerLayer(element as Visual);
            if (adornerLayer != null)
            {
                ThumbType oldvalue = (ThumbType)e.OldValue;
                var adorners = adornerLayer.GetAdorners(element as UIElement);
                Type oldtype = null;
                switch (oldvalue)
                {
                    case ThumbType.Style1:
                        oldtype = typeof(ElementAdorner);
                        break;
                    case ThumbType.Style2:
                        oldtype = typeof(ThumbAdorner);
                        break;
                    case ThumbType.Style3:
                        oldtype = typeof(ThumbAdorner2);
                        break;
                    default:
                        oldtype = typeof(ThumbAdorner2);
                        break;
                }
                foreach (var adorner in adorners) {
                    if (adorner.GetType() == oldtype)
                    {
                        adornerLayer.Remove(adorner);
                    }
                }

                ThumbType newvalue = (ThumbType)e.NewValue;
                Adorner Newadorner;
                switch (newvalue)
                {
                    case ThumbType.Style1:
                        Newadorner = new ElementAdorner(element);
                        break;
                    case ThumbType.Style2:
                        Newadorner = new ThumbAdorner(element);
                        break;
                    case ThumbType.Style3:
                        Newadorner = new ThumbAdorner2(element);
                        break;
                    default:
                        Newadorner = new ThumbAdorner2(element);
                        break;
                }
                if (Newadorner != null)
                {
                    adornerLayer.Add(Newadorner);
                }
            }
        }
        private void TransformThumb_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue is bool isVisible)
            {
                if (isVisible)
                {
                    CreateAdorner();
                }

            }
        }

        public override string ToString()
        {
            return $"{Id}";
        }
    }
}

2)新增 TransformThumb.xaml 代码如下:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:WPFDevelopers.Samples.ExampleViews">

    <Style BasedOn="{StaticResource WD.ControlBasicStyle}" TargetType="{x:Type controls:TransformThumb}">
        <Setter Property="MinHeight" Value="20" />
        <Setter Property="MinWidth" Value="30" />
        <Setter Property="Height" Value="100" />
        <Setter Property="Width" Value="50" />
        <Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:TransformThumb}">
                    <ContentPresenter
                        x:Name="PART_ContentPresenter"
                        Width="{TemplateBinding Width}"
                        Height="{TemplateBinding Height}"
                        Content="{TemplateBinding Content}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

3)新增 DrapViewExample.xaml 示例代码如下:

  • Stretch="None":画刷在应用到目标区域时不进行拉伸,保持原始大小。

  • TileMode="Tile":平铺图案以填充目标区域。

  • Viewport="0,0,16,16":设置 16x16 的区域。

  • ViewportUnits="Absolute":指定以像素为基准。

  • <DrawingBrush.Drawing> 元素内部定义一个绘图元素,通过 <GeometryDrawing><GeometryGroup> 来创建两个矩形作为背景图。

<DockPanel>
     <DockPanel.Resources>
         <Color x:Key="WD.MainContentForegroundColor">#FFF0F0F0</Color>
         <Color x:Key="WD.MainContentBackgroundColor">#FFF5F5F5</Color>
         <DrawingBrush
             x:Key="WD.MainContentForegroundDrawingBrush"
             RenderOptions.CachingHint="Cache"
             Stretch="None"
             TileMode="Tile"
             Viewport="0,0,16,16"
             ViewportUnits="Absolute">
             <DrawingBrush.Drawing>
                 <DrawingGroup>
                     <GeometryDrawing Brush="{DynamicResource WD.MainContentForegroundBrush}">
                         <GeometryDrawing.Geometry>
                             <GeometryGroup>
                                 <RectangleGeometry Rect="0,0,8,8" />
                                 <RectangleGeometry Rect="8,8,8,8" />
                             </GeometryGroup>
                         </GeometryDrawing.Geometry>
                     </GeometryDrawing>
                 </DrawingGroup>
             </DrawingBrush.Drawing>
         </DrawingBrush>

         <SolidColorBrush x:Key="WD.MainContentForegroundBrush" Color="{DynamicResource WD.MainContentForegroundColor}" />
         <SolidColorBrush x:Key="WD.MainContentBackgroundBrush" Color="{DynamicResource WD.MainContentBackgroundColor}" />
     </DockPanel.Resources>
     <Border Margin="0,10" DockPanel.Dock="Top">
         <StackPanel
             HorizontalAlignment="Center"
             Orientation="Horizontal"
             PreviewMouseLeftButtonDown="StackPanel_PreviewMouseLeftButtonDown">
             <RadioButton
                 Margin="4,0"
                 Background="Red"
                 GroupName="color"
                 IsChecked="True"
                 Style="{StaticResource WD.ColorRadioButton}" />
             <RadioButton
                 Margin="4,0"
                 Background="DodgerBlue"
                 GroupName="color"
                 Style="{StaticResource WD.ColorRadioButton}" />
             <RadioButton
                 Margin="4,0"
                 Background="LimeGreen"
                 GroupName="color"
                 Style="{StaticResource WD.ColorRadioButton}" />
             <RadioButton
                 Margin="4,0"
                 Background="Yellow"
                 GroupName="color"
                 Style="{StaticResource WD.ColorRadioButton}" />
         </StackPanel>
     </Border>
     <Border Margin="0,10" DockPanel.Dock="Top">
         <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
             <RadioButton
                 VerticalContentAlignment="Center"
                 Content="点"
                 GroupName="polygon"
                 Tag="0" />
             <RadioButton
                 VerticalContentAlignment="Center"
                 Content="线"
                 GroupName="polygon"
                 Tag="1" />
             <RadioButton
                 VerticalContentAlignment="Center"
                 Content="三角形"
                 GroupName="polygon"
                 Tag="2" />
             <RadioButton
                 VerticalContentAlignment="Center"
                 Content="矩形"
                 GroupName="polygon"
                 IsChecked="True"
                 Tag="3" />
             <RadioButton
                 VerticalContentAlignment="Center"
                 Content="多边形"
                 GroupName="polygon"
                 Tag="4" />
         </StackPanel>
     </Border>
     <Border Margin="0,10" DockPanel.Dock="Top">
         <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
             <RadioButton
                 VerticalContentAlignment="Center"
                 Checked="RadioButton_Checked"
                 Content="Style1"
                 GroupName="style"
                 IsChecked="True"
                 Tag="YYYY" />
             <RadioButton
                 VerticalContentAlignment="Center"
                 Checked="RadioButton_Checked"
                 Content="Style2"
                 GroupName="style"
                 Tag="1" />
             <RadioButton
                 VerticalContentAlignment="Center"
                 Checked="RadioButton_Checked"
                 Content="Style3"
                 GroupName="style"
                 Tag="2" />
             <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
                 <Button
                     Name="btn_import"
                     Margin="10,0"
                     Padding="10,5"
                     Content="导入" />
                 <Button
                     Name="btn_export"
                     Margin="10,0"
                     Padding="10,5"
                     Content="导出" />
                 <Button
                     Name="btn_remove"
                     Margin="10,0"
                     Padding="10,5"
                     Click="btn_remove_Click"
                     Content="删除选中" />
                 <Button
                     Name="btn_removeAll"
                     Margin="10,0"
                     Padding="10,5"
                     Click="btn_removeAll_Click"
                     Content="清空画板" />
             </StackPanel>
         </StackPanel>
     </Border>
     <Border Width="200" DockPanel.Dock="Left">
         <ListBox x:Name="lst_layers" />
     </Border>
     <Canvas
         Name="MainCanvas"
         Background="{StaticResource WD.MainContentForegroundDrawingBrush}"
         ClipToBounds="True"
         MouseLeftButtonDown="MainCanvas_MouseLeftButtonDown"
         MouseLeftButtonUp="MainCanvas_MouseLeftButtonUp"
         MouseMove="MainCanvas_MouseMove" />
 </DockPanel>

4)新增 DrapViewExample.xaml.cs 示例代码如下:

  • RadioButton_Checked: 单选按钮选择一个不同类型,会更新 selectThumb ,以便在绘制时使用正确的元素类型。

  • btn_remove_Click: 删除选定图形事件。它遍历 MainCanvas 中的所有子元素,找到被选中的 TransformThumb 元素,并将它从 MainCanvas 中移除,并从 lst_layers 中移除对应的项。

  • NodeExist: 这个方法用于检查给定的 TransformThumb 元素是否存在于 MainCanvas 中。它遍历 MainCanvas 的子元素,如果找到与给定节点相同的节点,则返回 true,否则返回 false

  • btn_removeAll_Click: 这个方法处理了移除所有图形元素的按钮点击事件。它会清空 MainCanvas 中的所有子元素,并清空 lst_layers 中的所有项。

  • StackPanel_PreviewMouseLeftButtonDown: 这个方法处理了鼠标左键按下事件,用于捕获当前选定的颜色。当用户点击颜色选择器时,会将当前颜色保存到 currentBrush 变量中,以便在绘制图形时使用。

public partial class DrapViewExample : UserControl
{
    Point start;
    TransformThumb element;
    Rectangle shape;
    ThumbType selectThumb;
    Brush currentBrush = new SolidColorBrush(Colors.Red);
    public DrapViewExample()
    {
        InitializeComponent();
    }
    private void MainCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        //鼠标左键按下
        Debug.WriteLine("鼠标左键按下");
        // 更新选中项
        UpdateDraw();
        // 判定是否包含快捷键

        // 判定左键按下
        if (element != null && element.PrintState == PrintState.Edit)
        {
            // 设置选项选中
            element.IsSeleted = true;
            element = null;
            e.Handled = true;
            return;
        }

        start = e.GetPosition(MainCanvas);
        // 创建几何实例
        shape = new Rectangle();
        shape.Stroke = currentBrush;
        shape.Fill = currentBrush;
        shape.StrokeThickness = 1;
        shape.Width = 0;
        shape.Height = 0;
        shape.MouseLeftButtonDown += (s, e1) => {
            e.Handled = false;
        };
        element = new TransformThumb();
        element.MouseLeftButtonDown += (s, e2) => {
            TransformThumb transform = s as TransformThumb;
            if (transform != null)
            {
                Debug.WriteLine($"节点:{transform.Id} 节点按下");
                transform.PrintState = PrintState.Edit;
                element = transform;
                e.Handled = false;
            }
        };
        element.MouseLeftButtonUp += (s, e3) => {
            e.Handled = false;
        };
        
        element.Content = shape;
        element.Width = shape.Width;
        element.Height = shape.Height;
        element.ThumbType = selectThumb;
        element.ContentType = shape.GetType();
        element.PrintState = PrintState.Printing;
        
    }

    private void MainCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        // 鼠标左键键起
        Debug.WriteLine("鼠标左键键起");

        // 更新选中项
        UpdateDraw();
        // 键下和键起在同一个位置表示页面无选择项
        Point end = Mouse.GetPosition(MainCanvas);
        if (end == start)
        {
            // 移除节点
            MainCanvas.Children.Remove(element);
            // 重置绘制节点
            element = null;
            return;
        }
        if (element != null && element.PrintState != PrintState.Edit)
        {
            element.IsSeleted = true;
            element.PrintState = PrintState.Edit;
        }
        // 重置绘制节点
        element = null;
    }

    private void UpdateDraw()
    {
        // 设置其他项默认隐藏
        foreach (var item in MainCanvas.Children)
        {
            var child = item as TransformThumb;
            if (child != null && child.IsSeleted && element != null)
            {
                // 重置非选中项
                if (child != element)
                {
                    child.IsSeleted = false;
                    child.PrintState = PrintState.Normal;
                }
            }
        }
    }

    private void MainCanvas_MouseMove(object sender, MouseEventArgs e)
    {
        if (element == null)
        {
            return;
        }
        if (!NodeExist(element))
        {
            // 设置几何位置
            Canvas.SetLeft(element, start.X);
            Canvas.SetTop(element, start.Y);
            // 更新其他选中项
            UpdateDraw();
            // 添加到面板中
            MainCanvas.Children.Add(element);
            lst_layers.Items.Add(element.ToString());
        }
        // 鼠标移动(鼠标未按下移动事件不生效)
        if (e.LeftButton == MouseButtonState.Pressed && element.PrintState == PrintState.Printing)
        {
            Debug.WriteLine("鼠标移动绘制几何");
            Point current = e.GetPosition(MainCanvas);
            // 判定几何实例
            // 判定鼠标移动点是否小于起始点
            if (current.X - start.X < 0)
            {
                // 设置起始点为移动点
                Canvas.SetLeft(element, current.X);
            }
            if (current.Y - start.Y < 0)
            {
                // 设置起始点为移动点
                Canvas.SetTop(element, current.Y);
            }
            shape.SetValue(WidthProperty, Math.Abs(current.X - start.X));
            shape.SetValue(HeightProperty, Math.Abs(current.Y - start.Y));
            element.Width = Math.Abs(current.X - start.X);
            element.Height = Math.Abs(current.Y - start.Y);
        }
    }

    private void RadioButton_Checked(object sender, RoutedEventArgs e)
    {
        RadioButton radioButton = (RadioButton)sender;
        if (radioButton != null)
        {
            ThumbType result;
            if (Enum.TryParse(radioButton.Content.ToString(), out result))
            {
                selectThumb = result;
            }
        }
    }

    private void btn_remove_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i < MainCanvas.Children.Count; i++)
        {
            UIElement item = MainCanvas.Children[i];
            if (item is TransformThumb)
            {
                var tranform = item as TransformThumb;
                if (tranform != null && tranform.IsSeleted)
                {
                    MainCanvas.Children.Remove(tranform);
                    lst_layers.Items.Remove(tranform.ToString());
                }
            }
        }
    }

    private bool NodeExist(TransformThumb node)
    {
        bool result = false;
        int count = MainCanvas.Children.Count;
        for (int i = 0; i < count; i++)
        {
            UIElement item = MainCanvas.Children[i];
            if (item is TransformThumb)
            {
                var tranform = item as TransformThumb;
                if (tranform == node)
                {
                    result = true;
                    break;
                }
            }
        }

        return result;
    }

    private void btn_removeAll_Click(object sender, RoutedEventArgs e)
    {
        MainCanvas.Children.Clear();
        lst_layers.Items.Clear();
    }

    private void StackPanel_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (e.Source is RadioButton)
        {
            var radioButton = (RadioButton)e.Source;
            currentBrush = radioButton.Background;
        }
    }
}
7231977a9133d34eeb226fd64d9c2a39.gif

参考资料

[1]

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

[2]

码云链接: https://gitee.com/WPFDevelopersOrg/WPFDevelopers

[3]

TransformLayout: https://github.com/WPFDevelopersOrg/WPFDevelopers/tree/dev/src/WPFDevelopers.Samples.Shared/Controls/TransformLayout

[4]

源码: https://github.com/WPFDevelopersOrg/WPFDevelopers/tree/dev/src/WPFDevelopers.Samples.Shared/ExampleViews/DrapView

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值