Let's try to correct the code from this answer so that it does not transfer the square in reality until you release it.
The idea with VisualBrush is a good one, so it will be easy for us to βduplicateβ existing elements. But adding a dummy item to the collection is not good, after all, we have to move - the care of the UI-level, not the model.
Therefore, there will be no changes at the VM level at all, which is good.
Add another level over our list of squares. That it does not interfere, we will make it transparent for the mouse. It will be necessary to bind the sizes of both elements, for this we need the name and Binding :
<Window x:Class="MvvmDraggable2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MvvmDraggable2" Title="Draggable squares 2" Height="350" Width="525"> <Grid> <ItemsControl ItemsSource="{Binding Squares}" Width="300" Height="300" Background="Beige" Name="DraggableItemsHost"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas IsItemsHost="True"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <local:DraggableSquare DraggedImageContainer="{Binding ElementName=DraggedImageContainer}"/> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Canvas.Left" Value="{Binding Position.X}"/> <Setter Property="Canvas.Top" Value="{Binding Position.Y}"/> </Style> </ItemsControl.ItemContainerStyle> </ItemsControl> <Canvas IsHitTestVisible="False"> <Rectangle Opacity="0.4" Name="DraggedImageContainer" Visibility="Collapsed"/> </Canvas> </Grid> </Window>
The name of the ItemsControl was added, the DraggableSquare had an additional binding (about it later), and a Canvas appeared below, containing the desired element.
Next, DraggbleSquare . We change our internal logic a bit. UpdatePosition will now be called only when you release the mouse or lose the focus of the window.
void OnDragMove(object sender, MouseEventArgs e) { //UpdatePosition(e); UpdateDraggedSquarePosition(e); } void FinishDrag(object sender, MouseEventArgs e) { MouseMove -= OnDragMove; LostMouseCapture -= OnLostCapture; UpdatePosition(e); UpdateDraggedSquarePosition(null); }
The new part of the logic: UpdateDraggedSquarePosition . For it, we need a new dependency property DraggedImageContainer , in which we in XAML have already put an element representing the moving image.
(Put it higher, near 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
Actually the new logic itself:
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<Canvas>(dragImageContainer); var position = e.GetPosition(parent) - relativeMousePos; Canvas.SetLeft(dragImageContainer, position.X); Canvas.SetTop(dragImageContainer, position.Y); }
Yes, and I had to slightly change GetParent so that it works with an arbitrary element:
static private T FindParent<T>(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; }
and in OnMouseDown write
container = FindParent<Canvas>(this);
It's all.
In order to get a beautiful picture, I also added color in SquareVM :
Color color; public Color Color { get { return color; } set { if (color != value) { color = value; NotifyPropertyChanged(); } } }
and set the colors in MainVM :
class MainVM : VM { public ObservableCollection<SquareVM> Squares { get; } = new ObservableCollection<SquareVM>() { 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) } }; }
And I also attached to him in DraggableSquare.xaml :
<UserControl x:Class="MvvmDraggable2.DraggableSquare" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="50" Height="50" MouseDown="OnMouseDown" MouseUp="OnMouseUp"> <UserControl.Background> <SolidColorBrush Color="{Binding Color}"/> </UserControl.Background> <Grid/> </UserControl>
It turns out that:

If you want the image to be moved to be smaller, the easiest way to reduce it is through the RenderTransform :
<Canvas IsHitTestVisible="False"> <Rectangle Opacity="0.4" Name="DraggedImageContainer" Visibility="Collapsed" RenderTransformOrigin="0.5,0.5"> <Rectangle.RenderTransform> <ScaleTransform ScaleX="0.9" ScaleY="0.9"/> </Rectangle.RenderTransform> </Rectangle> </Canvas>
