WPF ListView元素拖拽移动失效问题求助
问题描述
我在XAML中创建了一个ListView控件,设置Canvas作为ItemsPanel,将ListViewItem的Canvas.Left和Canvas.Top属性绑定到数据类的X、Y字段,数据集合在C#代码中赋值。我希望拖拽控件内的红色方块(ListViewItem)在窗口内自由移动,但目前移动功能失效,方块无法拖动。需要解决该移动问题,同时保留ListView的选中事件以获取选中元素的数据。
现有代码
XAML代码
<ListView x:Name="ItemsControlCollection" Margin="0,162,0,0" SelectionMode="single" SelectionChanged="ItemsControlCollection_SelectionChanged"> <ListView.ItemsPanel> <ItemsPanelTemplate> <Canvas IsItemsHost="True"/> </ItemsPanelTemplate> </ListView.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style TargetType="{x:Type ListViewItem}"> <Setter Property="Canvas.Left" Value="{Binding X}" /> <Setter Property="Canvas.Top" Value="{Binding Y}" /> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemTemplate> <DataTemplate> <Canvas Width="50" Height="50" Background="#FFD82B2B" MouseDown="FF_MouseDown" MouseMove="FF_MouseMove" MouseUp="FF_MouseUp"/> </DataTemplate> </ItemsControl.ItemTemplate> </ListView>
C#代码
数据类定义
public class ItemsControlCollections { public String Title { get; set; } public float X { get; set; } public float Y { get; set; } public List<ItemsControlCollections> _ItemsControlCollections = new List<ItemsControlCollections>(); }
窗口初始化与数据加载
public partial class MainWindow : Window { List<ItemsControlCollections> ObjectItems = new ItemsControlCollections()._ItemsControlCollections; private void Window_Loaded(object sender, RoutedEventArgs e) { ItemsControlCollection.Items.Clear(); } // 按钮点击从数据库加载数据并设置数据源 using (SqlDataReader rdr = commandItemXY.ExecuteReader()) { while (rdr.Read()) { ObjectItems.Add(new ItemsControlCollections() { Title = rdr["id_Item"].ToString(), X = Convert.ToInt32(rdr["X"]), Y = Convert.ToInt32(rdr["Y"]) }); } } ItemsControlCollection.ItemsSource = ObjectItems; }
拖拽事件代码
int countZ = 0; bool _canMove = false; Point _offsetPoint = new Point(0, 0); private void FF_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { _canMove = true; countZ++; FrameworkElement ffElement = (FrameworkElement)sender; Grid.SetZIndex(ffElement, countZ); Point posCursor = e.MouseDevice.GetPosition(this); _offsetPoint = new Point( posCursor.X - Canvas.GetLeft(ffElement), posCursor.Y - Canvas.GetTop(ffElement) ); e.MouseDevice.Capture(ffElement); } private void FF_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) { if (_canMove == true) { FrameworkElement ffElement = (FrameworkElement)sender; if (e.MouseDevice.Captured == ffElement) { Point p = e.MouseDevice.GetPosition(this); Canvas.SetLeft(ffElement, p.X - _offsetPoint.X); Canvas.SetTop(ffElement, p.Y - _offsetPoint.Y); } } } private void FF_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { _canMove = false; e.MouseDevice.Capture(null); } private void ItemsControlCollection_SelectionChanged(object sender, SelectionChangedEventArgs e) { }
解决方案
问题集中在绑定逻辑、拖拽目标定位和数据通知三个核心点,以下是针对性修正:
1. 修复数据类的属性通知
当前数据类未实现INotifyPropertyChanged,且绑定为单向模式,导致拖拽修改无法同步回数据源,ListViewItem位置被绑定值覆盖。
修改数据类:
public class ItemsControlCollections : INotifyPropertyChanged { private string _title; private float _x; private float _y; public string Title { get => _title; set { _title = value; OnPropertyChanged(nameof(Title)); } } public float X { get => _x; set { _x = value; OnPropertyChanged(nameof(X)); } } public float Y { get => _y; set { _y = value; OnPropertyChanged(nameof(Y)); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
同时修改XAML中绑定为双向模式:
<Style TargetType="{x:Type ListViewItem}"> <Setter Property="Canvas.Left" Value="{Binding X, Mode=TwoWay}" /> <Setter Property="Canvas.Top" Value="{Binding Y, Mode=TwoWay}" /> </Style>
2. 修正拖拽事件的目标对象
当前拖拽事件绑定在模板内的Canvas上,但实际控制位置的是ListViewItem(Canvas的父容器),修改拖拽逻辑定位到ListViewItem:
int countZ = 0; bool _canMove = false; Point _offsetPoint = new Point(0, 0); private void FF_MouseDown(object sender, MouseButtonEventArgs e) { _canMove = true; countZ++; FrameworkElement innerCanvas = (FrameworkElement)sender; // 获取对应的ListViewItem容器 ListViewItem itemContainer = ItemsControl.ContainerFromElement(ItemsControlCollection, innerCanvas) as ListViewItem; if (itemContainer == null) return; Grid.SetZIndex(itemContainer, countZ); // 计算鼠标相对于ListViewItem的偏移 Point posCursor = e.MouseDevice.GetPosition(ItemsControlCollection); _offsetPoint = new Point( posCursor.X - Canvas.GetLeft(itemContainer), posCursor.Y - Canvas.GetTop(itemContainer) ); e.MouseDevice.Capture(itemContainer); // 手动选中当前项 ItemsControlCollection.SelectedItem = itemContainer.DataContext; } private void FF_MouseMove(object sender, MouseEventArgs e) { if (!_canMove) return; FrameworkElement innerCanvas = (FrameworkElement)sender; ListViewItem itemContainer = ItemsControl.ContainerFromElement(ItemsControlCollection, innerCanvas) as ListViewItem; if (itemContainer == null || e.MouseDevice.Captured != itemContainer) return; Point p = e.MouseDevice.GetPosition(ItemsControlCollection); // 修改ListViewItem的位置,通过双向绑定同步到数据源 Canvas.SetLeft(itemContainer, p.X - _offsetPoint.X); Canvas.SetTop(itemContainer, p.Y - _offsetPoint.Y); } private void FF_MouseUp(object sender, MouseButtonEventArgs e) { _canMove = false; e.MouseDevice.Capture(null); }
3. 修正窗口类的集合声明
原集合初始化方式不合理,调整为直接声明:
public partial class MainWindow : Window { private List<ItemsControlCollections> ObjectItems = new List<ItemsControlCollections>(); private void Window_Loaded(object sender, RoutedEventArgs e) { ItemsControlCollection.Items.Clear(); } // 按钮点击加载数据方法(补充完整事件绑定) private void LoadData_Click(object sender, RoutedEventArgs e) { ObjectItems.Clear(); using (SqlDataReader rdr = commandItemXY.ExecuteReader()) { while (rdr.Read()) { ObjectItems.Add(new ItemsControlCollections() { Title = rdr["id_Item"].ToString(), X = Convert.ToInt32(rdr["X"]), Y = Convert.ToInt32(rdr["Y"]) }); } } ItemsControlCollection.ItemsSource = ObjectItems; } }
4. 完善选中事件逻辑
现在可以正常获取选中元素的数据:
private void ItemsControlCollection_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.AddedItems.Count > 0) { ItemsControlCollections selectedItem = e.AddedItems[0] as ItemsControlCollections; // 此处可使用selectedItem的Title、X、Y等数据 } }
内容的提问来源于stack exchange,提问作者POLITER
相关产品推荐
相关产品推荐

