首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WPF拖动Adorner固定移动并在Adorner中显示反馈

WPF拖动Adorner固定移动并在Adorner中显示反馈
EN

Stack Overflow用户
提问于 2015-09-22 22:07:18
回答 2查看 3.8K关注 0票数 0

我正在尝试为我正在创建的项目构建类似于界面的库存。想法是有一个可以被拖到玩家的图片列表,如下所示:

图像从目录加载并显示在ListView中,播放机列表显示在ListBox中。

我的XAML看起来如下:

代码语言:javascript
复制
<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>

代码背后:

代码语言:javascript
复制
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 (取自fidanov/archive/2009/07/28/drag-amp-drop-with-datapresenter-family-controls.aspx)

代码语言:javascript
复制
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?现在里面装饰的是那个棕色的背景,我只想得到透明的图像。
  • 如何显示拖动目标在装饰器中是否正确,而不是改变光标?如果目标是正确的,我想改变装饰的不透明度。
  • 我想让拖放处理触摸事件,@KOTIX建议使用龚WPF拖放,它在启用触摸的屏幕上会正常工作吗?
  • 目前,我正在将AllowDrop设置在ListBox ItemTemplate中的StackPanel上,它应该停留在那里,还是应该放在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/

最后一个非常有趣,但我无法修改它,以增加积分给玩家,而不是移动项目。在我的例子中,我希望左边的项目保持不变,我只想根据拖动的项目更新右侧的列表。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-09-23 13:49:59

我还没有得到所有的答案,但它可能会引导你达到你的目标。

  1. 怎么才能一直固定拖放看装饰品呢?

您的DragLayer是透明的,只需为GridBackground属性设置一个值

  1. 如何在装饰器中显示图像而不是selectedItem?现在里面装饰的是那个棕色的背景,我只想得到透明的图像。

我所讨论的最好的方法是探索VisualTree以获取模板中的Image

代码语言:javascript
复制
//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);
  1. 如何显示拖动目标在装饰器中是否正确,而不是改变光标?如果目标是正确的,我想改变装饰的不透明度。

为了显示一些反馈,你需要..。GiveFeedBack

下面是您的处理程序:

代码语言:javascript
复制
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;
    }
}

现在,您必须在两个位置设置所需的效果:Person模板和DragLayer

代码语言:javascript
复制
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种效果:

代码语言:javascript
复制
DragDropEffects de = DragDrop.DoDragDrop(MyList, data, DragDropEffects.Move | DragDropEffects.Copy);

此外,为了避免累积不透明度减少,在构造函数中使用1作为DragAdorner不透明度。

  1. 我想让拖放处理触摸事件,@KOTIX建议使用龚WPF拖放,它在启用触摸的屏幕上会正常工作吗?

这个解决方案是完全原生的,您应该让它与触摸设备一起工作。

  1. 目前,我正在将AllowDrop设置在ListBox ItemTemplate中的StackPanel上,它应该停留在那里,还是应该放在ListBox上?

如果您的目标是将MyImage.Points添加到drop目标,则必须在StackPanel上设置AllowDrop

票数 1
EN

Stack Overflow用户

发布于 2015-09-22 23:24:32

我建议使用宫WPF拖拽

这个图书馆一直为我工作很好,比内置的支持要好得多。它还增加了对MVVM设计的极大支持。

祝好运!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32727755

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档