实现目标,在一个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 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添加指针移动事件
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设为允许拖入
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 }
最终效果:
初始画面
拖拽后: