WPF:鼠标拖曳、拖动控件

效果

在这里插入图片描述

布局

<Grid Grid.Row="0" Name="gridChart" Margin="50,20" SizeChanged="gridChart_SizeChanged">
    <Canvas Name="backCanvas" Background="Red">
        <Canvas Name="foreCanvas" Background="AliceBlue"/>
    </Canvas>
</Grid>

1、gridChart 是绘图区域,设置Margin是为了给坐标值腾出位置;SizeChanged事件,更新图
2、为了方便管理,用两个Canvas绘图
3、backCanvas 背景画板用于画一些辅助性的东西。如下图的网格线,背景颜色,坐标值0、50、100那些
4、foreCanvas 前景画蓝色方块,以及包裹它的四条直线,和四个坐标值
5、拖动效果通过方块的三个事件完成:MouseLeftButtonDown、MouseMove、MouseLeftButtonUp
在这里插入图片描述

逻辑代码

1、gridChart_SizeChanged

当窗口大小改变时,获取gridChart 的大小,给Canvas赋值。删除前景画板中的所有东西,背景画板留下一个,就是前景画板。
DrawGridLine:在背景画板中画网格线,参数是网格线的横线和纵向偏移量
DrawRec:在前景画板中画蓝色方块,参数是方块的长宽

private void gridChart_SizeChanged(object sender, SizeChangedEventArgs e)
{
    backCanvas.Width = gridChart.ActualWidth;
    backCanvas.Height = gridChart.ActualHeight;
    foreCanvas.Width = gridChart.ActualWidth;
    foreCanvas.Height = gridChart.ActualHeight;

    backCanvas.Children.RemoveRange(1, backCanvas.Children.Count - 1);
    foreCanvas.Children.Clear();

    DrawGridLine(50, 50);
    DrawRec(100, 100);
}
2、DrawGridLine(double dx, double dy)

使用Line依次画垂直的网格和用Label画的横坐标。
使用Line依次画水平的网格和用Label画的纵坐标。

private void DrawGridLine(double dx, double dy)
{
    // 背景边框
    Border border = new Border()
    {
        Width = backCanvas.Width,
        Height = backCanvas.Height,
        BorderBrush = new SolidColorBrush(Color.FromRgb(0, 24, 113)),
        BorderThickness = new Thickness(1)
    };
    backCanvas.Children.Add(border);
    // 网格
    Line line = new Line();
    // 坐标值
    Label label = new Label();
    // 添加垂直网格
    double gridX = 0;
    double gridY = 0;
    while (gridX < backCanvas.Width)
    {
        line = new Line()
        {
            StrokeThickness = 0.1,
            Stroke = Brushes.Black,
            X1 = gridX,
            Y1 = 0,
            X2 = gridX,
            Y2 = backCanvas.Height
        };
        backCanvas.Children.Add(line);

        // 添加横坐标标签
        label = new Label();
        label.Content = gridX;
        backCanvas.Children.Add(label);
        Canvas.SetLeft(label, gridX - 10);
        Canvas.SetTop(label, -20);
        gridX += dx;
    }
    // 添加水平网格
    while (gridY < backCanvas.Height)
    {
        line = new Line()
        {
            StrokeThickness = 0.1,
            Stroke = Brushes.Black,
            X1 = 0,
            Y1 = gridY,
            X2 = backCanvas.Width,
            Y2 = gridY
        };
        backCanvas.Children.Add(line);

        // 添加横坐标标签
        label = new Label();
        label.Content = gridY;
        backCanvas.Children.Add(label);
        Canvas.SetLeft(label, -40);
        Canvas.SetTop(label, gridY -10);
        gridY += dy;
    }
}
3、DrawRec(double width, double height)

为方块添加三个事件MouseLeftButtonDown、MouseMove、MouseLeftButtonUp
Draw4Lines : 同时画4条包裹它的直线,参数是该方块

private void DrawRec(double width, double height)
{
    // 方块
    Rectangle rec = new Rectangle()
    {
        Width = width,
        Height = height,
        Fill = new SolidColorBrush(Color.FromRgb(0, 24, 113))
    };
    foreCanvas.Children.Add(rec);
    Canvas.SetLeft(rec, foreCanvas.Width / 2 - rec.Width / 2);
    Canvas.SetTop(rec, foreCanvas.Height / 2 - rec.Height / 2);
    // 拖动方块事件
    rec.MouseLeftButtonDown += Rec_MouseLeftButtonDown;
    rec.MouseMove += Rec_MouseMove;
    rec.MouseLeftButtonUp += Rec_MouseLeftButtonUp;
    Draw4Lines(rec);
}
4、Draw4Lines(Rectangle rec)

