WPF Drag Adorner 修复运动并在 Adorner 中显示反馈
WPF Drag Adorner fix movement and show feedback in Adorner
我正在尝试为我正在创建的项目构建类似于 Inventory 的界面。
想法是拥有可以拖动到播放器的图像列表,如下所示:
图片从目录加载并显示在ListView中,玩家列表显示在ListBox中。
我的 XAML 看起来像这样:
<Window x:Class="DynamicImagesDrag.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dynamicImagesDrag="clr-namespace:DynamicImagesDrag"
Title="MainWindow"
Height="405"
Width="719.162"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<dynamicImagesDrag:StringToImageConverter x:Key="StringToImageConverter" />
</Window.Resources>
<Grid>
<ListView Name="MyList"
ItemsSource="{Binding Images}"
PreviewMouseLeftButtonDown="UIElement_OnPreviewMouseLeftButtonDown"
PreviewMouseMove="UIElement_OnPreviewMouseMove"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
VerticalAlignment="Stretch"
HorizontalAlignment="Left"
Margin="10" Width="250">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Width="50" Height="50">
<DockPanel.Background>
<ImageBrush ImageSource="BG1.png"/>
</DockPanel.Background>
<Image Source="{Binding Path, Converter={StaticResource StringToImageConverter} }"
Height="32"
Width="32" />
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListBox ItemsSource="{Binding People}" HorizontalAlignment="Right" HorizontalContentAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"
Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" AllowDrop="True" PreviewDrop="UIElement_OnPreviewDrop">
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<ProgressBar Height="20" Value="{Binding Points}" Margin="0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
后面的代码:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DynamicImagesDrag
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
private readonly ObservableCollection<MyImage> _images = new ObservableCollection<MyImage>();
public ObservableCollection<MyImage> Images
{
get { return _images; }
}
private readonly ObservableCollection<Person> _people = new ObservableCollection<Person>();
public ObservableCollection<Person> People { get { return _people; } }
public MainWindow()
{
InitializeComponent();
_people.Add(new Person() { Name = "Person1", Points = 10 });
_people.Add(new Person() { Name = "Person2", Points = 0 });
_people.Add(new Person() { Name = "Person3", Points = 40 });
string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (appPath != null)
{
string imagePath = Path.Combine(appPath, "Images");
if (Directory.Exists(imagePath))
{
var images = Directory
.EnumerateFiles(imagePath)
.Where(file => file.ToLower().EndsWith("jpg") || file.ToLower().EndsWith("png"))
.ToList();
foreach (string image in images)
{
_images.Add(new MyImage
{
Name = Path.GetFileName(image),
Path = image,
Points = Convert.ToInt32(Path.GetFileNameWithoutExtension(image))
});
}
}
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
private void UIElement_OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
#region Field and Properties
private bool _dragHasLeftScope;
private Point _startPoint;
public bool IsDragging { get; set; }
DragAdorner _adorner;
AdornerLayer _layer;
public FrameworkElement DragScope { get; set; }
#endregion // Field and Properties
private void UIElement_OnPreviewMouseMove(object sender, MouseEventArgs e)
{
// Ensure that the user does not drag by accident
if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
{
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
StartDragInProcAdorner(e);
}
}
}
void DragScope_DragLeave(object sender, DragEventArgs e)
{
if (e.OriginalSource == DragScope)
{
Point p = e.GetPosition(DragScope);
Rect r = VisualTreeHelper.GetContentBounds(DragScope);
if (!r.Contains(p))
{
_dragHasLeftScope = true;
e.Handled = true;
}
}
}
void Window1_DragOver(object sender, DragEventArgs args)
{
if (_adorner == null) return;
_adorner.LeftOffset = args.GetPosition(DragScope).X /* - _startPoint.X */ ;
_adorner.TopOffset = args.GetPosition(DragScope).Y /* - _startPoint.Y */ ;
}
void DragScope_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
if (_dragHasLeftScope)
{
e.Action = DragAction.Cancel;
e.Handled = true;
}
}
private void StartDragInProcAdorner(MouseEventArgs e)
{
DragScope = Application.Current.MainWindow.Content as FrameworkElement;
bool previousDrop = DragScope.AllowDrop;
DragScope.AllowDrop = true;
try
{
DragEventHandler draghandler = Window1_DragOver;
DragScope.PreviewDragOver += draghandler;
DragEventHandler dragleavehandler = DragScope_DragLeave;
DragScope.DragLeave += dragleavehandler;
QueryContinueDragEventHandler queryhandler = DragScope_QueryContinueDrag;
DragScope.QueryContinueDrag += queryhandler;
DragScope.GiveFeedback+=DragScope_GiveFeedback;
FrameworkElement dr = MyList.ItemContainerGenerator.ContainerFromItem(MyList.SelectedItem) as FrameworkElement;
if (dr == null)
return;
_adorner = new DragAdorner(DragScope, dr, true, 0.5);
_layer = AdornerLayer.GetAdornerLayer(DragScope);
_layer.Add(_adorner);
IsDragging = true;
_dragHasLeftScope = false;
DataObject data = new DataObject(MyList.SelectedItem as MyImage);
DragDropEffects de = DragDrop.DoDragDrop(MyList, data, DragDropEffects.Move);
DragScope.AllowDrop = previousDrop;
AdornerLayer.GetAdornerLayer(DragScope).Remove(_adorner);
_adorner = null;
DragScope.DragLeave -= dragleavehandler;
DragScope.QueryContinueDrag -= queryhandler;
DragScope.PreviewDragOver -= draghandler;
IsDragging = false;
}
catch
{
DragScope.AllowDrop = previousDrop;
AdornerLayer.GetAdornerLayer(DragScope).Remove(_adorner);
_adorner = null;
IsDragging = false;
}
}
private void DragScope_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
}
private void UIElement_OnPreviewDrop(object sender, DragEventArgs e)
{
var stackPanel = sender as StackPanel;
if (stackPanel == null) return;
var student = stackPanel.DataContext as Person;
MyImage myImage = e.Data.GetData(typeof(MyImage)) as MyImage;
if (student != null) student.Points += myImage.Points;
}
}
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Person : INotifyPropertyChanged
{
private string _name;
private int _points;
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public int Points
{
get { return _points; }
set
{
if (value == _points) return;
_points = value;
if (_points >= 100)
{
_points -= 100;
Debug.WriteLine("100!");
}
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyImage
{
public string Path { get; set; }
public string Name { get; set; }
public int Points { get; set; }
}
}
和 DragAdorner(取自 http://www.infragistics.com/community/blogs/alex_fidanov/archive/2009/07/28/drag-amp-drop-with-datapresenter-family-controls.aspx)
class DragAdorner : Adorner
{
public DragAdorner(UIElement owner) : base(owner) { }
public DragAdorner(UIElement owner, UIElement adornElement, bool useVisualBrush, double opacity)
: base(owner)
{
_owner = owner;
VisualBrush _brush = new VisualBrush
{
Opacity = opacity,
Visual = adornElement
};
DropShadowEffect dropShadowEffect = new DropShadowEffect
{
Color = Colors.Black,
BlurRadius = 15,
Opacity = opacity
};
Rectangle r = new Rectangle
{
RadiusX = 3,
RadiusY = 3,
Fill = _brush,
Effect = dropShadowEffect,
Width = adornElement.DesiredSize.Width,
Height = adornElement.DesiredSize.Height
};
XCenter = adornElement.DesiredSize.Width / 2;
YCenter = adornElement.DesiredSize.Height / 2;
_child = r;
}
private void UpdatePosition()
{
AdornerLayer adorner = (AdornerLayer)Parent;
if (adorner != null)
{
adorner.Update(AdornedElement);
}
}
#region Overrides
protected override Visual GetVisualChild(int index)
{
return _child;
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
protected override Size MeasureOverride(Size finalSize)
{
_child.Measure(finalSize);
return _child.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
_child.Arrange(new Rect(_child.DesiredSize));
return finalSize;
}
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
GeneralTransformGroup result = new GeneralTransformGroup();
result.Children.Add(base.GetDesiredTransform(transform));
result.Children.Add(new TranslateTransform(_leftOffset, _topOffset));
return result;
}
#endregion
#region Field & Properties
public double scale = 1.0;
protected UIElement _child;
protected VisualBrush _brush;
protected UIElement _owner;
protected double XCenter;
protected double YCenter;
private double _leftOffset;
public double LeftOffset
{
get { return _leftOffset; }
set
{
_leftOffset = value - XCenter;
UpdatePosition();
}
}
private double _topOffset;
public double TopOffset
{
get { return _topOffset; }
set
{
_topOffset = value - YCenter;
UpdatePosition();
}
}
#endregion
}
拖动几乎可以正常工作:
除装饰器仅在源列表和目标列表中可见外,在整个拖动过程中不显示。
我的问题是:
- 如何修复拖放以一直查看装饰器?
- 如何在装饰器中显示图像而不是 selectedItem?现在装饰里面是棕色背景,我只想得到透明图像。
- 如何显示装饰器内的 dragtarget 是否正确而不是更改光标?如果目标正确,我想更改装饰器的不透明度。
- 我想要拖放处理触摸事件,@KOTIX 建议使用 Gong WPF 拖放,它在支持触摸的屏幕上可以正常工作吗?
- 目前我正在 ListBox ItemTemplate 内的 StackPanel 上设置
AllowDrop
,它应该留在那里还是我应该在 ListBox 上设置?
我在互联网(包括 SO)上搜索了解决方案,但找不到适合我需要的任何东西。
我发现了一些很棒的文章:
http://www.codeproject.com/Articles/37161/WPF-Drag-and-Drop-Smorgasbord
http://www.zagstudio.com/blog/488#.VgHPyxHtmkp
http://nonocast.cn/adorner-in-wpf-part-5-drag-and-drop/
https://blogs.claritycon.com/blog/2009/03/generic-wpf-drag-and-drop-adorner/
最后一个非常有趣,但我无法修改为向玩家添加点数而不是移动物品。在我的例子中,我希望左侧的项目保留,我只想根据拖动的项目更新右侧的列表。
我建议使用 Gong WPF dragdrop
这个库对我来说一直很好用,而且比内置支持要好得多。它还增加了对 MVVM 设计的强大支持。
祝你好运!
我还没有找到所有答案,但它可能会引导您实现目标。
How can I fix drag and drop to see adorner whole time?
你的DragLayer是透明的,只需为你的Grid
的Background
属性设置一个值
How can I display image instead selectedItem inside adorner? Right now
inside adorner is that brown background, I'd like to get only
transparent image.
我最好的办法是探索 VisualTree
以获取模板内的 Image
:
//Get the `ListViewItem`
FrameworkElement dr = MyList.ItemContainerGenerator.ContainerFromItem(MyList.SelectedItem) as FrameworkElement;
//Explore the VisualTree to get the grand-child
//This should be refactored to a Func<UIElement,UIElement> to accord to templates changes
UIElement el = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(dr, 0), 0) as UIElement;
//Create the DragAdorner using the found UIElement
_adorner = new DragAdorner(DragScope, el, true, 1d);
How can I show if dragtarget is correct inside adorner instead of
changing cursor? I'd like to change opacity of adorner if target is
correct.
要显示一些反馈,您需要... GiveFeedBack
这是您的处理程序:
private void DragScope_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
if (_adorner == null) return;
if (e.Effects == DragDropEffects.Copy)
{
_adorner.Opacity = 1d;
e.Handled = true;
}
else
{
_adorner.Opacity = 0.5d;
e.Handled = true;
}
}
现在,您必须在 2 个地方设置所需的效果:Person
模板和 DragLayer
:
private void StackPanel_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
e.Handled = true;
}
void Window1_DragOver(object sender, DragEventArgs args)
{
if (_adorner == null) return;
_adorner.LeftOffset = args.GetPosition(DragScope).X;
_adorner.TopOffset = args.GetPosition(DragScope).Y;
if (!args.Handled)
args.Effects = DragDropEffects.Move;
}
为了使这些起作用,您必须在启动 DragDrop 时允许这 2 个效果:
DragDropEffects de = DragDrop.DoDragDrop(MyList, data, DragDropEffects.Move | DragDropEffects.Copy);
此外,为避免累积不透明度降低,请在构造函数中将 DragAdorner
不透明度设为 1。
I'd like to get drag and drop working with touch events, @KOTIX
suggested using Gong WPF dragdrop, will it work fine on touch enabled
screens?
这个解决方案是完全原生的,你应该让它与触摸设备一起工作。
Currently I'm setting AllowDrop on StackPanel inside ListBox
ItemTemplate, should it stay there or maybe I should set in on
ListBox?
如果您的目标是将 MyImage.Points
添加到放置目标,则必须在 StackPanel
上设置 AllowDrop
。
我正在尝试为我正在创建的项目构建类似于 Inventory 的界面。 想法是拥有可以拖动到播放器的图像列表,如下所示:
图片从目录加载并显示在ListView中,玩家列表显示在ListBox中。
我的 XAML 看起来像这样:
<Window x:Class="DynamicImagesDrag.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dynamicImagesDrag="clr-namespace:DynamicImagesDrag"
Title="MainWindow"
Height="405"
Width="719.162"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<dynamicImagesDrag:StringToImageConverter x:Key="StringToImageConverter" />
</Window.Resources>
<Grid>
<ListView Name="MyList"
ItemsSource="{Binding Images}"
PreviewMouseLeftButtonDown="UIElement_OnPreviewMouseLeftButtonDown"
PreviewMouseMove="UIElement_OnPreviewMouseMove"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
VerticalAlignment="Stretch"
HorizontalAlignment="Left"
Margin="10" Width="250">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Width="50" Height="50">
<DockPanel.Background>
<ImageBrush ImageSource="BG1.png"/>
</DockPanel.Background>
<Image Source="{Binding Path, Converter={StaticResource StringToImageConverter} }"
Height="32"
Width="32" />
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListBox ItemsSource="{Binding People}" HorizontalAlignment="Right" HorizontalContentAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"
Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" AllowDrop="True" PreviewDrop="UIElement_OnPreviewDrop">
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<ProgressBar Height="20" Value="{Binding Points}" Margin="0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
后面的代码:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DynamicImagesDrag
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
private readonly ObservableCollection<MyImage> _images = new ObservableCollection<MyImage>();
public ObservableCollection<MyImage> Images
{
get { return _images; }
}
private readonly ObservableCollection<Person> _people = new ObservableCollection<Person>();
public ObservableCollection<Person> People { get { return _people; } }
public MainWindow()
{
InitializeComponent();
_people.Add(new Person() { Name = "Person1", Points = 10 });
_people.Add(new Person() { Name = "Person2", Points = 0 });
_people.Add(new Person() { Name = "Person3", Points = 40 });
string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (appPath != null)
{
string imagePath = Path.Combine(appPath, "Images");
if (Directory.Exists(imagePath))
{
var images = Directory
.EnumerateFiles(imagePath)
.Where(file => file.ToLower().EndsWith("jpg") || file.ToLower().EndsWith("png"))
.ToList();
foreach (string image in images)
{
_images.Add(new MyImage
{
Name = Path.GetFileName(image),
Path = image,
Points = Convert.ToInt32(Path.GetFileNameWithoutExtension(image))
});
}
}
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
private void UIElement_OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
#region Field and Properties
private bool _dragHasLeftScope;
private Point _startPoint;
public bool IsDragging { get; set; }
DragAdorner _adorner;
AdornerLayer _layer;
public FrameworkElement DragScope { get; set; }
#endregion // Field and Properties
private void UIElement_OnPreviewMouseMove(object sender, MouseEventArgs e)
{
// Ensure that the user does not drag by accident
if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
{
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
StartDragInProcAdorner(e);
}
}
}
void DragScope_DragLeave(object sender, DragEventArgs e)
{
if (e.OriginalSource == DragScope)
{
Point p = e.GetPosition(DragScope);
Rect r = VisualTreeHelper.GetContentBounds(DragScope);
if (!r.Contains(p))
{
_dragHasLeftScope = true;
e.Handled = true;
}
}
}
void Window1_DragOver(object sender, DragEventArgs args)
{
if (_adorner == null) return;
_adorner.LeftOffset = args.GetPosition(DragScope).X /* - _startPoint.X */ ;
_adorner.TopOffset = args.GetPosition(DragScope).Y /* - _startPoint.Y */ ;
}
void DragScope_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
if (_dragHasLeftScope)
{
e.Action = DragAction.Cancel;
e.Handled = true;
}
}
private void StartDragInProcAdorner(MouseEventArgs e)
{
DragScope = Application.Current.MainWindow.Content as FrameworkElement;
bool previousDrop = DragScope.AllowDrop;
DragScope.AllowDrop = true;
try
{
DragEventHandler draghandler = Window1_DragOver;
DragScope.PreviewDragOver += draghandler;
DragEventHandler dragleavehandler = DragScope_DragLeave;
DragScope.DragLeave += dragleavehandler;
QueryContinueDragEventHandler queryhandler = DragScope_QueryContinueDrag;
DragScope.QueryContinueDrag += queryhandler;
DragScope.GiveFeedback+=DragScope_GiveFeedback;
FrameworkElement dr = MyList.ItemContainerGenerator.ContainerFromItem(MyList.SelectedItem) as FrameworkElement;
if (dr == null)
return;
_adorner = new DragAdorner(DragScope, dr, true, 0.5);
_layer = AdornerLayer.GetAdornerLayer(DragScope);
_layer.Add(_adorner);
IsDragging = true;
_dragHasLeftScope = false;
DataObject data = new DataObject(MyList.SelectedItem as MyImage);
DragDropEffects de = DragDrop.DoDragDrop(MyList, data, DragDropEffects.Move);
DragScope.AllowDrop = previousDrop;
AdornerLayer.GetAdornerLayer(DragScope).Remove(_adorner);
_adorner = null;
DragScope.DragLeave -= dragleavehandler;
DragScope.QueryContinueDrag -= queryhandler;
DragScope.PreviewDragOver -= draghandler;
IsDragging = false;
}
catch
{
DragScope.AllowDrop = previousDrop;
AdornerLayer.GetAdornerLayer(DragScope).Remove(_adorner);
_adorner = null;
IsDragging = false;
}
}
private void DragScope_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
}
private void UIElement_OnPreviewDrop(object sender, DragEventArgs e)
{
var stackPanel = sender as StackPanel;
if (stackPanel == null) return;
var student = stackPanel.DataContext as Person;
MyImage myImage = e.Data.GetData(typeof(MyImage)) as MyImage;
if (student != null) student.Points += myImage.Points;
}
}
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Person : INotifyPropertyChanged
{
private string _name;
private int _points;
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public int Points
{
get { return _points; }
set
{
if (value == _points) return;
_points = value;
if (_points >= 100)
{
_points -= 100;
Debug.WriteLine("100!");
}
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyImage
{
public string Path { get; set; }
public string Name { get; set; }
public int Points { get; set; }
}
}
和 DragAdorner(取自 http://www.infragistics.com/community/blogs/alex_fidanov/archive/2009/07/28/drag-amp-drop-with-datapresenter-family-controls.aspx)
class DragAdorner : Adorner
{
public DragAdorner(UIElement owner) : base(owner) { }
public DragAdorner(UIElement owner, UIElement adornElement, bool useVisualBrush, double opacity)
: base(owner)
{
_owner = owner;
VisualBrush _brush = new VisualBrush
{
Opacity = opacity,
Visual = adornElement
};
DropShadowEffect dropShadowEffect = new DropShadowEffect
{
Color = Colors.Black,
BlurRadius = 15,
Opacity = opacity
};
Rectangle r = new Rectangle
{
RadiusX = 3,
RadiusY = 3,
Fill = _brush,
Effect = dropShadowEffect,
Width = adornElement.DesiredSize.Width,
Height = adornElement.DesiredSize.Height
};
XCenter = adornElement.DesiredSize.Width / 2;
YCenter = adornElement.DesiredSize.Height / 2;
_child = r;
}
private void UpdatePosition()
{
AdornerLayer adorner = (AdornerLayer)Parent;
if (adorner != null)
{
adorner.Update(AdornedElement);
}
}
#region Overrides
protected override Visual GetVisualChild(int index)
{
return _child;
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
protected override Size MeasureOverride(Size finalSize)
{
_child.Measure(finalSize);
return _child.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
_child.Arrange(new Rect(_child.DesiredSize));
return finalSize;
}
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
GeneralTransformGroup result = new GeneralTransformGroup();
result.Children.Add(base.GetDesiredTransform(transform));
result.Children.Add(new TranslateTransform(_leftOffset, _topOffset));
return result;
}
#endregion
#region Field & Properties
public double scale = 1.0;
protected UIElement _child;
protected VisualBrush _brush;
protected UIElement _owner;
protected double XCenter;
protected double YCenter;
private double _leftOffset;
public double LeftOffset
{
get { return _leftOffset; }
set
{
_leftOffset = value - XCenter;
UpdatePosition();
}
}
private double _topOffset;
public double TopOffset
{
get { return _topOffset; }
set
{
_topOffset = value - YCenter;
UpdatePosition();
}
}
#endregion
}
拖动几乎可以正常工作:
除装饰器仅在源列表和目标列表中可见外,在整个拖动过程中不显示。
我的问题是:
- 如何修复拖放以一直查看装饰器?
- 如何在装饰器中显示图像而不是 selectedItem?现在装饰里面是棕色背景,我只想得到透明图像。
- 如何显示装饰器内的 dragtarget 是否正确而不是更改光标?如果目标正确,我想更改装饰器的不透明度。
- 我想要拖放处理触摸事件,@KOTIX 建议使用 Gong WPF 拖放,它在支持触摸的屏幕上可以正常工作吗?
- 目前我正在 ListBox ItemTemplate 内的 StackPanel 上设置
AllowDrop
,它应该留在那里还是我应该在 ListBox 上设置?
我在互联网(包括 SO)上搜索了解决方案,但找不到适合我需要的任何东西。
我发现了一些很棒的文章:
http://www.codeproject.com/Articles/37161/WPF-Drag-and-Drop-Smorgasbord
http://www.zagstudio.com/blog/488#.VgHPyxHtmkp
http://nonocast.cn/adorner-in-wpf-part-5-drag-and-drop/
https://blogs.claritycon.com/blog/2009/03/generic-wpf-drag-and-drop-adorner/
最后一个非常有趣,但我无法修改为向玩家添加点数而不是移动物品。在我的例子中,我希望左侧的项目保留,我只想根据拖动的项目更新右侧的列表。
我建议使用 Gong WPF dragdrop
这个库对我来说一直很好用,而且比内置支持要好得多。它还增加了对 MVVM 设计的强大支持。
祝你好运!
我还没有找到所有答案,但它可能会引导您实现目标。
How can I fix drag and drop to see adorner whole time?
你的DragLayer是透明的,只需为你的
Grid
的How can I display image instead selectedItem inside adorner? Right now inside adorner is that brown background, I'd like to get only transparent image.
Background
属性设置一个值
我最好的办法是探索 VisualTree
以获取模板内的 Image
:
//Get the `ListViewItem`
FrameworkElement dr = MyList.ItemContainerGenerator.ContainerFromItem(MyList.SelectedItem) as FrameworkElement;
//Explore the VisualTree to get the grand-child
//This should be refactored to a Func<UIElement,UIElement> to accord to templates changes
UIElement el = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(dr, 0), 0) as UIElement;
//Create the DragAdorner using the found UIElement
_adorner = new DragAdorner(DragScope, el, true, 1d);
How can I show if dragtarget is correct inside adorner instead of changing cursor? I'd like to change opacity of adorner if target is correct.
要显示一些反馈,您需要... GiveFeedBack
这是您的处理程序:
private void DragScope_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
if (_adorner == null) return;
if (e.Effects == DragDropEffects.Copy)
{
_adorner.Opacity = 1d;
e.Handled = true;
}
else
{
_adorner.Opacity = 0.5d;
e.Handled = true;
}
}
现在,您必须在 2 个地方设置所需的效果:Person
模板和 DragLayer
:
private void StackPanel_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
e.Handled = true;
}
void Window1_DragOver(object sender, DragEventArgs args)
{
if (_adorner == null) return;
_adorner.LeftOffset = args.GetPosition(DragScope).X;
_adorner.TopOffset = args.GetPosition(DragScope).Y;
if (!args.Handled)
args.Effects = DragDropEffects.Move;
}
为了使这些起作用,您必须在启动 DragDrop 时允许这 2 个效果:
DragDropEffects de = DragDrop.DoDragDrop(MyList, data, DragDropEffects.Move | DragDropEffects.Copy);
此外,为避免累积不透明度降低,请在构造函数中将 DragAdorner
不透明度设为 1。
I'd like to get drag and drop working with touch events, @KOTIX suggested using Gong WPF dragdrop, will it work fine on touch enabled screens?
这个解决方案是完全原生的,你应该让它与触摸设备一起工作。
Currently I'm setting AllowDrop on StackPanel inside ListBox ItemTemplate, should it stay there or maybe I should set in on ListBox?
如果您的目标是将 MyImage.Points
添加到放置目标,则必须在 StackPanel
上设置 AllowDrop
。