当前位置: 欣欣网 > 码农

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

2024-05-08码农

基于 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

  • 以下代码请跳转 源码 [4]

  • 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;
    namespaceWPFDevelopers.Samples.ExampleViews
    {
    public classTransformThumb : ContentControl
    {
    public Guid Id { getprivateset; }
    publicTransformThumb()
    {
    Id = Guid.NewGuid();
    }
    public Type ContentType
    {
    get { return (Type)GetValue(ContentTypeProperty); }
    set { SetValue(ContentTypeProperty, value); }
    }
    publicstaticreadonly DependencyProperty ContentTypeProperty =
    DependencyProperty.Register("ContentType"typeof(Type), typeof(TransformThumb));
    // 选中事件
    // 点击事件
    publicevent EventHandler Click
    {
    add { AddHandler(ClickEvent, value); }
    remove { RemoveHandler(ClickEvent, value); }
    }
    publicstaticreadonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
    "Click", RoutingStrategy.Bubble, typeof(EventHandler), typeof(TransformThumb));

    public PrintState PrintState
    {
    get { return (PrintState)GetValue(PrintStateProperty); }
    set { SetValue(PrintStateProperty, value); }
    }
    publicstaticreadonly DependencyProperty PrintStateProperty =
    DependencyProperty.Register("PrintState"typeof(PrintState), typeof(TransformThumb), new PropertyMetadata(PrintState.Printing));
    publicbool IsSeleted
    {
    get { return (bool)GetValue(IsSeletedProperty); }
    set { SetValue(IsSeletedProperty, value); }
    }
    publicstaticreadonly 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); }
    }
    publicstaticreadonly DependencyProperty ShapeTypeProperty =
    DependencyProperty.Register("ShapeType"typeof(ShapeType), typeof(TransformThumb));
    public ThumbType ThumbType
    {
    get { return (ThumbType)GetValue(ThumbTypeProperty); }
    set { SetValue(ThumbTypeProperty, value); }
    }
    publicstaticreadonly DependencyProperty ThumbTypeProperty =
    DependencyProperty.Register("ThumbType"typeof(ThumbType), typeof(TransformThumb),new FrameworkPropertyMetadata(ThumbType. style1, onPropertyChanged));
    privatestaticvoidonPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
    UpdateAdorner(d, e);
    }
    staticTransformThumb()
    {
    Default styleKeyProperty.OverrideMetadata(typeof(TransformThumb), new FrameworkPropertyMetadata(typeof(TransformThumb)));
    }
    publicoverridevoidOnApplyTemplate()
    {
    base.OnApplyTemplate();
    IsVisibleChanged += TransformThumb_IsVisibleChanged;
    CreateAdorner();
    }
    voidCreateAdorner()
    {
    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);
    }
    }
    }
    staticvoidUpdateAdorner(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);
    }
    }
    }
    privatevoidTransformThumb_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
    if (e.NewValue isbool isVisible)
    {
    if (isVisible)
    {
    CreateAdorner();
    }
    }
    }
    publicoverridestringToString()
    {
    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">

    < styleBasedOn="{StaticResource WD.ControlBasic style}"TargetType="{x:Type controls:TransformThumb}">
    <SetterProperty="MinHeight"Value="20" />
    <SetterProperty="MinWidth"Value="30" />
    <SetterProperty="Height"Value="100" />
    <SetterProperty="Width"Value="50" />
    <SetterProperty="RenderTransformOrigin"Value="0.5,0.5" />
    <SetterProperty="Template">
    <Setter.Value>
    <ControlTemplateTargetType="{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>
    <Colorx:Key="WD.MainContentForegroundColor">#FFF0F0F0</Color>
    <Colorx: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>
    <GeometryDrawingBrush="{DynamicResource WD.MainContentForegroundBrush}">
    <GeometryDrawing.Geometry>
    <GeometryGroup>
    <RectangleGeometryRect="0,0,8,8" />
    <RectangleGeometryRect="8,8,8,8" />
    </GeometryGroup>
    </GeometryDrawing.Geometry>
    </GeometryDrawing>
    </DrawingGroup>
    </DrawingBrush.Drawing>
    </DrawingBrush>
    <SolidColorBrushx:Key="WD.MainContentForegroundBrush"Color="{DynamicResource WD.MainContentForegroundColor}" />
    <SolidColorBrushx:Key="WD.MainContentBackgroundBrush"Color="{DynamicResource WD.MainContentBackgroundColor}" />
    </DockPanel.Resources>
    <BorderMargin="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>
    <BorderMargin="0,10"DockPanel.Dock="Top">
    <StackPanelHorizontalAlignment="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>
    <BorderMargin="0,10"DockPanel.Dock="Top">
    <StackPanelHorizontalAlignment="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" />

    <StackPanelHorizontalAlignment="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>
    <BorderWidth="200"DockPanel.Dock="Left">
    <ListBoxx: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 变量中,以便在绘制图形时使用。

  • publicpartial classDrapViewExample : UserControl
    {
    Point start;
    TransformThumb element;
    Rectangle shape;
    ThumbType selectThumb;
    Brush currentBrush = new SolidColorBrush(Colors.Red);
    publicDrapViewExample()
    {
    InitializeComponent();
    }
    privatevoidMainCanvas_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;
    }
    privatevoidMainCanvas_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;
    }
    privatevoidUpdateDraw()
    {
    // 设置其他项默认隐藏
    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;
    }
    }
    }
    }
    privatevoidMainCanvas_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);
    }
    }
    privatevoidRadioButton_Checked(object sender, RoutedEventArgs e)
    {
    RadioButton radioButton = (RadioButton)sender;
    if (radioButton != null)
    {
    ThumbType result;
    if (Enum.TryParse(radioButton.Content.ToString(), out result))
    {
    selectThumb = result;
    }
    }
    }
    privatevoidbtn_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());
    }
    }
    }
    }
    privateboolNodeExist(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;
    }
    privatevoidbtn_removeAll_Click(object sender, RoutedEventArgs e)
    {
    MainCanvas.Children.Clear();
    lst_layers.Items.Clear();
    }
    privatevoidStackPanel_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
    if (e.Source is RadioButton)
    {
    var radioButton = (RadioButton)e.Source;
    currentBrush = radioButton.Background;
    }
    }
    }












    参考资料

    [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