Avalonia 列表拖拽替换

实现目标,在一个ListBox中选择一个子项进行拖拽到另一个ListBox中,拖拽到某一子项区域进行替换

axaml代码

 1  <ListBox
 2                             Name="consumableListBox"
 3                             Margin="5"
 4                             ItemsSource="{Binding ConsumableList}"
 5                             SelectionMode="Single">
 6                             <ListBox.ItemTemplate>
 7                                 <DataTemplate>
 8                                     <StackPanel Margin="5,5,5,0">
 9                                         <Border
10                                             Width="160"
11                                             Height="100"
12                                             Margin="0,0,0,5"
13                                             HorizontalAlignment="Center"
14                                             Background="Red"
15                                             CornerRadius="5" />
16                                         <TextBlock HorizontalAlignment="Center" Text="{Binding}" />
17                                     </StackPanel>
18                                 </DataTemplate>
19                             </ListBox.ItemTemplate>
20                         </ListBox>
源ListBox
<ListBox
                Name="platePositionListBox"
                Margin="5"
                ItemsSource="{Binding PlatePositionList}"
                SelectionMode="Single">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="5,5,5,0">
                            <Border
                                Width="160"
                                Height="100"
                                Margin="0,0,0,5"
                                HorizontalAlignment="Center"
                                Background="Red"
                                CornerRadius="5" />
                            <TextBlock HorizontalAlignment="Center" Text="{Binding}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
目标ListBox

给源ListBox添加指针移动事件

 1 private void SourceList_PointerMoved(object sender, PointerEventArgs e)
 2     {
 3         // 当拖拽操作开始时,在源列表中开始拖拽
 4         if (e.GetCurrentPoint(consumableListBox).Properties.IsLeftButtonPressed)
 5         {
 6             DataObject dataObject = new DataObject();
 7             dataObject.Set("dataObject", consumableListBox.SelectedItem);
 8             DragDrop.DoDragDrop(e, dataObject, DragDropEffects.Move);
 9         }
10     }
源ListBox指针移动

 将目标ListBox设为允许拖入

DragDrop.SetAllowDrop(platePositionListBox, true); 

由于ListBox没有DropEvent事件,需要进行添加事件

 platePositionListBox.AddHandler(DragDrop.DropEvent, PlatePositionListBox_DropEvent, RoutingStrategies.Bubble | RoutingStrategies.Direct); 

对应事件实现,其中需要获取到需要替换的项的下标,本处是通过定位进行计算

 1 private void PlatePositionListBox_DropEvent(object? sender, DragEventArgs e)
 2     {
 3         var a = e.Data.Get("dataObject");
 4         if (e.Data.Contains("dataObject") && a != null)
 5         {
 6             var targetIndex = GetDropTargetIndex(platePositionListBox, e.GetPosition(platePositionListBox));
 7             if (targetIndex >= 0)
 8             {
 9                 if (this.DataContext is PlanViewModel viewModel)
10                 {
11                     viewModel.PlatePositionList.RemoveAt(targetIndex);
12                     viewModel.PlatePositionList.Insert(targetIndex, a.ToString());
13                 }
14             }
15         }
16     }
17 
18 
19 //获取目标项下标
20 private int GetDropTargetIndex(ListBox targetListBox, Avalonia.Point position)
21     {
22         var itemsControl = targetListBox;
23 
24         var itemsPoint = GetAllItemDistances(targetListBox);
25         var firstItemPanel = (StackPanel)Avalonia.VisualTree.VisualExtensions.FindDescendantOfType<StackPanel>(itemsControl);
26         var itemContainer = (Control)firstItemPanel;
27         var firstItemBounds = itemContainer.Bounds;
28         var items = itemsControl.Items;
29         var y = firstItemBounds.Y;
30         for (int i = 0; i < items.Count; i++)
31         {
32             if (itemsPoint[i].Index == -1)
33             {
34                 continue;
35             }
36             if (position.Y >= itemsPoint[i].DistanceToTop && position.Y <= itemsPoint[i].DistanceToTop + firstItemBounds.Height)
37             {
38                 return i;
39             }
40         }
41         return items.Count;
42     }
43 
44 //获取目标ListBox的项距离顶部边框的距离,如果未呈现到画面上,下标设为-1
45 private List<ItemCoordinates> GetAllItemDistances(ListBox listBox)
46     {
47         var itemCoordinatesList = new List<ItemCoordinates>();
48         var items = listBox.Items;
49         var scrollViewer = listBox.FindDescendantOfType<ScrollViewer>();
50         var topOffset = scrollViewer.Offset.Y;
51 
52         for (int i = 0; i < items.Count; i++)
53         {
54             var itemContainer = listBox.ItemContainerGenerator.ContainerFromIndex(i) as Control;
55             if (itemContainer != null)
56             {
57                 var itemBounds = itemContainer.Bounds;
58                 var distanceToTop = itemBounds.Top - topOffset;
59                 itemCoordinatesList.Add(new ItemCoordinates(i, distanceToTop));
60             }
61             else
62             {
63                 itemCoordinatesList.Add(new ItemCoordinates(-1, -999));
64             }
65         }
66         return itemCoordinatesList;
67     }
具体实现

对应的实体类

 1 public class ItemCoordinates
 2     {
 3         public int Index { get; }
 4         public double DistanceToTop { get; }
 5 
 6         public ItemCoordinates(int index, double distanceToTop)
 7         {
 8             Index = index;
 9             DistanceToTop = distanceToTop;
10         }
11     }

最终效果:

初始画面

拖拽后:

 

posted @ 2023-08-03 20:58  逸羽澜心  阅读(707)  评论(0编辑  收藏  举报