Давайте попробуем подправить код из этого ответа, чтобы он не переносил квадрат в реальности до тех пор, пока вы его не отпустите.
Идея с VisualBrush — хорошая, так нам будет легко «размножать» существующие элементы. А вот добавлять фиктивный элемент в коллекцию нехорошо, в конце-концов, у нас перемещение — забота UI-уровня, а не модели.
Поэтому изменений на VM-уровне не будет вовсе, и это хорошо.
Добавим поверх нашего списка квадратов ещё один уровень. Чтобы он не мешал, сделаем его прозрачным для мыши. Нужно будет привязать размеры обоих элементов, для этого нам понадобится имя и 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>
Добавилось имя у 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<Canvas>(dragImageContainer);
var position = e.GetPosition(parent) - relativeMousePos;
Canvas.SetLeft(dragImageContainer, position.X);
Canvas.SetTop(dragImageContainer, position.Y);
}
Да, и мне пришлось немного поменять GetParent, чтобы он работал с произвольным элементом:
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;
}
и в OnMouseDown написать
container = FindParent<Canvas>(this);
Это всё.
Для того, чтобы получить красивую картинку, я ещё добавил цвет в SquareVM:
Color color;
public Color Color
{
get { return color; }
set { if (color != value) { color = value; NotifyPropertyChanged(); } }
}
и задал цвета в 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) }
};
}
И ещё привязался к нему в 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>
Получается вот что:

Если вы хотите, чтобы перемещаемая картинка была поменьше, проще всего уменьшить её через 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>

ItemsControlсCanvas'ом не лежат в одномGrid'е, а есть ещё промежуточные слои). В данном случае, да, можно упростить. – VladD Apr 20 '17 at 19:03<ItemsControl ItemsSource="{Binding Squares}" Width="300" Height="300". Там явно указаны размеры. – VladD Apr 20 '17 at 19:06