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

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


前言

本文中所开发的功能是为了给后台目标检测算法做区域标注的一个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; }
    }
}

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;
        }
    }
}

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;
        }

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.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>

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.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>

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();
        }
    }
}

总结

本文功能只是一个很简陋的Demo希望对大家有帮助。发辉你们的想想去拓展它吧。
源码下载

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值