在 WPF 中实现拖放的方法有很多,但没有一个是真正简单的。例如,您可以:
- 将Thumbs添加到 Canvas 并实现 dragstart、dragcomplete 和 dragdelta
- 使用装饰器。好文章在这里。
- 使用 RenderTransform 并创建一个 TranslateTransform 来移动控件。并在您的窗口/用户控件上手动实现 mousedown、mousemove 和 mouseup。
以下代码使用运行时生成的标签的 RenderTransform 属性在用户用鼠标拖动它们时在屏幕上移动它们。我的示例的一个限制是您不能将其他转换应用于您的标签,因为它将被覆盖。考虑使用 TransformGroup。
Xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Background="Gray" PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown" PreviewMouseMove="Window_PreviewMouseMove" PreviewMouseLeftButtonUp="Window_PreviewMouseLeftButtonUp">
<Grid>
<ItemsControl ItemsSource="{Binding LabelsCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label HorizontalAlignment="Center" Content="{Binding}"></Label>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
背后的代码
public partial class MainWindow : Window, INotifyPropertyChanged
{
private List<DependencyObject> _hitResultsList = new List<DependencyObject>();
private Point _currentlyDraggedMouseOffset;
private Label _currentlyDragged;
private ObservableCollection<string> _labelsCollection;
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
DataContext = this;
InitializeComponent();
LabelsCollection = new ObservableCollection<string>();
for (int i = 1; i <= 10; i++)
{
LabelsCollection.Add("Label " + i);
}
}
public ObservableCollection<string> LabelsCollection
{
get { return _labelsCollection; }
set
{
_labelsCollection = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LabelsCollection"));
}
}
}
private void Window_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_currentlyDragged != null)
{
var mousePos = e.GetPosition(this);
_currentlyDragged.RenderTransform = new TranslateTransform(mousePos.X - _currentlyDraggedMouseOffset.X, mousePos.Y - _currentlyDraggedMouseOffset.Y);
}
}
private void Window_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_currentlyDragged = null;
ReleaseMouseCapture();
}
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
CaptureMouse();
Point pt = e.GetPosition((UIElement)sender);
_hitResultsList.Clear();
VisualTreeHelper.HitTest(this, null,
new HitTestResultCallback(MyHitTestResult),
new PointHitTestParameters(pt));
if (_hitResultsList.Count > 0)
{
foreach (DependencyObject d in _hitResultsList)
{
var parent = VisualTreeHelper.GetParent(d);
if (parent != null && parent is Label)
{
_currentlyDragged = parent as Label;
if (_currentlyDragged.RenderTransform is TranslateTransform)
{
_currentlyDraggedMouseOffset.X = e.GetPosition(this).X - ((TranslateTransform)_currentlyDragged.RenderTransform).X;
_currentlyDraggedMouseOffset.Y = e.GetPosition(this).Y - ((TranslateTransform)_currentlyDragged.RenderTransform).Y;
}
else
{
_currentlyDraggedMouseOffset.X = pt.X;
_currentlyDraggedMouseOffset.Y = pt.Y;
}
return;
}
}
}
_currentlyDragged = null;
}
// Return the result of the hit test to the callback.
public HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
_hitResultsList.Add(result.VisualHit);
return HitTestResultBehavior.Continue;
}
}