参考
一、图像缩放
滚轮放大缩小图像
private void Grid_MouseWheel(object sender, MouseWheelEventArgs e)
{
var point = e.GetPosition(canvas);
var delta = e.Delta * 0.002;
DoImageWheelZoom(point, delta);
RefreshCrossLine(e.GetPosition(imgControl));
}
/// <summary>
/// 图像缩放
/// </summary>
/// <param name="point"></param>
/// <param name="delta"></param>
private void DoImageWheelZoom(Point point, double delta)
{
if (scaleTransform.ScaleX + delta < 1) delta = 1 - scaleTransform.ScaleX;
if (delta == 0) return;
scaleTransform.ScaleX += delta;
scaleTransform.ScaleY += delta;
translateTransform.X -= point.X * delta;
translateTransform.Y -= point.Y * delta;
}
/// <summary>
/// 刷新辅助线大小位置
/// </summary>
/// <param name="mouse"></param>
private void RefreshCrossLine(Point mouse)
{
if (!crossLineShow) { return; }
double scale = scaleTransform.ScaleX;
double W = imgControl.ActualWidth;
double H = imgControl.ActualHeight;
double w = canvas.ActualWidth;
double h = canvas.ActualHeight;
lineX.StartPoint = new Point(mouse.X, Math.Max(0, translateTransform.Y + (H - h) / 2));
lineX.EndPoint = new Point(mouse.X, Math.Min(H, translateTransform.Y + h * scale + (H - h) / 2));
lineY.StartPoint = new Point(Math.Max(0, translateTransform.X + (W - w) / 2), mouse.Y);
lineY.EndPoint = new Point(Math.Min(W, translateTransform.X + w * scale + (W - w) / 2), mouse.Y);
}
二、图像拖动
空格键按下拖放图像
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
//空格键设置可拖动图像
if (e.Key == Key.Space)
{
isSpaceDown = true;
canvas.Cursor = Cursors.Hand;
crossLineShow = false;
ClearCrossLine();
}
}
private void MainWindow_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
isSpaceDown = false;
canvas.Cursor = Cursors.Arrow;
crossLineShow = isLabel;
}
}
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
#region 图像拖动
if (isSpaceDown && canvas.IsMouseOver)
{
isDrag = true;
preMovePoint = e.GetPosition(this);
canvas.CaptureMouse();
}
#endregion
}
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
#region 图像拖动
if (isDrag)
{
var point = e.GetPosition(this); // 获取移动后的位置
var dp = point - preMovePoint; // 计算偏移量
preMovePoint = point;
translateTransform.X += dp.X;
translateTransform.Y += dp.Y;
return;
}
#endregion
}
三、辅助线
bool crossLineShow = false;
LineGeometry lineX = new LineGeometry(), lineY = new LineGeometry();
GeometryGroup linegroup = new GeometryGroup();
/// <summary>
/// 隐藏辅助线
/// </summary>
private void ClearCrossLine()
{
lineX.StartPoint = new Point();
lineX.EndPoint = new Point();
lineY.StartPoint = new Point();
lineY.EndPoint = new Point();
}
/// <summary>
/// 刷新辅助线大小位置
/// </summary>
/// <param name="mouse"></param>
private void RefreshCrossLine(Point mouse)
{
if (!crossLineShow) { return; }
double scale = scaleTransform.ScaleX;
double W = imgControl.ActualWidth;
double H = imgControl.ActualHeight;
double w = canvas.ActualWidth;
double h = canvas.ActualHeight;
lineX.StartPoint = new Point(mouse.X, Math.Max(0, translateTransform.Y + (H - h) / 2));
lineX.EndPoint = new Point(mouse.X, Math.Min(H, translateTransform.Y + h * scale + (H - h) / 2));
lineY.StartPoint = new Point(Math.Max(0, translateTransform.X + (W - w) / 2), mouse.Y);
lineY.EndPoint = new Point(Math.Min(W, translateTransform.X + w * scale + (W - w) / 2), mouse.Y);
}
四、辅助大图
标注时显示光标附近放大图像
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
#region 辅助大图
if (isLabel && (canvas.IsMouseOver || crossline.IsMouseOver))
{
RefreshBigImagePosition(e.GetPosition(canvas));
}
#endregion
}
/// <summary>
/// 刷新辅助大图位置
/// </summary>
/// <param name="point"></param>
private void RefreshBigImagePosition(Point point)
{
bigImg.Margin = new Thickness(-point.X * bigImaScale + 50, -point.Y * bigImaScale + 50, 0, 0);
}
五、标注框
internal class RectPanel : Grid
{
TranslateTransform translateTransform = new TranslateTransform();
Rectangle rect;
Panel parentPanel;
public ListBoxItem listBoxItem = new ListBoxItem();
SolidColorBrush color = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));
ObservableCollection<RectPanel> rectPanels;
ObservableCollection<ListBoxItem> labelItems;
public RectPanel(ObservableCollection<RectPanel> rectPanels, ObservableCollection<ListBoxItem> labelItems)
{
this.MouseLeftButtonDown += RectPanel_MouseLeftButtonDown;
this.MouseLeftButtonUp += RectPanel_MouseLeftButtonUp;
this.MouseMove += RectPanel_MouseMove;
this.MouseEnter += RectPanel_MouseEnter;
this.MouseLeave += RectPanel_MouseLeave;
this.HorizontalAlignment = HorizontalAlignment.Left;
this.VerticalAlignment = VerticalAlignment.Top;
this.RenderTransform = translateTransform;
this.rectPanels = rectPanels;
this.labelItems = labelItems;
this.rectPanels.Add(this);
this.labelItems.Add(listBoxItem);
listBoxItem.PreviewMouseLeftButtonUp += ListBoxItem_PreviewMouseLeftButtonUp;
listBoxItem.Focusable = false;
#region 边框
rect = new Rectangle();
rect.Stroke = color;
rect.StrokeThickness = 1;
this.Children.Add(rect);
#endregion
#region 拖拽点
Rectangle rectLeftTop = new Rectangle();
rectLeftTop.Width = 3;
rectLeftTop.Height = 3;
rectLeftTop.Fill = color;
rectLeftTop.HorizontalAlignment = HorizontalAlignment.Left;
rectLeftTop.VerticalAlignment = VerticalAlignment.Top;
rectLeftTop.Margin = new Thickness(-1, -1, 0, 0);
rectLeftTop.Cursor = Cursors.SizeNWSE;
rectLeftTop.MouseLeftButtonDown += Rect_MouseLeftButtonDown;
rectLeftTop.MouseLeftButtonUp += Rect_MouseLeftButtonUp;
rectLeftTop.MouseMove += Rect_MouseMove;
this.Children.Add(rectLeftTop);
Rectangle rectRigthTop = new Rectangle();
rectRigthTop.Width = 3;
rectRigthTop.Height = 3;
rectRigthTop.Fill = color;
rectRigthTop.HorizontalAlignment = HorizontalAlignment.Right;
rectRigthTop.VerticalAlignment = VerticalAlignment.Top;
rectRigthTop.Margin = new Thickness(0, -1, -1, 0);
rectRigthTop.Cursor = Cursors.SizeNESW;
rectRigthTop.MouseLeftButtonDown += Rect_MouseLeftButtonDown;
rectRigthTop.MouseLeftButtonUp += Rect_MouseLeftButtonUp;
rectRigthTop.MouseMove += Rect_MouseMove;
this.Children.Add(rectRigthTop);
Rectangle rectRightBottom = new Rectangle();
rectRightBottom.Width = 3;
rectRightBottom.Height = 3;
rectRightBottom.Fill = color;
rectRightBottom.HorizontalAlignment = HorizontalAlignment.Right;
rectRightBottom.VerticalAlignment = VerticalAlignment.Bottom;
rectRightBottom.Margin = new Thickness(0, 0, -1, -1);
rectRightBottom.Cursor = Cursors.SizeNWSE;
rectRightBottom.MouseLeftButtonDown += Rect_MouseLeftButtonDown;
rectRightBottom.MouseLeftButtonUp += Rect_MouseLeftButtonUp;
rectRightBottom.MouseMove += Rect_MouseMove;
this.Children.Add(rectRightBottom);
Rectangle rectLeftBottom = new Rectangle();
rectLeftBottom.Width = 3;
rectLeftBottom.Height = 3;
rectLeftBottom.Fill = color;
rectLeftBottom.HorizontalAlignment = HorizontalAlignment.Left;
rectLeftBottom.VerticalAlignment = VerticalAlignment.Bottom;
rectLeftBottom.Margin = new Thickness(-1, 0, 0, -1);
rectLeftBottom.Cursor = Cursors.SizeNESW;
rectLeftBottom.MouseLeftButtonDown += Rect_MouseLeftButtonDown;
rectLeftBottom.MouseLeftButtonUp += Rect_MouseLeftButtonUp;
rectLeftBottom.MouseMove += Rect_MouseMove;
this.Children.Add(rectLeftBottom);
#endregion
}
/// <summary>
/// 标注位置大小信息
/// </summary>
public Rect GetLabelInfo
{
get
{
return new Rect(new Point(translateTransform.X, translateTransform.Y), new Size(this.ActualWidth, this.ActualHeight));
}
}
#region 事件
#region 拖动
bool isDrag = false;
Point mouseDownPoint = new Point(0, 0);
Point translateTransformPoint = new Point(0, 0);
private void RectPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isDrag = true;
translateTransformPoint.X = translateTransform.X;
translateTransformPoint.Y = translateTransform.Y;
mouseDownPoint = e.GetPosition(parentPanel);
this.CaptureMouse();
}
private void RectPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDrag = false;
this.ReleaseMouseCapture();
}
private void RectPanel_MouseMove(object sender, MouseEventArgs e)
{
if (isDrag && e.LeftButton == MouseButtonState.Pressed)
{
var point = e.GetPosition(parentPanel); // 获取移动后的位置
var dp = point - mouseDownPoint; // 计算偏移量
double x = translateTransformPoint.X + dp.X, y = translateTransformPoint.Y + dp.Y;
//拖动边界
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + this.ActualWidth > parentPanel.ActualWidth) x = parentPanel.ActualWidth - this.ActualWidth;
if (y + this.ActualHeight > parentPanel.ActualHeight) y = parentPanel.ActualHeight - this.ActualHeight;
translateTransform.X = x;
translateTransform.Y = y;
}
}
#endregion
#region 移入移出背景光标
private void RectPanel_MouseEnter(object sender, MouseEventArgs e)
{
MouseEnterStyle();
}
private void RectPanel_MouseLeave(object sender, MouseEventArgs e)
{
MouseLeaveStyle();
}
#endregion
bool isChange = false;
Point startPoint = new Point();
private void Rect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
Rectangle rect = sender as Rectangle;
if (rect == null) return;
isChange = true;
rect.CaptureMouse();
//左上角
if (rect.HorizontalAlignment == HorizontalAlignment.Left && rect.VerticalAlignment == VerticalAlignment.Top)
{
//右下角坐标
startPoint.X = translateTransform.X + this.ActualWidth;
startPoint.Y = translateTransform.Y + this.ActualHeight;
}
//右上角
else if (rect.HorizontalAlignment == HorizontalAlignment.Right && rect.VerticalAlignment == VerticalAlignment.Top)
{
//左下角坐标
startPoint.X = translateTransform.X;
startPoint.Y = translateTransform.Y + this.ActualHeight;
}
//右下角
else if (rect.HorizontalAlignment == HorizontalAlignment.Right && rect.VerticalAlignment == VerticalAlignment.Bottom)
{
//左上角坐标
startPoint.X = translateTransform.X;
startPoint.Y = translateTransform.Y;
}
//左下角
else if (rect.HorizontalAlignment == HorizontalAlignment.Left && rect.VerticalAlignment == VerticalAlignment.Bottom)
{
//右上角坐标
startPoint.X = translateTransform.X + this.ActualWidth;
startPoint.Y = translateTransform.Y;
}
}
private void Rect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Rectangle rect = sender as Rectangle;
if (rect == null) return;
isChange = false;
rect.ReleaseMouseCapture();
}
private void Rect_MouseMove(object sender, MouseEventArgs e)
{
if (isChange)
{
ResizeChange(startPoint, e.GetPosition(parentPanel));
}
}
private void ListBoxItem_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
listBoxItem.IsSelected = true;
}
#endregion
/// <summary>
/// 大小位置变更
/// </summary>
/// <param name="startPoint"></param>
/// <param name="endPoint"></param>
public void ResizeChange(Point startPoint, Point endPoint)
{
if (parentPanel == null) parentPanel = this.Parent as Panel;
#region 边界
if (endPoint.X < 0) endPoint.X = 0;
if (endPoint.Y < 0) endPoint.Y = 0;
if (endPoint.X > parentPanel.ActualWidth) endPoint.X = parentPanel.ActualWidth;
if (endPoint.Y > parentPanel.ActualHeight) endPoint.Y = parentPanel.ActualHeight;
#endregion
double width = Math.Abs(startPoint.X - endPoint.X);
double height = Math.Abs(startPoint.Y - endPoint.Y);
if (width <= 0 || height <= 0) return;
double x = Math.Min(startPoint.X, endPoint.X);
double y = Math.Min(startPoint.Y, endPoint.Y);
this.rect.Width = width;
this.rect.Height = height;
translateTransform.X = x;
translateTransform.Y = y;
}
/// <summary>
/// 标注框删除
/// </summary>
public void RectDel()
{
if (rectPanels.Contains(this)) rectPanels.Remove(this);
if (labelItems.Contains(listBoxItem)) labelItems.Remove(listBoxItem);
if (parentPanel.Children.Contains(this)) parentPanel.Children.Remove(this);
}
/// <summary>
/// 移入样式
/// </summary>
public void MouseEnterStyle()
{
this.Cursor = Cursors.Hand;
this.Background = new SolidColorBrush(Color.FromArgb(55, 0, 255, 0));
Panel.SetZIndex(this, 10000);
}
/// <summary>
/// 移出样式
/// </summary>
public void MouseLeaveStyle()
{
this.Background = new SolidColorBrush(Colors.Transparent);
Panel.SetZIndex(this, 0);
}
}
六、标注类型弹窗
internal class LabelPopupWindow : Window
{
ObservableCollection<ListBoxItem> listBoxItems;
public LabelPopupWindow(ObservableCollection<ListBoxItem> listBoxItems)
{
this.listBoxItems = listBoxItems;
WinInit();
}
public new bool? Show()
{
return this.ShowDialog();
}
TextBox textBox;
ListBox listBox;
#region 初始化
private void WinInit()
{
this.Width = 300;
this.Height = 400;
this.ResizeMode = ResizeMode.NoResize;
this.WindowStyle = WindowStyle.None;
this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
#region 边框
Border border = new Border();
border.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));
border.BorderThickness = new Thickness(1);
this.Content = border;
#endregion
#region 布局容器
Grid grid = new Grid();
grid.Margin = new Thickness(20);
border.Child = grid;
RowDefinition row1 = new RowDefinition();
RowDefinition row2 = new RowDefinition();
RowDefinition row3 = new RowDefinition();
row1.Height = new GridLength(50);
row3.Height = new GridLength(50);
grid.RowDefinitions.Add(row1);
grid.RowDefinitions.Add(row2);
grid.RowDefinitions.Add(row3);
#endregion
#region 标注类型
textBox = new TextBox();
textBox.Height = 35;
textBox.VerticalContentAlignment = VerticalAlignment.Center;
textBox.VerticalAlignment = VerticalAlignment.Center;
Grid.SetRow(textBox, 0);
grid.Children.Add(textBox);
#endregion
#region 标注类型集合
listBox = new ListBox();
listBox.Margin = new Thickness(0, 20, 0, 20);
Grid.SetRow(listBox, 1);
listBox.ItemsSource = listBoxItems;
grid.Children.Add(listBox);
//默认上次选中项
ListBoxItem sltItem = listBoxItems.FirstOrDefault(item => item.IsSelected);
if (sltItem != null)
{
listBox.SelectedIndex = listBoxItems.IndexOf(sltItem);
}
#region 数据绑定
this.DataContext = this;
Binding binding = new Binding("SelectedItem.Content")
{
Source = listBox,
Mode = BindingMode.OneWay
};
textBox.SetBinding(TextBox.TextProperty, binding);
#endregion
#endregion
#region 确定 | 取消
StackPanel stackPanel = new StackPanel();
stackPanel.Orientation = Orientation.Horizontal;
stackPanel.FlowDirection = FlowDirection.RightToLeft;
Grid.SetRow(stackPanel, 2);
grid.Children.Add(stackPanel);
Button btnCancel = new Button();
btnCancel.Width = 80;
btnCancel.Height = 30;
btnCancel.Content = "取消";
btnCancel.HorizontalAlignment = HorizontalAlignment.Right;
btnCancel.VerticalAlignment = VerticalAlignment.Center;
btnCancel.Click += BtnCancel_Click;
stackPanel.Children.Add(btnCancel);
Button btnCfm = new Button();
btnCfm.Margin = new Thickness(20, 0, 20, 0);
btnCfm.Width = 80;
btnCfm.Height = 30;
btnCfm.Content = "确定";
btnCfm.HorizontalAlignment = HorizontalAlignment.Right;
btnCfm.VerticalAlignment = VerticalAlignment.Center;
btnCfm.Click += BtnCfm_Click;
stackPanel.Children.Add(btnCfm);
#endregion
}
#endregion
/// <summary>
/// 标注类型
/// </summary>
public string label;
public string Label
{
get
{
return label;
}
private set
{
label = value;
}
}
/// <summary>
/// 确定
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnCfm_Click(object sender, RoutedEventArgs e)
{
Label = textBox.Text.Trim();
if (string.IsNullOrWhiteSpace(label))
{
this.DialogResult = false;
return;
}
//检测是否存在相同标注类型,不存在添加
if (!listBoxItems.Any(item => item.Content.ToString() == label))
{
ListBoxItem listBoxItem = new ListBoxItem();
listBoxItem.Content = label;
listBoxItems.Add(listBoxItem);
}
//设置默认选中最后标注类型
foreach (ListBoxItem item in listBoxItems)
{
if (item.Content.Equals(Label))
{
item.IsSelected = true;
}
else
{
item.IsSelected = false;
}
}
this.DialogResult = true;
}
/// <summary>
/// 取消
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
}
}
七、删除
Delete快捷键删除选中项标注
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
//空格键设置可拖动图像
if (e.Key == Key.Space)
{
isSpaceDown = true;
canvas.Cursor = Cursors.Hand;
crossLineShow = false;
ClearCrossLine();
}
//Delete键删除标注
else if (e.Key == Key.Delete)
{
if (listBox.SelectedIndex < 0) return;
rectPanels[listBox.SelectedIndex].RectDel();
}
}