そしてコーデに…
マークアップ:
<AdornerDecorator Margin="5">
<ListBox x:Name="_listBox" Width="300"
HorizontalAlignment="Left"
ItemsSource="{Binding Path=Items}"
AllowDrop="True" Drop="listBox_Drop">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="ListBoxItem.DragOver" Handler="listBoxItem_DragOver"/>
<EventSetter Event="ListBoxItem.Drop" Handler="listBoxItem_Drop"/>
<EventSetter Event="ListBoxItem.MouseMove" Handler="listBoxItem_MouseMove"/>
<EventSetter Event="ListBoxItem.MouseDown" Handler="listBoxItem_MouseDown"/>
<EventSetter Event="ListBoxItem.PreviewMouseDown" Handler="listBoxItem_MouseDown"/>
<Setter Property="AllowDrop" Value="True"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</AdornerDecorator>
コードビハインド:
private bool _isDragging;
private void listBox_MouseDown(object sender, MouseButtonEventArgs e)
{
_isDragging = false;
}
Adorner _adorner;
private void listBox_MouseMove(object sender, MouseEventArgs e)
{
if (!_isDragging && e.LeftButton == MouseButtonState.Pressed)
{
_isDragging = true;
if (_listBox.SelectedValue != null)
{
DragDrop.DoDragDrop(_listBox, _listBox.SelectedValue,
DragDropEffects.Move);
}
}
}
private ListBoxItem FindlistBoxItem(DragEventArgs e)
{
var visualHitTest = VisualTreeHelper.HitTest(_listBox, e.GetPosition(_listBox)).VisualHit;
ListBoxItem listBoxItem = null;
while (visualHitTest != null)
{
if (visualHitTest is ListBoxItem)
{
listBoxItem = visualHitTest as ListBoxItem;
break;
}
else if (visualHitTest == _listBox)
{
Console.WriteLine("Found listBox instance");
return null;
}
visualHitTest = VisualTreeHelper.GetParent(visualHitTest);
}
return listBoxItem;
}
void ClearAdorner()
{
if (_adorner != null)
{
var adornerLayer = AdornerLayer.GetAdornerLayer(_listBox);
adornerLayer.Remove(_adorner);
}
}
private void listBox_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Move;
ClearAdorner();
var listBoxItem = FindlistBoxItem(e);
if (listBoxItem == null || listBoxItem.DataContext == _listBox.SelectedItem) return;
if (IsInFirstHalf(listBoxItem, e.GetPosition(listBoxItem)))
{
var adornerLayer = AdornerLayer.GetAdornerLayer(_listBox);
_adorner = new DropBeforeAdorner(listBoxItem);
adornerLayer.Add(_adorner);
}
else if (IsInLastHalf(listBoxItem, e.GetPosition(listBoxItem)))
{
var adornerLayer = AdornerLayer.GetAdornerLayer(_listBox);
_adorner = new DropAfterAdorner(listBoxItem);
adornerLayer.Add(_adorner);
}
}
private void listBox_Drop(object sender, DragEventArgs e)
{
if (_isDragging)
{
_isDragging = false;
ClearAdorner();
var listBoxItem = FindlistBoxItem(e);
if (listBoxItem == null || listBoxItem.DataContext == _listBox.SelectedItem) return;
var drop = _listBox.SelectedItem as Export.Domain.Components.Component;
var target = listBoxItem.DataContext as Export.Domain.Components.Component;
var listBoxItem = GetlistBoxItemControl(listBoxItem);
if (IsInFirstHalf(listBoxItem, e.GetPosition(listBoxItem)))
{
var vm = this.DataContext as ComponentlistBoxModel;
vm.DropBefore(drop, target);
}
else if (IsInLastHalf(listBoxItem, e.GetPosition(listBoxItem)))
{
var vm = this.DataContext as ComponentlistBoxModel;
vm.DropAfter(drop, target);
}
}
}
public static bool IsInFirstHalf(FrameworkElement container, Point mousePosition)
{
return mousePosition.Y < (container.ActualHeight/2);
}
public static bool IsInLastHalf(FrameworkElement container, Point mousePosition)
{
return mousePosition.Y > (container.ActualHeight/2);
}
私のコード ビハインドが具体的にビューモデルをタイプ別に参照しているという事実が気に入らないかもしれませんが、それは仕事を成し遂げました。それは迅速かつ簡単で、技術的には MVVM パターンを壊すことはありません。ロジックは引き続きビューモデルに任せます。
追加 1
アニメーションは、おそらく探している効果を提供します。私の実装では、ドロップ時にスワップが発生します。ただし、アドナーを使用してドラッグ時にスワップを発生させることで、アニメーション効果を実現できます。ドラッグ イベントは、コレクション内のオブジェクトの装飾位置とインデックスを更新します。