今天需求这边想在wpf里绘制一个控件,实现页面动态布局。用户想在一个页面上看平面图,剖面图和三维图,但是又想自己定义显示的位置。先看下效果吧:
视图布局
参考了知乎的这篇帖子:WPF网格(Grid)实现拖拽元素交换位置,包含拖拽动画
其实是看了看博主写的代码,然后写了写注释改了改。博主的代码最终实现了九宫格任意拖拽和元素互换。咱们新写的实现了不规则的grid任意拖拽,界面如下:
我自己比较喜欢用stackpanel控件,所以用stackpanel布局。主要步骤是实现MouseLeftButtonDown、MouseLeftButtonUp、PreviewMouseMove三个事件。要注意的几点是,
1.控制拖拽状态,在代码中用isDown控制。
2.动态绘制拖拽的矩形效果,代码中参考_dragdropPopup。
3.控制界面元素的透明度,参见element.Opacity。
4.计算拖拽后的对象在整个画布的哪一行哪一列。参见MouseLeftButtonUp方法。
代码写了注释,大家可以参考代码。
页面代码:
<Grid x:Name="myGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Background="LightGray">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="平面图" />
</StackPanel>
<StackPanel
Grid.Row="0"
Grid.Column="1"
Background="LightBlue">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="剖面图" />
</StackPanel>
<StackPanel
Grid.Row="1"
Grid.Column="1"
Background="LightGreen">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="三维图" />
</StackPanel>
</Grid>
后端代码
public Window3()
{
InitializeComponent();
this.WindowState = WindowState.Maximized;
InitLayout();
}
void InitLayout()
{
myGrid.MouseLeftButtonDown += Element_MouseLeftButtonDown;
myGrid.MouseLeftButtonUp += Element_MouseLeftButtonUp;
myGrid.PreviewMouseMove += Element_PreviewMouseMove;
}
#region drag and drop
private UIElement initUE;
private Point initPt;
private Popup _dragdropPopup = null;
private bool isDown = false;
private double moveOpacity = 0.5;
private void Element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!isDown) return;
isDown = false;
if (this._dragdropPopup != null)
{
this._dragdropPopup.IsOpen = false;
this._dragdropPopup.Child = null;
this._dragdropPopup = null;
}
foreach (UIElement element in myGrid.Children)
{
if (element != initUE)
{
element.Opacity = 1;
}
}
initUE.ReleaseMouseCapture();
double y = e.GetPosition(myGrid).Y;
double start = 0.0;
int row = 0;//拖动后的点最终所在的行数
foreach (RowDefinition rd in myGrid.RowDefinitions)
{
start += rd.ActualHeight;
if (y < start)
{
break;
}
row++;
}
double x = e.GetPosition(myGrid).X;
double cstart = 0.0;
int column = 0;//拖动后的点最终所在的列数
foreach (ColumnDefinition cd in myGrid.ColumnDefinitions)
{
cstart += cd.ActualWidth;
if (x < cstart)
{
break;
}
column++;
}
if (row == 1 && column == 0)//这一列被合并了
{
row = 0;
}
var initRow = Grid.GetRow(initUE);//被拖动块初始所在的行
var initCol = Grid.GetColumn(initUE);//被拖动块初始坐在的列
UIElement uIElement = null;
if (row != initRow || column != initCol)
{
uIElement = GetChildren(myGrid, row, column);//找到目标块元素
}
if (uIElement != null)
{
myGrid.Children.Remove(uIElement);//移除目标块
Grid.SetColumn(initUE, column);//将被移动的块位置设置为目标块所在的行和列
Grid.SetRow(initUE, row);
if (column == 0 && row == 0)
{
Grid.SetRowSpan(initUE, 2);
Grid.SetRowSpan(uIElement, 1);
}
myGrid.Children.Add(uIElement);//将目标块添加进根目录,并且将它的行和列设置为目标块的
Grid.SetColumn(uIElement, initCol);
Grid.SetRow(uIElement, initRow);
if (initCol == 0 && initRow == 0)
{
Grid.SetRowSpan(uIElement, 2);
Grid.SetRowSpan(initUE, 1);
}
}
}
private void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (isDown)//已经结束拖拽
{
isDown = false;
return;
}
initUE = (UIElement)e.Source;//获取被拖拽的块
initUE.CaptureMouse();//鼠标强制捕获这个块
initPt = new Point(e.GetPosition(initUE).X, e.GetPosition(initUE).Y - SystemParameters.CaptionHeight);//被拖动块的初始位置
foreach (UIElement element in myGrid.Children)
{
if (element != initUE)
{
element.Opacity = moveOpacity;//修改其它块的透明度
}
}
CreateDragDropPopup(initUE, e);//弹框效果
isDown = true;//拖拽完成
}
private void Element_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (isDown == false) return;//非拖拽状态就返回
double y = e.GetPosition(myGrid).Y;//鼠标当前的y值
double x = e.GetPosition(myGrid).X;//鼠标当前的x值
if (_dragdropPopup != null)//如果拖拽的弹框不为空
{
_dragdropPopup.HorizontalOffset = x - initPt.X;//当前x与初始块的偏移
_dragdropPopup.VerticalOffset = y - initPt.Y;//当前y与初始块的偏移
}
double start = 0.0;
int row = 0;//当前鼠标y所在的行
foreach (RowDefinition rd in myGrid.RowDefinitions)
{
start += rd.ActualHeight;
if (y < start)
{
break;
}
row++;
}
double cstart = 0.0;
int column = 0;//当前鼠标x所在的列
foreach (ColumnDefinition cd in myGrid.ColumnDefinitions)
{
cstart += cd.ActualWidth;
if (x < cstart)
{
break;
}
column++;
}
UIElement uIElement = GetChildren(myGrid, row, column);//获取当前行列的块
foreach (UIElement element in myGrid.Children)
{
if (element != initUE && element != uIElement)//设置透明度
{
element.Opacity = moveOpacity;
}
else
{
element.Opacity = 1;
}
}
}
private UIElement GetChildren(Grid grid, int row, int column)
{
foreach (UIElement child in grid.Children)
{
if (Grid.GetRow(child) == row && Grid.GetColumn(child) == column)
{
return child;
}
}
return null;
}
private void CreateDragDropPopup(Visual dragElement, MouseButtonEventArgs e)
{
this._dragdropPopup = new Popup();
Rectangle r = new Rectangle();//弹出一个框,绘制当前被拖拽的元素为一个矩形
r.Width = ((FrameworkElement)dragElement).ActualWidth;
r.Height = ((FrameworkElement)dragElement).ActualHeight;
r.Fill = new VisualBrush(dragElement);
this._dragdropPopup.Child = r;
double y = e.GetPosition(myGrid).Y;
double x = e.GetPosition(myGrid).X;
_dragdropPopup.HorizontalOffset = x - initPt.X;
_dragdropPopup.VerticalOffset = y - initPt.Y;
this._dragdropPopup.IsOpen = true;
}
#endregion