Casi he conseguido este trabajo, aparte de un poco molesto cosa…

Porque el cuadro de lista de selección que sucede en el ratón hacia abajo, si se inicia el arrastre con el ratón hacia abajo cuando se selecciona el último elemento de arrastre funciona bien, pero si selecciona todos los elementos de arrastre primero y, a continuación, haga clic en la selección para iniciar el arrastre, el que usted haga clic en recibe no seleccionados y que quedaron después de la operación de arrastre.

Alguna idea sobre la mejor manera de conseguir alrededor de esto?

<DockPanel LastChildFill="True">
    <ListBox ItemsSource="{Binding SourceItems}"
             SelectionMode="Multiple"
             PreviewMouseLeftButtonDown="HandleLeftButtonDown"
             PreviewMouseLeftButtonUp="HandleLeftButtonUp"
             PreviewMouseMove="HandleMouseMove"
             MultiSelectListboxDragDrop:ListBoxExtension.SelectedItemsSource="{Binding SelectedItems}"/>
    <ListBox ItemsSource="{Binding DestinationItems}"
             AllowDrop="True"
             Drop="DropOnToDestination"/>
<DockPanel>

public partial class Window1
{
    private bool clickedOnSourceItem;

    public Window1()
    {
        InitializeComponent();
        DataContext = new WindowViewModel();
    }

    private void DropOnToDestination(object sender, DragEventArgs e)
    {
        var viewModel = (WindowViewModel)
                            e.Data.GetData(typeof(WindowViewModel));
        viewModel.CopySelectedItems();
    }

    private void HandleLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var sourceElement = (FrameworkElement)sender;
        var hitItem = sourceElement.InputHitTest(e.GetPosition(sourceElement))
                      as FrameworkElement;

        if(hitItem != null)
        {
            clickedOnSourceItem = true;
        }
    }

    private void HandleLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        clickedOnSourceItem = false;
    }

    private void HandleMouseMove(object sender, MouseEventArgs e)
    {
        if(clickedOnSourceItem)
        {
            var sourceItems = (FrameworkElement)sender;
            var viewModel = (WindowViewModel)DataContext;

            DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
            clickedOnSourceItem = false;
        }
    }
}
InformationsquelleAutor IanR | 2009-10-12

