WPF Prism MVVM【动态添加控件并可用鼠标、拖动、缩放、旋转】

引用地址:https://blog.csdn.net/redfox6843/article/details/126117819

文章目录

    WPF Prism MVVM【动态添加控件并可用鼠标、拖动、缩放、旋转】
    前言
        项目运行截图
    一、基本类
        1.控件对应的实体类
        2.控件缩放和旋转的核心类
        3.查找控件的方法
        4.MVVM向ViewModel传递多个参数的类
    二、为标识方向的控件
        1.XAML代码
        2.CS代码
    三、主程序
        1.AXML代码
        2.ViewModel端的CS代码
    总结

前言

本文中所开发的功能是为了给后台目标检测算法做区域标注的一个Demo。为视频中标注电子围栏和框选区域。主要采用了Prism.DryIoc的MVVM方式,用到控件和方法:ItemsControl(控件模板)、Thumb(可拖动控件)、Adorner(装饰器)、CommandParameter的多参数传递、GetChildObjectByUid(通过UID查找某类型的子控件)
项目运行截图

一、基本类
1.控件对应的实体类

这个类只是为了给生成的控件一个数据的支撑(可以理解为灵魂:😜)

using Prism.Commands;
using Prism.Mvvm;
namespace BlankApp2
{
    public class ObjEntity : BindableBase
    {
        public int ID { get; set; }      
        /// <summary>
        /// 节点名称
        /// </summary>
        public string Name { set; get; }
        /// <summary>
        /// 拖动事件
        /// </summary>
        public DelegateCommand<object> onDragDeltaCommand { get; set; }
        /// <summary>
        /// 控件加载后事件
        /// </summary>
        public DelegateCommand<object> onThumbLoadedCommand { get; set; }
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

2.控件缩放和旋转的核心类

GridAdorner(Grid装饰器)这个类主要是在动态添加到前端的Loaded事件中为其添加上一些Thumb控件,并且用鼠标操作这些Thumb来调整其控件的大小和旋转。

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace BlankApp2
{
    /// <summary>
    /// Grid装饰器
    /// </summary>
    public class GridAdorner : Adorner
    {
        //4条边
        Thumb _leftThumb, _topThumb, _rightThumb, _bottomThumb;
        //4个角
        Thumb _lefTopThumb, _rightTopThumb, _rightBottomThumb, _leftbottomThumb;

        Thumb _middleThumb;
        //布局容器,如果不使用布局容器,则需要给上述8个控件布局,实现和Grid布局定位是一样的,会比较繁琐且意义不大。
        Grid _grid;
        UIElement _adornedElement;
        public GridAdorner(UIElement adornedElement) : base(adornedElement)
        {           
            _adornedElement = adornedElement;
            //初始化四周和中间的thumb
            _middleThumb = new Thumb();
            _middleThumb.HorizontalAlignment = HorizontalAlignment.Center;
            _middleThumb.VerticalAlignment = VerticalAlignment.Center;
            _middleThumb.Cursor =Cursors.Cross;
     
              _leftThumb = new Thumb();
            _leftThumb.HorizontalAlignment = HorizontalAlignment.Left;
            _leftThumb.VerticalAlignment = VerticalAlignment.Center;
            _leftThumb.Cursor = Cursors.SizeWE;
            _topThumb = new Thumb();
            _topThumb.HorizontalAlignment = HorizontalAlignment.Center;
            _topThumb.VerticalAlignment = VerticalAlignment.Top;
            _topThumb.Cursor = Cursors.SizeNS;
            _rightThumb = new Thumb();
            _rightThumb.HorizontalAlignment = HorizontalAlignment.Right;
            _rightThumb.VerticalAlignment = VerticalAlignment.Center;
            _rightThumb.Cursor = Cursors.SizeWE;
            _bottomThumb = new Thumb();
            _bottomThumb.HorizontalAlignment = HorizontalAlignment.Center;
            _bottomThumb.VerticalAlignment = VerticalAlignment.Bottom;
            _bottomThumb.Cursor = Cursors.SizeNS;
            _lefTopThumb = new Thumb();
            _lefTopThumb.HorizontalAlignment = HorizontalAlignment.Left;
            _lefTopThumb.VerticalAlignment = VerticalAlignment.Top;
            _lefTopThumb.Cursor = Cursors.SizeNWSE;
            _rightTopThumb = new Thumb();
            _rightTopThumb.HorizontalAlignment = HorizontalAlignment.Right;
            _rightTopThumb.VerticalAlignment = VerticalAlignment.Top;
            _rightTopThumb.Cursor = Cursors.SizeNESW;
            _rightBottomThumb = new Thumb();
            _rightBottomThumb.HorizontalAlignment = HorizontalAlignment.Right;
            _rightBottomThumb.VerticalAlignment = VerticalAlignment.Bottom;
            _rightBottomThumb.Cursor = Cursors.SizeNWSE;
            _leftbottomThumb = new Thumb();
            _leftbottomThumb.HorizontalAlignment = HorizontalAlignment.Left;
            _leftbottomThumb.VerticalAlignment = VerticalAlignment.Bottom;
            _leftbottomThumb.Cursor = Cursors.SizeNESW;


            _grid = new Grid();           
            _grid.Children.Add(_middleThumb);
            _grid.Children.Add(_leftThumb);
            _grid.Children.Add(_topThumb);
            _grid.Children.Add(_rightThumb);
            _grid.Children.Add(_bottomThumb);
            _grid.Children.Add(_lefTopThumb);
            _grid.Children.Add(_rightTopThumb);
            _grid.Children.Add(_rightBottomThumb);
            _grid.Children.Add(_leftbottomThumb);
            AddVisualChild(_grid);

            foreach (Thumb thumb in _grid.Children)
            {
                thumb.Width = 13;
                thumb.Height = 13;
                thumb.Background = Brushes.Green;
                thumb.Template = new ControlTemplate(typeof(Thumb))
                {
                    VisualTree = GetFactory(new SolidColorBrush(Colors.White))
                };
                thumb.DragDelta += Thumb_DragDelta;            
            }
        }
       /// <summary>
       /// 控件的旋转事件
       /// </summary>
       /// <param name="sender"></param>
       /// <param name="e"></param>
        private void _zhongThumb_MouseMove(object sender, MouseEventArgs e)
        {           
            if (Mouse.Captured == _adornedElement)
            {
                double a = Math.PI;
                var c = _adornedElement as FrameworkElement;
               // c.RenderTransformOrigin = new Point(0.5, 0.5);
                // Get the current mouse position relative to the volume control
                Point currentLocation = Mouse.GetPosition(this);

                // We want to rotate around the center of the knob, not the top corner
                Point knobCenter = new Point(this.ActualHeight / 2, this.ActualWidth / 2);

                // Calculate an angle
                //double radians = Math.Atan((currentLocation.Y - knobCenter.Y) /
                //                           (currentLocation.X - knobCenter.X));
                double radians = Math.Atan((currentLocation.Y)/
                                      (currentLocation.X));
                RotateTransform rt = new RotateTransform();
                rt.Angle = radians * 180 / Math.PI;
                rt.Angle = radians ;
                // Apply a 180 degree shift when X is negative so that we can rotate
                // all of the way around
                //if (currentLocation.X - knobCenter.X < 0)
                //{
                //    rt.Angle += 180;
                //}
                c.RenderTransform = rt;
              //  c.RenderTransformOrigin = new Point(0, 0);
            }
        }
        /// <summary>
        /// 鼠标在旋转控件上抬起
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _zhongThumb_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Mouse.Capture(null);
        }
        /// <summary>
        /// 鼠标在旋转控件上按下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _zhongThumb_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Mouse.Capture(this);
        }
        protected override Visual GetVisualChild(int index)
        {
            return _grid;
        }
        protected override int VisualChildrenCount
        {
            get
            {
                return 1;
            }
        }
        protected override Size ArrangeOverride(Size finalSize)
        {            //直接给grid布局,grid内部的thumb会自动布局。
            _grid.Arrange(new Rect(new Point(-_leftThumb.Width / 2, -_leftThumb.Height / 2), new Size(finalSize.Width + _leftThumb.Width, finalSize.Height + _leftThumb.Height)));
            return finalSize;
        }
        /// <summary>
        /// 拖动事件逻辑
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            var c = _adornedElement as FrameworkElement;
            var thumb = sender as FrameworkElement;
            double left, top, right, bottom, width, height;
            if (thumb.HorizontalAlignment == HorizontalAlignment.Left)
            {
                right = c.Margin.Right;
                left = c.Margin.Left + e.HorizontalChange;
                width = (double.IsNaN(c.Width) ? c.ActualWidth : c.Width) - e.HorizontalChange;
            }
            else
            {
                left = c.Margin.Left;
                right = c.Margin.Right - e.HorizontalChange;
                width = (double.IsNaN(c.Width) ? c.ActualWidth : c.Width) + e.HorizontalChange;
            }

