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希望对大家有帮助。发辉你们的想想去拓展它吧。
源码下载