Страницы

Поиск по вопросам

четверг, 5 декабря 2019 г.

Drag & Drop с предпросмотром

#wpf #xaml #mvvm


Как сделать такой Drag&Drop с preview как на гифке при этом в рамках MVVM?

Есть Canvas с привязанной коллекцией объектов. Каждый объект представляет из себя
CustomControl в котором уже реализован обычный Drag&Drop по примеру из этого ответа.

Думал при MouseMove делать VisualBrush текущего объекта, рисовать новый прямоугольник
этой кистью и уже его перетаскивать. Но ведь Canvas привязан к коллекции и получается,
что этот прямоугольник также нужно будет добавлять в коллекцию, чтобы он отобразился.


    
        
            
        
    
    
        
           
        
    
    
        
    




    


Ответы

Ответ 1



Давайте попробуем подправить код из этого ответа, чтобы он не переносил квадрат в реальности до тех пор, пока вы его не отпустите. Идея с VisualBrush — хорошая, так нам будет легко «размножать» существующие элементы. А вот добавлять фиктивный элемент в коллекцию нехорошо, в конце-концов, у нас перемещение — забота UI-уровня, а не модели. Поэтому изменений на VM-уровне не будет вовсе, и это хорошо. Добавим поверх нашего списка квадратов ещё один уровень. Чтобы он не мешал, сделаем его прозрачным для мыши. Нужно будет привязать размеры обоих элементов, для этого нам понадобится имя и Binding: Добавилось имя у ItemsControl, у DraggableSquare появилась дополнительная привязка (о ней позже), и внизу появился Canvas, содержащий нужный элемент. Далее, DraggbleSquare. Мы меняем немного нашу внутреннюю логику. UpdatePosition будет теперь вызываться только при отпускании мыши или потере фокуса окна. void OnDragMove(object sender, MouseEventArgs e) { //UpdatePosition(e); UpdateDraggedSquarePosition(e); } void FinishDrag(object sender, MouseEventArgs e) { MouseMove -= OnDragMove; LostMouseCapture -= OnLostCapture; UpdatePosition(e); UpdateDraggedSquarePosition(null); } Новая часть логики: UpdateDraggedSquarePosition. Для неё нам понадобится новое dependency property DraggedImageContainer, в которое мы в XAML уже положили элемент, представляющий перемещающуюся картинку. (Поместите это повыше, около RequestMoveCommand.) #region dp Shape DraggedImageContainer public Shape DraggedImageContainer { get { return (Shape)GetValue(DraggedImageContainerProperty); } set { SetValue(DraggedImageContainerProperty, value); } } public static readonly DependencyProperty DraggedImageContainerProperty = DependencyProperty.Register( "DraggedImageContainer", typeof(Shape), typeof(DraggableSquare)); #endregion Собственно сама новая логика: void UpdateDraggedSquarePosition(MouseEventArgs e) { var dragImageContainer = DraggedImageContainer; if (dragImageContainer == null) return; var needVisible = e != null; var wasVisible = dragImageContainer.Visibility == Visibility.Visible; // включаем/выключаем видимость перемещаемой картинки dragImageContainer.Visibility = needVisible ? Visibility.Visible : Visibility.Collapsed; if (!needVisible) // если мы выключились, нам больше нечего делать return; if (!wasVisible) // а если мы были выключены и включились, { // нам надо привязать изображение себя dragImageContainer.Fill = new VisualBrush(this); dragImageContainer.SetBinding( // а также ширину/высоту Shape.WidthProperty, new Binding(nameof(ActualWidth)) { Source = this }); dragImageContainer.SetBinding( Shape.HeightProperty, new Binding(nameof(ActualHeight)) { Source = this }); // Binding нужен потому, что наш размер может по идее измениться } // перемещаем картинку на нужную позицию var parent = FindParent(dragImageContainer); var position = e.GetPosition(parent) - relativeMousePos; Canvas.SetLeft(dragImageContainer, position.X); Canvas.SetTop(dragImageContainer, position.Y); } Да, и мне пришлось немного поменять GetParent, чтобы он работал с произвольным элементом: static private T FindParent(FrameworkElement from) where T : FrameworkElement { FrameworkElement current = from; T t; do { t = current as T; current = (FrameworkElement)VisualTreeHelper.GetParent(current); } while (t == null && current != null); return t; } и в OnMouseDown написать container = FindParent(this); Это всё. Для того, чтобы получить красивую картинку, я ещё добавил цвет в SquareVM: Color color; public Color Color { get { return color; } set { if (color != value) { color = value; NotifyPropertyChanged(); } } } и задал цвета в MainVM: class MainVM : VM { public ObservableCollection Squares { get; } = new ObservableCollection() { new SquareVM() { Position = new Point(30, 30), Color = Color.FromRgb(0x3D, 0x31, 0x5B) }, new SquareVM() { Position = new Point(100, 70), Color = Color.FromRgb(0x44, 0x4B, 0x6E) }, new SquareVM() { Position = new Point(80, 0), Color = Color.FromRgb(0x70, 0x8B, 0x75) }, new SquareVM() { Position = new Point(90, 180), Color = Color.FromRgb(0x9A, 0xB8, 0x7A) }, new SquareVM() { Position = new Point(200, 200), Color = Color.FromRgb(0xF8, 0xF9, 0x91) } }; } И ещё привязался к нему в DraggableSquare.xaml: Получается вот что: Если вы хотите, чтобы перемещаемая картинка была поменьше, проще всего уменьшить её через RenderTransform:

Комментариев нет:

Отправить комментарий