            if (thumb.VerticalAlignment == VerticalAlignment.Top)
            {
                bottom = c.Margin.Bottom;
                top = c.Margin.Top + e.VerticalChange;
                height = (double.IsNaN(c.Height) ? c.ActualHeight : c.Height) - e.VerticalChange;
            }
            else
            {
                top = c.Margin.Top;
                bottom = c.Margin.Bottom - e.VerticalChange;
                height = (double.IsNaN(c.Height) ? c.ActualHeight : c.Height) + e.VerticalChange;
            }
            if (thumb.HorizontalAlignment != HorizontalAlignment.Center)
            {
                if (width >= 0)
                {
                    c.Margin = new Thickness(left, c.Margin.Top, right, c.Margin.Bottom);
                    c.Width = width;
                }
            }
            if (thumb.VerticalAlignment != VerticalAlignment.Center)
            {
                if (height >= 0)
                {
                    c.Margin = new Thickness(c.Margin.Left, top, c.Margin.Right, bottom);
                    c.Height = height;
                }
            }
            if (thumb.HorizontalAlignment == HorizontalAlignment.Center && thumb.VerticalAlignment == VerticalAlignment.Center)
            {
                Point currentLocation = Mouse.GetPosition(this);

                // 我们想绕着旋钮的中心旋转,而不是顶角
                Point knobCenter = new Point(this.ActualHeight / 2, this.ActualWidth / 2);

                // 计算角度
                double radians = Math.Atan((currentLocation.Y - knobCenter.Y) /
                                           (currentLocation.X - knobCenter.X));
                RotateTransform rt = new RotateTransform();
                rt.Angle = radians * 180 / Math.PI;

                // 当X为负时,应用180度偏移,这样我们可以旋转所有的方向,但是这个地方怎么看着效果不是那么好看
                if (currentLocation.X - knobCenter.X < 0)
                {
                    rt.Angle += 180;
                }
                c.RenderTransform = rt;    
            }
        }
        /// <summary>
        /// 四周thumb的样式
        /// </summary>
        /// <param name="back"></param>
        /// <returns></returns>
        FrameworkElementFactory GetFactory(Brush back)
        {
            var fef = new FrameworkElementFactory(typeof(Ellipse));
            fef.SetValue(Ellipse.FillProperty, back);
            fef.SetValue(Ellipse.StrokeProperty, new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999999")));
            fef.SetValue(Ellipse.StrokeThicknessProperty, (double)2);
            return fef;
        }
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249

3.查找控件的方法

由于在控件模板里有些控件的Name可能无法用Binding来绑定的,所以为了后面查找控件方便采用了UID来做为标识

         /// <summary>
        /// 通过UID查找某类型的子控件
        /// </summary>
        /// <example>StackPanel sp = GetChildObject<StackPanel>(this.LayoutRoot, "spDemoPanel")</example>
        /// <typeparam name="T">子控件类型</typeparam>
        /// <param name="obj">父控件</param>
        /// <param name="name">子控件名称,默认为空</param>
        /// <returns></returns>
        public static T GetChildObjectByUid<T>(DependencyObject obj, string uid = null) where T : FrameworkElement
        {
            DependencyObject child = null;
            T grandChild = null;

            for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
            {
                child = VisualTreeHelper.GetChild(obj, i);

                if (child is T && (((T)child).Uid == uid | string.IsNullOrEmpty(uid)))
                {
                    return (T)child;
                }
                else
                {
                    grandChild = GetChildObjectByUid<T>(child, uid);
                    if (grandChild != null)
                        return grandChild;
                 }
            }
            return null;
        }
        /// <summary>
        /// 通过名称查找某类型的子控件
        /// </summary>
        /// <example>StackPanel sp = GetChildObject<StackPanel>(this.LayoutRoot, "spDemoPanel")</example>
        /// <typeparam name="T">子控件类型</typeparam>
        /// <param name="obj">父控件</param>
        /// <param name="name">子控件名称,默认为空</param>
        /// <returns></returns>
        public static T GetChildObject<T>(DependencyObject obj, string name = null) where T : FrameworkElement
        {
            DependencyObject child = null;
            T grandChild = null;

            for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
            {
                child = VisualTreeHelper.GetChild(obj, i);

                if (child is T && (((T)child).Name == name | string.IsNullOrEmpty(name)))
                {
                    return (T)child;
                }
                else
                {
                    grandChild = GetChildObject<T>(child, name);
                    if (grandChild != null)
                        return grandChild;
                }
            }
            return null;
        }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60

4.MVVM向ViewModel传递多个参数的类

using System;
using System.Linq;
using System.Windows.Data;
namespace WPF_Common
{
    /// <summary>
    /// CommandParameter 多参数传递
    /// </summary>
    public class ObjectConvert : IMultiValueConverter
    {
        #region IMultiValueConverter Members
        public static object ConverterObject;
        public object Convert(object[] values, Type targetType,
          object parameter, System.Globalization.CultureInfo culture)
        {
            ConverterObject = values;
            string str = values.GetType().ToString();
            return values.ToArray();
        }
        public object[] ConvertBack(object value, Type[] targetTypes,
          object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

二、为标识方向的控件

1.XAML代码

在这里面的ID_Head是用来标识方向的,最后我们可以获它的相对坐标就可以了。

<UserControl x:Class="BlankApp2.Views.ArrowControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:BlankApp2.Views"
             mc:Ignorable="d"
             d:DesignHeight="50" d:DesignWidth="100">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="2"/>
        </Grid.ColumnDefinitions>
        <Path  Grid.Column="0" Fill="Red" Panel.ZIndex="0" Data="M -15,8 L 17,17 C 17,17 19,18 17,19 L 17,19 L -15,28 C -15,28 -17,28.2 -16,26 L -16,26 L -5,18 L -16,10 C -16,10 -17,8.5 -15,8 Z" Margin="0,0,0,0" Stretch="Fill"/>
        <Ellipse Grid.Column="1" Name="ID_Head"   Height="1" Width="1" Stroke="Black" VerticalAlignment="Center" HorizontalAlignment="Center" />
           </Grid>
</UserControl>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

2.CS代码

这里为了方便就没用MVVM

using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
namespace BlankApp2.Views
{
    /// <summary>
    /// UserControl.xaml 的交互逻辑
    /// </summary>
    public partial class ArrowControl : UserControl
    {
        public ArrowControl()
        {
            InitializeComponent();
        }
        #region ID
        /// <summary>
        /// 左面标题
        /// </summary>
        public string ID
        {
            get => (string)GetValue(IDProperty);
            set => SetValue(IDProperty, value);
        }
        public static readonly DependencyProperty IDProperty =
                  DependencyProperty.RegisterAttached(
                        "ID",
                        typeof(string),
                        typeof(ArrowControl),
                        new FrameworkPropertyMetadata("", new PropertyChangedCallback(OnPropertyChanged)));

        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var b = d as ArrowControl;
            if (b != null && IDProperty != null)
            {
              // b.Title;
            }
        }
        #endregion
        public Ellipse Direction
        {
            get { return this.ID_Head; }
        }
        public static readonly DependencyProperty DirectionProperty =
             DependencyProperty.RegisterAttached(
                   "Direction",
                   typeof(string),
                   typeof(ArrowControl),
                   new FrameworkPropertyMetadata("", null));
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51

三、主程序
1.AXML代码

在界面上主要用了两个ItemsControl 分别用来动态加载“线头”和“方框” 要注意的事,这两个ItemsControl是叠加在一起的,这样是为了让用户感觉在同一区域操作。但是叠加后会有一个控件遮挡的问题(鼠标是点击不到下面的控件的)。为了解决这个问题需要为最上层的控件背景设置为: Background=“{x:Null}” 并且 把穿透设置为: IsHitTestVisible=“True” 。

<UserControl xmlns:Views="clr-namespace:BlankApp2.Views"  x:Class="BlankApp2.Views.PrismUserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"            
           xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:local1="clr-namespace:WPF_Common;assembly=WPF_Common"
             prism:ViewModelLocator.AutoWireViewModel="True" Background="Bisque">
    <UserControl.Resources>
        <ResourceDictionary>
            <!--多参数的初始化-->
            <local1:ObjectConvert x:Key="objectConverter"/>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid Background="Gainsboro" AllowDrop="True"   >
        <Grid.RowDefinitions>
            <RowDefinition Height="200*"/>
            <RowDefinition Height="50*"/>
        </Grid.RowDefinitions>
        <!--鼠标点击穿透:背景设置为: Background="{x:Null}" 并且 把穿透设置为: IsHitTestVisible="True"-->
        <ItemsControl x:Name="ic"  Background="{x:Null}" ItemsSource="{Binding objSource}"   Grid.Column="0" Grid.Row="0"  IsHitTestVisible="True"  >
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="{x:Type ContentPresenter}">
                    <Setter Property="Canvas.Left" Value="{Binding CnvLeft}"/>
                    <Setter Property="Canvas.Top" Value="{Binding CnvTop}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemTemplate >
                <DataTemplate >
                    <Thumb IsHitTestVisible="True"   Name="Thumbs" Uid="{Binding Name}" Tag="{Binding Name}" Background="Black"  VerticalAlignment="Center" HorizontalAlignment="Center"  TextElement.FontStyle="Normal" >
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="DragDelta">
                                <i:InvokeCommandAction Command="{Binding onDragDeltaCommand}"   PassEventArgsToCommand="True"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                        <Thumb.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="删除" Name="miClickMe"     >
                                    <i:Interaction.Triggers>
                                        <i:EventTrigger EventName="Click">
                                            <i:InvokeCommandAction Command="{Binding onThumbDeleteCommand}"   PassEventArgsToCommand="True"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                </MenuItem>
                            </ContextMenu>
                        </Thumb.ContextMenu>
                        <Thumb.Template>
                            <ControlTemplate  >
                                <!--将旋转的点改为中心RenderTransformOrigin = "0.5,0.5"-->
                                <Grid Name="wpl" IsHitTestVisible="True"  HorizontalAlignment="Center"
                                     VerticalAlignment="Center"  Tag="{Binding ID}" RenderTransformOrigin = "0.5,0.5"   Background="Transparent" Height="80"  Width="80" >
                                    <i:Interaction.Triggers>
                                        <i:EventTrigger EventName="Loaded">
                                            <i:InvokeCommandAction Command="{Binding onThumbLoadedCommand}"    PassEventArgsToCommand="True"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                    <!--将样式做成线头-->
                                    <Views:ArrowControl ID="{Binding ID}" x:Name="Arrow" Width="100" Height="50" />
                                    <Border Height="{Binding ElementName=wpl,Path=Height}" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" Width="6" Background="LightYellow" BorderThickness="1" Panel.ZIndex="1">
                                        <Grid Background="Black">
                                            <!--为线两端加上两个圆用于获取线的两端坐标-->
                                            <Ellipse Name="LineTop"   Height="10" Width="10" Stroke="White" Fill="White" VerticalAlignment="Top" HorizontalAlignment="Center" />
                                            <Ellipse Name="LineBottom"   Height="10" Width="10" Stroke="Gold" Fill="Gold" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
                                        </Grid>
                                    </Border>
                                </Grid>
                            </ControlTemplate>
                        </Thumb.Template>
                    </Thumb>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid Name="myG"  Grid.Column="0" Grid.Row="0"  IsHitTestVisible="True"  Background="Blue">                      
                    </Grid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <ItemsControl x:Name="ic2" ItemsSource="{Binding objSource2}" Background="{x:Null}"  Grid.Column="0" Grid.Row="0" Panel.ZIndex="1"  IsHitTestVisible="True" >
                        <ItemsControl.ItemTemplate >
                <DataTemplate >
                    <Thumb IsHitTestVisible="True" Name="Thumbs" Tag="{Binding Name}" Background="Black"  VerticalAlignment="Center" HorizontalAlignment="Center"  TextElement.FontStyle="Normal" >
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="DragDelta">
                                <i:InvokeCommandAction Command="{Binding onDragDeltaCommand}"   PassEventArgsToCommand="True"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                        <Thumb.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="删除" Name="miClickMe"     >
                                    <i:Interaction.Triggers>
                                        <i:EventTrigger EventName="Click">
                                            <i:InvokeCommandAction Command="{Binding onThumbDeleteCommand}"   PassEventArgsToCommand="True"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                </MenuItem>
                            </ContextMenu>
                        </Thumb.ContextMenu>
                        <Thumb.Template>
                            <ControlTemplate >
                                <!--将旋转的点改为中心RenderTransformOrigin = "0.5,0.5"-->
                                <Grid Name="wpl"   HorizontalAlignment="Center"
                                     VerticalAlignment="Center"  Tag="{Binding ID}" RenderTransformOrigin = "0.5,0.5"   Background="Transparent" Height="80"  Width="80" >
                                    <i:Interaction.Triggers>
                                        <!--控件加载后调用事件-->
                                        <i:EventTrigger EventName="Loaded">
                                            <i:InvokeCommandAction Command="{Binding onThumbLoadedCommand}"    PassEventArgsToCommand="True"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                    <!--将样式改成一个方框-->
                                    <Rectangle x:Name="rl" StrokeThickness="2" Stroke="Red" Fill="AliceBlue" Opacity="0.5"/>
                                </Grid>
                            </ControlTemplate>
                        </Thumb.Template>
                    </Thumb>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate  >
                    <Grid Name="myG2"  Grid.Column="0" Grid.Row="0"  Background="{x:Null}" IsHitTestVisible="True" >
                        <!--<Grid.Background>
                            <ImageBrush Stretch="None"
                        ImageSource="{Binding ImageSources}"
                        AlignmentY="Center"
                        AlignmentX="Center">
                            </ImageBrush>
                        </Grid.Background>-->
                    </Grid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <Button Content="线头" HorizontalAlignment="Left" Margin="88,34,0,0" Grid.Row="1" VerticalAlignment="Top" Command="{Binding AddT}"/>
        <Button Content="方框" HorizontalAlignment="Left" Margin="138,34,0,0" Grid.Row="1" VerticalAlignment="Top" Command="{Binding AddT2}"/>
        <Button Content="Get" HorizontalAlignment="Left" Margin="50,34,0,0" Grid.Row="1" VerticalAlignment="Top" Command="{Binding GetT}"  >
            <Button.CommandParameter>
                <MultiBinding Converter="{ StaticResource ResourceKey=objectConverter}">
                    <Binding ElementName="ic"></Binding>
                    <Binding ElementName="rtl"></Binding>
                </MultiBinding>
            </Button.CommandParameter>
        </Button>
        <TextBlock Grid.Row="1" Margin="200,34,0,0" Text="{Binding MarginValue}"/>
    </Grid>
</UserControl>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143

2.ViewModel端的CS代码

using Prism.Commands;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using WPF_Common;
namespace BlankApp2.ViewModels
{
    public class PrismUserControl1ViewModel : BindableBase, IModule
    {
        /// <summary>
        /// 拖动事件
        /// </summary>
        public DelegateCommand<object> onDragDeltaCommand { get; private set; }
        /// <summary>
        /// 控件加载后事件
        /// </summary>
        public DelegateCommand<object> onThumbLoadedCommand { get; private set; }
        /// <summary>
        /// 控件删除事件
        /// </summary>
        public DelegateCommand<object> onThumbDeleteCommand { get; private set; }
        /// <summary>
        /// 获取坐标事件
        /// </summary>
        public DelegateCommand<object> GetT { get; private set; }
        /// <summary>
        /// 添加线头事件
        /// </summary>
        public DelegateCommand AddT { get; private set; }
        /// <summary>
        /// 添加方框事件
        /// </summary>
        public DelegateCommand AddT2 { get; private set; }
        /// <summary>
        /// 加载本页面事件
        /// </summary>
        public PrismUserControl1ViewModel()
        {
            onDragDeltaCommand = new DelegateCommand<object>(onDragDelta);
            onThumbLoadedCommand = new DelegateCommand<object>(onThumbLoaded);
            onThumbDeleteCommand = new DelegateCommand<object>(onThumbDelete);
            AddT = new DelegateCommand(Add);
            AddT2 = new DelegateCommand(Add2);
            GetT = new DelegateCommand<object>(GetTOne);
        }
        /// <summary>
        /// 获取坐标事件
        /// </summary>
        /// <param name="obj"></param>
        private void GetTOne(object obj)
        {
            ///obj对像是View端传来的多参数
            object[] multiObj = obj as object[];
            Grid grid = FindChildControlHelper.GetChildObject<Grid>(multiObj[0] as DependencyObject, "myG");
            //Grid wplGrid = FindChildControlHelper.GetChildObject<Grid>(multiObj[0] as DependencyObject, "wpl");
            //Ellipse eps = FindChildControlHelper.GetChildObject<Ellipse>(multiObj[0] as DependencyObject , "ID_Head");
            //Ellipse LineTop = FindChildControlHelper.GetChildObject<Ellipse>(multiObj[0] as DependencyObject, "LineTop");
            //Ellipse LineBottom = FindChildControlHelper.GetChildObject<Ellipse>(multiObj[0] as DependencyObject, "LineBottom");
            //Rectangle obj1 = FindChildControlHelper.GetChildObject<Rectangle>(multiObj[0] as DependencyObject, "rtl");
            //System.Windows.Point pointeps = eps.TranslatePoint(new System.Windows.Point(), grid);
            //System.Windows.Point pointLineTop = LineTop.TranslatePoint(new System.Windows.Point(), grid);
            //System.Windows.Point pointLineBottom = LineBottom.TranslatePoint(new System.Windows.Point(), grid);

            for (int i = 0; i < objSource.Count; i++)
            {
                string sourceName = objSource[i].Name;
                Thumb Thumb1 = FindChildControlHelper.GetChildObjectByUid<Thumb>(multiObj[0] as DependencyObject, sourceName);
                Grid wplGrid = FindChildControlHelper.GetChildObject<Grid>(Thumb1 as DependencyObject, "wpl");
                System.Windows.Point point = Thumb1.TranslatePoint(new System.Windows.Point(), grid);//获取控件在grid的相对坐标
                MessageBox.Show(Thumb1.Name + ";" + Thumb1.Tag + "H:" + Thumb1.Height + ";" + "W:" + Thumb1.Width + "T:" + Thumb1.Margin.Top + ";" + "L:" + Thumb1.Margin.Left);
            }
        }
        private string _MarginValue;
        /// <summary>
        /// Margin四个方向的值
        /// </summary>
        public string MarginValue
        {
            get { return _MarginValue; }
            set { SetProperty(ref _MarginValue, value); }
        }
        private int _CanvasWidth = 600;
        public int CanvasWidth
        {
            get { return _CanvasWidth; }
            set { SetProperty(ref _CanvasWidth, value); }
        }
        private int _CanvasHeight = 700;
        public int CanvasHeight
        {
            get { return _CanvasHeight; }
            set { SetProperty(ref _CanvasHeight, value); }
        }
        /// <summary>
        /// 添加线头
        /// </summary>
        private void Add()
        {
            for (int i = 1; i < 2; i++)
            {
                int k = objSource.Count + 1;
                ObjEntity obj = new ObjEntity();
                obj.ID = k;
                obj.Name = "Name" + k;
                obj.onDragDeltaCommand = onDragDeltaCommand;
                obj.onThumbLoadedCommand = onThumbLoadedCommand;
                objSource.Add(obj);
            }
        }
        /// <summary>
        /// 添加方框
        /// </summary>
        private void Add2()
        {
            for (int i = 1; i < 2; i++)
            {
                int k = objSource.Count + 1;
                ObjEntity obj = new ObjEntity();
                obj.ID = k;
                obj.Name = "NameL" + k;
                obj.onDragDeltaCommand = onDragDeltaCommand;
                obj.onThumbLoadedCommand = onThumbLoadedCommand;
                objSource2.Add(obj);
            }
        }
        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="obj"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void onThumbDelete(object obj)
        {
            objSource.Remove(obj as ObjEntity);
        }
        /// <summary>
        /// 加载四周的Thumb
        /// </summary>
        /// <param name="obj"></param>
        private void onThumbLoaded(object obj)
        {
            if (obj is System.Windows.RoutedEventArgs e)
            {
                AdornerLayer layer = AdornerLayer.GetAdornerLayer((Grid)e.Source);
                layer.Add(new GridAdorner((Grid)e.Source));
            }
        }
        /// <summary>
        /// 拖动逻辑
        /// </summary>
        /// <param name="obj"></param>
        private void onDragDelta(object obj)
        {
            if (obj is DragDeltaEventArgs e)
            {
                if (((System.Windows.FrameworkElement)e.Source).DataContext is ObjEntity t)
                {
                    var m = ((System.Windows.FrameworkElement)e.Source).Margin;
                    MarginValue = $"; m.Left={m.Left} + e.HorizontalChange={e.HorizontalChange} ;;m.Top={m.Top} +  e.VerticalChange={e.VerticalChange};;  m.Right={m.Right};;m.Bottom={m.Bottom}";
                    ((System.Windows.FrameworkElement)e.Source).Margin = new Thickness(m.Left + e.HorizontalChange, m.Top + e.VerticalChange, m.Right, m.Bottom);
                }
            }
        }
        /// <summary>
        /// 线头
        /// </summary>
        private ObservableCollection<ObjEntity> _objSource = new ObservableCollection<ObjEntity>();
        public ObservableCollection<ObjEntity> objSource
        {
            get { return _objSource; }
            set { SetProperty(ref _objSource, value); }
        }
        /// <summary>
        /// 方框
        /// </summary>
        private ObservableCollection<ObjEntity> _objSource2 = new ObservableCollection<ObjEntity>();
        public ObservableCollection<ObjEntity> objSource2
        {
            get { return _objSource2; }
            set { SetProperty(ref _objSource2, value); }
        }
        public void OnInitialized(IContainerProvider containerProvider)
        {
            var regionManager = containerProvider.Resolve<IRegionManager>();
            regionManager.RegisterViewWithRegion("ContentRegion", typeof(Views.PrismUserControl1));
        }
        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            // throw new NotImplementedException();
        }
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198

总结

本文功能只是一个很简陋的Demo希望对大家有帮助。发辉你们的想想去拓展它吧。
源码下载
————————————————
版权声明:本文为CSDN博主「redfox6843」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/redfox6843/article/details/126117819

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值