grid布局
2025/3/18原创大约 3 分钟约 903 字
1. 拖动效果
1.1. 前端代码
<Window x:Class="WpfApp10.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:WpfApp10"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid x:Name="MainGrid" ShowGridLines="True" Background="AliceBlue">
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="5"/>
<Setter Property="Padding" Value="10"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
</Grid>
<!-- 拖放位置指示器 -->
<Rectangle x:Name="DropIndicator"
Stroke="DarkOrange"
StrokeThickness="2"
Fill="#20FF4500"
Visibility="Collapsed"
RadiusX="5"
RadiusY="5"/>
</Grid>
</Window>
1.2. 后端代码
using System.IO;
using System.Text;
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.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp10
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Point _dragStartPosition;
private UIElement _draggedButton;
private int _originalRow, _originalColumn;
private TranslateTransform _transform;
private Button _targetButton;
public MainWindow()
{
InitializeComponent();
InitializeGrid(4, 4);
}
private void InitializeGrid(int rows, int cols)
{
MainGrid.RowDefinitions.Clear();
MainGrid.ColumnDefinitions.Clear();
// 创建行列定义
for (int i = 0; i < rows; i++)
MainGrid.RowDefinitions.Add(new RowDefinition());
for (int i = 0; i < cols; i++)
MainGrid.ColumnDefinitions.Add(new ColumnDefinition());
// 添加按钮
for (int r = 0; r < rows; r++)
{
for (int c = 0; c < cols; c++)
{
var btn = new Button
{
Content = $"{r}-{c}",
Background = new LinearGradientBrush(Colors.LightBlue, Colors.SteelBlue, 45),
Foreground = Brushes.White
};
Grid.SetRow(btn, r);
Grid.SetColumn(btn, c);
// 事件处理
btn.PreviewMouseLeftButtonDown += Button_PreviewMouseLeftButtonDown;
btn.PreviewMouseMove += Button_PreviewMouseMove;
btn.PreviewMouseLeftButtonUp += Button_PreviewMouseLeftButtonUp;
MainGrid.Children.Add(btn);
}
}
}
private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_draggedButton = sender as Button;
if (_draggedButton == null) return;
_dragStartPosition = e.GetPosition(MainGrid);
_originalRow = Grid.GetRow(_draggedButton);
_originalColumn = Grid.GetColumn(_draggedButton);
// 初始化变换
_transform = new TranslateTransform();
_draggedButton.RenderTransform = _transform;
// 捕获鼠标
_draggedButton.CaptureMouse();
// 设置半透明效果
_draggedButton.Opacity = 0.7;
}
private void Button_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_draggedButton == null || e.LeftButton != MouseButtonState.Pressed) return;
var currentPos = e.GetPosition(MainGrid);
var offset = currentPos - _dragStartPosition;
// 更新按钮位置
_transform.X = offset.X;
_transform.Y = offset.Y;
// 更新拖放指示器
UpdateDropIndicator(currentPos);
}
private void UpdateDropIndicator(Point position)
{
int targetRow = GetClosestRow(position.Y);
int targetCol = GetClosestColumn(position.X);
if (targetRow < 0 || targetCol < 0) return;
// 获取目标单元格的位置和大小
var cellRect = GetCellBounds(targetRow, targetCol);
DropIndicator.Width = cellRect.Width;
DropIndicator.Height = cellRect.Height;
// 更新位置
Canvas.SetLeft(DropIndicator, cellRect.Left);
Canvas.SetTop(DropIndicator, cellRect.Top);
DropIndicator.Visibility = Visibility.Visible;
// 检查目标按钮
_targetButton = FindButtonAtCell(targetRow, targetCol);
}
private Rect GetCellBounds(int row, int column)
{
double x = 0, y = 0;
// 计算列位置
for (int c = 0; c < column; c++)
x += MainGrid.ColumnDefinitions[c].ActualWidth;
// 计算行位置
for (int r = 0; r < row; r++)
y += MainGrid.RowDefinitions[r].ActualHeight;
return new Rect(
x,
y,
MainGrid.ColumnDefinitions[column].ActualWidth,
MainGrid.RowDefinitions[row].ActualHeight);
}
private Button FindButtonAtCell(int row, int col)
{
foreach (UIElement child in MainGrid.Children)
{
if (child is Button btn &&
Grid.GetRow(btn) == row &&
Grid.GetColumn(btn) == col)
{
return btn;
}
}
return null;
}
private int GetClosestRow(double yPos)
{
double accumulatedHeight = 0;
for (int i = 0; i < MainGrid.RowDefinitions.Count; i++)
{
accumulatedHeight += MainGrid.RowDefinitions[i].ActualHeight;
if (yPos < accumulatedHeight)
return i;
}
return -1;
}
private int GetClosestColumn(double xPos)
{
double accumulatedWidth = 0;
for (int i = 0; i < MainGrid.ColumnDefinitions.Count; i++)
{
accumulatedWidth += MainGrid.ColumnDefinitions[i].ActualWidth;
if (xPos < accumulatedWidth)
return i;
}
return -1;
}
private void Button_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_draggedButton == null) return;
// 释放鼠标捕获
_draggedButton.ReleaseMouseCapture();
// 重置透明度
_draggedButton.Opacity = 1;
// 隐藏指示器
DropIndicator.Visibility = Visibility.Collapsed;
// 获取最终位置
var finalPosition = e.GetPosition(MainGrid);
int targetRow = GetClosestRow(finalPosition.Y);
int targetCol = GetClosestColumn(finalPosition.X);
// 有效位置检查
if (targetRow >= 0 && targetCol >= 0 &&
(targetRow != _originalRow || targetCol != _originalColumn))
{
// 执行位置交换
if (_targetButton != null && _targetButton != _draggedButton)
{
SwapButtons((Button)_draggedButton, _targetButton);
}
else
{
MoveButtonToCell((Button)_draggedButton, targetRow, targetCol);
}
}
else
{
// 返回原位
AnimateToOriginalPosition();
}
_draggedButton.RenderTransform = null;
_draggedButton = null;
_targetButton = null;
}
private void SwapButtons(Button source, Button target)
{
// 交换网格位置
int sourceRow = Grid.GetRow(source);
int sourceCol = Grid.GetColumn(source);
int targetRow = Grid.GetRow(target);
int targetCol = Grid.GetColumn(target);
Grid.SetRow(source, targetRow);
Grid.SetColumn(source, targetCol);
Grid.SetRow(target, sourceRow);
Grid.SetColumn(target, sourceCol);
// 添加交换动画
AnimateSwap(source, target);
}
private void MoveButtonToCell(Button button, int row, int col)
{
Grid.SetRow(button, row);
Grid.SetColumn(button, col);
AnimateMovement(button);
}
private void AnimateToOriginalPosition()
{
var originalPosition = GetCellBounds(_originalRow, _originalColumn);
var animation = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromMilliseconds(300),
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut }
};
_transform.BeginAnimation(TranslateTransform.XProperty, animation);
_transform.BeginAnimation(TranslateTransform.YProperty, animation);
}
private void AnimateMovement(Button button)
{
var transform = new TranslateTransform();
button.RenderTransform = transform;
var animation = new DoubleAnimation
{
From = 50,
To = 0,
Duration = TimeSpan.FromMilliseconds(300),
EasingFunction = new ElasticEase { Oscillations = 1, Springiness = 4 }
};
transform.BeginAnimation(TranslateTransform.YProperty, animation);
}
private void AnimateSwap(Button a, Button b)
{
var duration = TimeSpan.FromMilliseconds(400);
// 动画效果
a.RenderTransform = new TranslateTransform();
b.RenderTransform = new TranslateTransform();
var aAnimation = new DoubleAnimation(0, duration) { EasingFunction = new QuinticEase() };
var bAnimation = new DoubleAnimation(0, duration) { EasingFunction = new QuinticEase() };
a.RenderTransform.BeginAnimation(TranslateTransform.XProperty, aAnimation);
a.RenderTransform.BeginAnimation(TranslateTransform.YProperty, aAnimation);
b.RenderTransform.BeginAnimation(TranslateTransform.XProperty, bAnimation);
b.RenderTransform.BeginAnimation(TranslateTransform.YProperty, bAnimation);
}
}
}
1.3. 效果图
