WPF中实现不规则Grid拖拽元素交换(可用于动态布局)

今天需求这边想在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

【转载声明】转载本博客的文章请注明原始出处和作者

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值