尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

WPF实现组件拖动(Canvas)

WPF实现组件拖动(Canvas)
📅 发布时间:2026/6/19 18:26:16

xaml:

<Window x:Class="WpfApp3Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3Test"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">

<Window.Resources>
<!-- 标题栏样式(针对Border类型) -->
<Style x:Key="BorderStyle" TargetType="Border">
<Setter Property="Height" Value="30"/>
<Setter Property="Background" Value="YellowGreen"/>
</Style>

<!-- TextBlock样式(针对TextBlock类型) -->
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Yellow"/>
<Setter Property="FontSize" Value="20"/>

</Style>
</Window.Resources>

 

<Grid x:Name="parentGrid" >
<Canvas x:Name="parentCanvas">
<!-- 组件A:Canvas(下层,ZIndex=1) -->
<Canvas x:Name="ComponentA" Panel.ZIndex="1" MouseLeftButtonDown="A_MouseLeftButtonDown">
<!-- Canvas内可放置绘制元素(示例:简单矩形) -->
<Rectangle Width="500" Height="300" Fill="LightBlue" />
<TextBlock Canvas.Left="20" Canvas.Top="20" Text="组件A(Canvas)" FontSize="16" />
</Canvas>

<!-- 组件B:Border(上层,ZIndex=2) -->
<Border x:Name="ComponentB"
Width="300" Height="250"
BorderBrush="Black" BorderThickness="1"
Canvas.Left="0" Canvas.Top="0"
Panel.ZIndex="2">
<!-- 层级高于A -->

<!-- B的内部布局:两行Grid -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<!-- 标题栏行(固定高度) -->
<RowDefinition Height="*" />
<!-- 内容区行(占剩余高度) -->
</Grid.RowDefinitions>

<!-- 标题栏(第一行) -->
<Border x:Name="TitleBar"
Grid.Row="0"
Style="{StaticResource BorderStyle}"
MouseLeftButtonDown="B_TitleBar_MouseLeftButtonDown" MouseMove="B_TitleBar_MouseMove">
<TextBlock Text="B的标题栏" Style="{StaticResource TextBlockStyle}" VerticalAlignment="Center" Margin="5" />
</Border>

<!-- 内容区(第二行):ScrollViewer + ItemsControl -->
<ScrollViewer Grid.Row="1"
VerticalScrollBarVisibility="Auto" IsHitTestVisible="False">
<!-- 内容超出时显示滚动条 -->
<ItemsControl x:Name="ContentItems">
<!-- 列表项模板(简单文本展示) -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- 每个列表项显示文本(绑定示例) -->
<TextBlock Text="{Binding}" Margin="5" FontSize="14" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Border>
</Canvas>
</Grid>
</Window>

 

xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp3Test
{
/// <summary>
///
/// /// MainWindow.xaml 的交互逻辑
/// /// </summary>
public partial class MainWindow : Window
{
private bool _isDragging = false;

// 是否正在拖拽
private Point _lastMousePosition;

// 上一次鼠标位置(关键:每次移动后更新,避免偏移叠加)
private Point _lastTitleBarPosition;

// 存储ComponentB相对于parentCanvas的位置
public MainWindow()
{
InitializeComponent();
ContentItems.ItemsSource = new List<string> { "项1", "项2", "项3" };
}

private void A_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ MessageBox.Show("点击了组件A"); }

private void B_TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!_isDragging)
{
// 1. 启动拖拽:记录初始状态
_isDragging = true;
_lastMousePosition = e.GetPosition(parentCanvas);
//记录鼠标当前位置(相对于父Canvas)
_lastTitleBarPosition = new Point(
Canvas.GetLeft(ComponentB),
Canvas.GetTop(ComponentB)

);
TitleBar.CaptureMouse(); // 捕获鼠标,确保移动时事件不丢失
}
else
{
//2. 停止拖拽:重置状态 _
_isDragging = false;
// 释放鼠标捕获
TitleBar.ReleaseMouseCapture();
}
e.Handled = true;
// 阻止事件传递到组件A
}

private void B_TitleBar_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging && TitleBar.IsMouseCaptured)
{
// 1. 获取当前鼠标相对于parentCanvas的位置
Point currentMousePos = e.GetPosition(parentCanvas);
// 2. 计算本次移动的偏移量(当前 - 上一次鼠标位置)
double offsetX = currentMousePos.X - _lastMousePosition.X;
double offsetY = currentMousePos.Y - _lastMousePosition.Y;
// 3. 更新ComponentB的位置(基于Canvas的Left/Top)
double newLeft = _lastTitleBarPosition.X + offsetX;
double newTop = _lastTitleBarPosition.Y + offsetY;
Canvas.SetLeft(ComponentB, newLeft);
Canvas.SetTop(ComponentB, newTop);
// 4. 更新“上一次”的鼠标位置和组件位置
_lastMousePosition = currentMousePos;

_lastTitleBarPosition = new Point(
Canvas.GetLeft(ComponentB),
Canvas.GetTop(ComponentB)
);
}
}
}
}

相关新闻

  • 2025 年最新测控终端厂家推荐:符合国标 + 数据透传技术,靠谱企业深度测评报告4G 测控终端/远程测控终端/物联网测控终端/测控终端 RTU 公司推荐
  • React系列教程:2. 定义一个组件
  • 质数筛

最新新闻

  • 深度解析macOS滚动事件拦截:构建专业级定制插件的完整指南
  • 常州多年黄金回收攻略,三十年实体经营,收的顶本地口碑有保障 - 奢侈品回收测评
  • 01_系统架构设计
  • 如何免费实现专业级直播抠像:obs-backgroundremoval插件完全指南
  • 新手必看!抖音保存视频到相册的详细步骤技巧 - 工具软件使用方法推荐
  • LaTeX长表格排版进阶:如何用longtable宏包实现跨页表格的精细控制?

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号