4 Comentarios

  1. 13

    Así que…después de haber convertido en el orgulloso propietario de un tumbleweed insignia, tengo que recuperar a este para intentar & encontrar una manera alrededor de ella. 😉

    No estoy seguro de que me gusta la solución, así que todavía estoy abierto a cualquier mejor enfoques.

    Básicamente, lo que terminó haciendo es recordar lo que ListBoxItem fue el último clic en & a continuación, asegúrese de que se agrega a los elementos seleccionados antes de arrastrar. Esto también significa mirar a lo lejos se mueve el ratón antes de iniciar una operación de arrastrar porque al hacer clic sobre un elemento seleccionado para anular la selección que a veces puede resultar en que se está activada de nuevo si el ratón rebote empezó un poco operación de arrastre.

    Por último, he añadido algunas caliente, el seguimiento de los elementos de listbox así, si pasa el ratón sobre un elemento seleccionado va a ser desactivada, pero usted todavía obtener algunos comentarios para indicar que se incluirán en la operación de arrastre.

    private void HandleLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var source = (FrameworkElement)sender;
        var hitItem = source.InputHitTest(e.GetPosition(source)) as FrameworkElement;
        hitListBoxItem = hitItem.FindVisualParent<ListBoxItem>();
        origPos = e.GetPosition(null);
    }
    private void HandleLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        hitListBoxItem = null;
    }
    private void HandleMouseMove(object sender, MouseEventArgs e)
    {
        if (ShouldStartDrag(e))
        {
            hitListBoxItem.IsSelected = true;
    
            var sourceItems = (FrameworkElement)sender;
            var viewModel = (WindowViewModel)DataContext;
            DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
            hitListBoxItem = null;
        }
    }
    private bool ShouldStartDrag(MouseEventArgs e)
    {
        if (hitListBoxItem == null)
            return false;
    
        var curPos = e.GetPosition(null);
        return
      Math.Abs(curPos.Y-origPos.Y) > SystemParameters.MinimumVerticalDragDistance ||
      Math.Abs(curPos.X-origPos.X) > SystemParameters.MinimumHorizontalDragDistance;
    }

    XAML cambios incluyen bañera de seguimiento…

    <Style TargetType="ListBoxItem">
        <Setter Property="Margin" Value="1"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Grid>
                      <Border Background="{TemplateBinding Background}" />
                      <Border Background="#BEFFFFFF" Margin="1">
                        <Grid>
                          <Grid.RowDefinitions>
                            <RowDefinition /><RowDefinition />
                          </Grid.RowDefinitions>
                          <Border Margin="1" Grid.Row="0" Background="#57FFFFFF" />
                        </Grid>
                      </Border>
                      <ContentPresenter Margin="8,5" />
                    </Grid>
                    <ControlTemplate.Triggers>
                      <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="PowderBlue" />
                      </Trigger>
                      <MultiTrigger>
                        <MultiTrigger.Conditions>
                          <Condition Property="IsMouseOver" Value="True" />
                          <Condition Property="IsSelected" Value="False"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Background" Value="#5FB0E0E6" />
                      </MultiTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
  2. 12

    He encontrado una manera muy sencilla de habilitar el Explorador de Windows como arrastrar/soltar el comportamiento en caso de tener varios elementos seleccionados. La solución se sustituye el común ListBox con un poco de derivados de la laminilla que sustituye a la ListBoxItem con una forma más inteligente de la versión. De esta manera, podemos encapsular haga clic en el estado en el nivel adecuado, y llamar a la selección protegida de la maquinaria de la ListBox. Aquí es la clase de que se trate. Para ver un ejemplo completo, ver mi repo en github.

    public class ListBoxEx : ListBox
    {
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new ListBoxItemEx();
        }
    
        class ListBoxItemEx : ListBoxItem
        {
            private bool _deferSelection = false;
    
            protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
            {
                if (e.ClickCount == 1 && IsSelected)
                {
                    //the user may start a drag by clicking into selected items
                    //delay destroying the selection to the Up event
                    _deferSelection = true;
                }
                else
                {
                    base.OnMouseLeftButtonDown(e);
                }
            }
    
            protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
            {
                if (_deferSelection)
                {
                    try
                    {
                        base.OnMouseLeftButtonDown(e);
                    }
                    finally
                    {
                        _deferSelection = false;
                    }
                }
                base.OnMouseLeftButtonUp(e);
            }
    
            protected override void OnMouseLeave(MouseEventArgs e)
            {
                //abort deferred Down
                _deferSelection = false;
                base.OnMouseLeave(e);
            }
        }
    }
    • Esta es una gran solución. Sólo he probado y funciona como un encanto. Creo que esta debe ser la respuesta seleccionada, ya que es más limpio y más confiable de la OMI.
    • Simple y funciona como un encanto.
    • las grandes obras. dios los bendiga
    • El. Perfecto. Solución.
    • Bueno! Aunque espeluznante que es necesario… Una pregunta: ¿por qué el try...finally bloque? Qué podría ir mal y puede ser ignorada con seguridad?
    • El finally cláusula se asegura de que _deferSelection es restablecer correctamente en el evento. Eso fue hace cinco años, así que ni idea de lo que en realidad podría causar excepciones para ser arrojado allí, pero supongo que cualquier otro controlador de evento que podría arruinar.

  3. 2

    Una opción sería la de no permitir el control ListBox o ListView para quitar los elementos seleccionados hasta MouseLeftButtonUp se activa.
    Ejemplo de código:

        List<object> removedItems = new List<object>();
    
        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.RemovedItems.Count > 0)
            {
                ListBox box = sender as ListBox;
                if (removedItems.Contains(e.RemovedItems[0]) == false)
                {
                    foreach (object item in e.RemovedItems)
                    {
                        box.SelectedItems.Add(item);
                        removedItems.Add(item);
                    }
                }
            }
        }
    
        private void ListBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (removedItems.Count > 0)
            {
                ListBox box = sender as ListBox;
                foreach (object item in removedItems)
                {
                    box.SelectedItems.Remove(item);
                }
                removedItems.Clear();
            }
        }
    • Se siente muy mal, pero ya esta resuelto el mismo problema para mí!
  4. 2

    Me sorprendió que el comportamiento de la diferencia entre el ListBox y el Explorador de Windows no se ha tenido el cuidado de después de 4 años en 3 de las principales actualizaciones de la misma .NET framework.

    Me encontré a este problema en Silverlight 3. Terminé reemplazando el botón del ratón y el ratón de seguridad de controlador de eventos a simular completamente el Explorador de Windows comportamiento.

    No tengo el código fuente, sino que la lógica debe ser:

    Cuando el ratón hacia abajo

    • si la opción no está seleccionada, claro selección existente
      • si la tecla Ctrl presionada, agregar elemento de destino para la selección de
      • si la tecla Shift presionada
        • si hay un elemento seleccionado anteriormente, agregar todos los elementos entre el elemento objetivo y el elemento anterior a la selección
        • sólo agregar elemento de destino para la selección de
    • si la opción está seleccionada de seleccionar sólo si la tecla Ctrl presionada

    Cuando el ratón (en el mismo artículo)

    • si la opción está seleccionada
      • si la tecla Ctrl presionada, quitar el elemento de la selección
      • si la tecla Shift presionada
        • si hay un elemento seleccionado anteriormente, retire todos los artículos entre el elemento objetivo y el elemento anterior de la selección
        • sólo quitar el elemento de destino de la selección

    Sin embargo
    Esta realidad debería ser de Microsoft de trabajo para actualizar el comportamiento que se debe ser coherente con el sistema operativo y para ser más intuitiva. Me he presentado como un error a Microsoft si cualquier cuerpo quiere votar por ella: http://connect.microsoft.com/VisualStudio/feedback/details/809192/

Dejar respuesta

Please enter your comment!
Please enter your name here