2

所以这是我正在使用的场景:

我有一个优先级列表,当前表示为一个 ItemsControl/ListView,绑定到一个可观察的优先级项目集合。我想为元素重新排序提供严格的视觉约束垂直拖动。

所以,没有拖动装饰器,没有水平移动,只有垂直移动。当一个列表项移过另一个项的中点时,它应该通过动画“交换位置”。我确信这可以通过在容器本身上使用 mousedown/mousemove 来完成,并且我确信可以应用渲染转换来做到这一点,但我理想的解决方案将有两个组件:

  1. 该功能可以作为 WPF 交互行为附加。

  2. 该系统将是 MVVM 友好的,并且不需要任何重要的代码。

这已经完成了吗?我在哪里可以找到它?如果还没有,我怎么能把所有的位放在一起来做到这一点?

编辑:赏金打开。请通过评论直接向我提问,我会尽快回复。

4

2 回答 2

6

而对于代码...

加价:

<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 模式。我仍然将逻辑留给视图模型。

Addition 1 Animations 可能会提供您正在寻找的效果。在我的实现中,交换发生在丢弃时。但是,您可以通过使用装饰器并在拖动时进行交换来实现动画效果。拖动事件将更新装饰器位置和集合中对象的索引。

于 2012-10-25T22:28:10.973 回答
-1

虽然我自己没有提出解决方案,但我曾经遇到过一篇博客文章,我认为它通过使用附加属性和装饰器实现了出色的关注点分离,完全符合您的要求。在这里看一下:ZagStudio。希望能帮助到你。

于 2012-11-03T12:01:09.150 回答