4条直线由8个坐标点构成,pointList 就是存储该8个点,然后遍历画出,同时画4个由Label构成的坐标值。

private void Draw4Lines(Rectangle rec)
{
    List<Tuple<double, double>> pointList = new List<Tuple<double, double>>()
    {
        // 左竖线
        new Tuple<double, double>(Canvas.GetLeft(rec), 0),
        new Tuple<double, double>(Canvas.GetLeft(rec), foreCanvas.Height),
        // 右竖线
        new Tuple<double, double>(Canvas.GetLeft(rec) + rec.Width, 0),
        new Tuple<double, double>(Canvas.GetLeft(rec) + rec.Width, foreCanvas.Height),
        // 上横线
        new Tuple<double, double>(0, Canvas.GetTop(rec)),
        new Tuple<double, double>(foreCanvas.Width, Canvas.GetTop(rec)),
        // 下横线
        new Tuple<double, double>(0, Canvas.GetTop(rec) + rec.Height),
        new Tuple<double, double>(foreCanvas.Width, Canvas.GetTop(rec) + rec.Height)
    };
    for (int i = 0; i < pointList.Count; i += 2)
    {
        Line line = new Line()
        {
            StrokeThickness = 1,
            Stroke = new SolidColorBrush(Color.FromRgb(225, 88, 93)),
            X1 = pointList[i].Item1,
            Y1 = pointList[i].Item2,
            X2 = pointList[i + 1].Item1,
            Y2 = pointList[i + 1].Item2
        };
        foreCanvas.Children.Add(line);
        // 坐标值
        Label label = new Label()
        {
            Content = (i < 4) ? pointList[i].Item1 : pointList[i].Item2,
            Foreground = new SolidColorBrush(Color.FromRgb(255, 181, 73)),
            Background = new SolidColorBrush(Color.FromRgb(19, 51, 76))
        };
        foreCanvas.Children.Add(label);
        Canvas.SetLeft(label, pointList[i].Item1);
        Canvas.SetTop(label, pointList[i].Item2);
    }
}
5、鼠标事件

鼠标按下、释放,控制鼠标形状。

private void Rec_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    ((Rectangle)sender).Cursor = Cursors.Hand;
}

private void Rec_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    ((Rectangle)sender).ReleaseMouseCapture();
    ((Rectangle)sender).Cursor = Cursors.Arrow;
}

鼠标移动时,判断左键是按压状态;

获取鼠标的位置,计算出方块左上角顶点的位置,也就是 (marginLeft ,marginTop),同时判断是否超过边界;

如没有,则移动方块位置;

同时更新4条直线的位置。

private void Rec_MouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        // 鼠标位置
        Point point = e.GetPosition(foreCanvas);
        Rectangle rec = (Rectangle)sender;
        double marginLeft = point.X - rec.Width / 2;
        double marginTop = point.Y - rec.Height / 2;
        // 超出边界
        if (marginLeft < 0)
        {
            marginLeft = 0;
        }
        else if ((marginLeft + rec.Width) > foreCanvas.Width)
        {
            marginLeft = foreCanvas.Width - rec.Width;
        }
        if (marginTop < 0)
        {
            marginTop = 0;
        }
        else if ((marginTop + rec.Height) > foreCanvas.Height)
        {
            marginTop = foreCanvas.Height - rec.Height;
        }
        Canvas.SetLeft(rec, marginLeft);
        Canvas.SetTop(rec, marginTop);
        // 移除四条边框线,重新画
        foreCanvas.Children.RemoveRange(1, foreCanvas.Children.Count - 1);
        Draw4Lines(rec);
    }
}

小Bug

鼠标移动的太快,方块会不动
在这里插入图片描述

鼠标移动超过gridChart,例如超过右边界,此时不能上下移动,如下图,注意鼠标一直是按压状态
在这里插入图片描述

解决小bug

更新:2021-09-03
解决方法:使小方块强制获取到鼠标CaptureMouse

private void Rec_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    ((Rectangle)sender).CaptureMouse();
    ((Rectangle)sender).Cursor = Cursors.Hand;
}

因为,MouseMove事件,必须是鼠标处于控件之上才能发生,包括MouseDownMouseUp事件。
例如鼠标悬浮在控件之上,按下鼠标,此时会触发按下事件;此时鼠标移开到控件外,注意鼠标一直是按下状态,再松开,此时不会触发释放事件。因为鼠标不在控件之上~

解决后的效果,快的飞起~~~
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值