0

I am working with a WPF client application that uses a list box to display an itinerary. my task is to implement a drag and drop functionality to reorder the list items in the work list, thereby reordering the itinerary. Each of the items in the work list has its own view and viewmodel, and the orange arrow(starts a permit inspection), the red permit number(brings up more info on the permit) and the check box(selects the item in order to upload the inspection, or save changes. also triggers the buttons on the list box view to become active) are all located in that view. itinerary Pic

I have accomplished the drag and drop using the code behind on the listbox, and the PreviewLeftMouseButtonDown trigger. However it seems that the aforementioned trigger consumed the trigger for the checkbox, but not the red button or the orange button. the "Select All" button on the list box view still works though.
Here is the XAML code for the list item condensed to only show the checkbox code:

<StackPanel Grid.Row="0" Grid.Column="0" Grid.RowSpan="3">
        <CheckBox  ClickMode="Press" IsTabStop="False" Style="{DynamicResource CheckBoxStyle}" HorizontalAlignment="Left" VerticalAlignment="Top" IsChecked="{Binding IsSelected,Mode=TwoWay,FallbackValue='False'}" Margin="0 2 0 0"></CheckBox>
        <Border   BorderBrush="{DynamicResource GrayBorder}" BorderThickness="0 0 1 0"/>
    </StackPanel>

Here is the code for the red permit number box that still works for comparison:

 <StackPanel Grid.Row="0" Grid.Column="1" Margin="4 4 4 4">
        <StackPanel Orientation="Horizontal">
            <Button IsTabStop="False" ClickMode="Press" Command="{Binding PermitDetailViewCommand}" CommandParameter="{Binding RequestInfo.PermitNumber}"  Style="{StaticResource PermitDetailButton}" Content="{Binding RequestInfo.PermitNumber, Mode=OneTime,FallbackValue=''}"/>
            <Button Style="{StaticResource CriticalIconStyle}" Visibility="{Binding RequestInfo.IsCritical, Converter={StaticResource BooleanToVisibility}}" Margin="0,4,0,0" ToolTip="{Binding RequestInfo.CriticalInformation}" Height="14" IsTabStop="False"/>
            <Button Style="{StaticResource ContactIconStyle}" Content="{Binding RequestInfo.ContactAttemptedCountCode}" Visibility="{Binding RequestInfo.ContactAttemptedCountCode, Converter={StaticResource StringToVisibility}}" Margin="0,2,0,0" IsTabStop="False"/>
        </StackPanel>
    </StackPanel>

Here is the XAML code that instantiates each item in the list box:

        <ListBox Margin="4,8" ItemsSource="{Binding Path=CheckedOutVM,Mode=TwoWay,IsAsync=True}"
              SelectedItem="{Binding Path=SelectedLocalPermit}" Grid.Row="1" Grid.Column="0" BorderThickness="0"  
              KeyboardNavigation.TabNavigation="Continue" Name="RequestCheckedOutV" BorderBrush="{DynamicResource DarkBorder}" attprop:ArrowKeyPressed.IsEnabled="True">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="ClickMode.Press">
                <cmd:EventToCommand
                    Command="{Binding SelectedLocalCommand}"
                    CommandParameter="{Binding SelectedItem}"
               />
            </i:EventTrigger>
        </i:Interaction.Triggers>
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">

                <Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
                <Setter Property="Background" Value="Transparent" />
                <Setter Property="Control.HorizontalContentAlignment" Value="Center"/>
                <Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
                <Setter Property="Control.VerticalContentAlignment" Value="Top"/>
                <Setter Property="AllowDrop" Value="True"/>

                <EventSetter Event="PreviewMouseLeftButtonDown" Handler="S_PreviewMouseLeftButtonDown"/>
                <EventSetter Event="Drop" Handler="listbox1_Drop"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}"  >
                            <ContentPresenter />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>

            </Style>
        </ListBox.ItemContainerStyle>
        <ListBox.ItemTemplate>
            <DataTemplate >
                <ContentControl  Content="{Binding}" IsTabStop="False"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

Here is the code behind on the list box to allow drag and drop:

 public partial class WorklistSmallView : UserControl
{
    WorklistSmallViewModel vm = new WorklistSmallViewModel();
    /// <summary>
    /// Initializes a new instance of the WorklistSmallView class.
    /// </summary>
    public WorklistSmallView()
    {
        InitializeComponent();
    }
    private void S_PreviewMouseLeftButtonDown(object sender, MouseEventArgs e)
    {
        WorklistSmallViewModel vm = new WorklistSmallViewModel();

        ListBoxItem draggedItem = sender as ListBoxItem;
            draggedItem.IsSelected = true;
            DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
            vm = RequestCheckedOutV.DataContext as WorklistSmallViewModel;
         }



    }

    void listbox1_Drop(object sender, DragEventArgs e)
    {

        WorklistSmallDetailViewModel droppedData = e.Data.GetData(typeof(WorklistSmallDetailViewModel)) as WorklistSmallDetailViewModel;
        WorklistSmallDetailViewModel target = ((ListBoxItem)(sender)).DataContext as WorklistSmallDetailViewModel;

        int removedIdx = RequestCheckedOutV.Items.IndexOf(droppedData);
        int targetIdx = RequestCheckedOutV.Items.IndexOf(target);


        if (removedIdx < targetIdx)
        {
            vm = (WorklistSmallViewModel)RequestCheckedOutV.DataContext;

            vm.CheckedOutVM.Insert(targetIdx + 1, droppedData);
            vm.CheckedOutVM.RemoveAt(removedIdx);
        }
        else
        {
            int remIdx = removedIdx + 1;
            if (vm.CheckedOutVM.Count + 1 > remIdx)
            {
                vm.CheckedOutVM.Insert(targetIdx, droppedData);
                vm.CheckedOutVM.RemoveAt(remIdx);
            }
        }

        foreach (var item in vm.CheckedOutVM)
        {
            item.RouteOrder = RequestCheckedOutV.Items.IndexOf(item) + 1;
        }
        RequestCheckedOutV.Items.Refresh();
    }
}

I appreciate any help that is offered and if you need any more info please let me know.

Update#1 Troubleshooting attempts and results 1. Remove the dynamic style from the checkbox and use the checkbox. Results- No change in the checkbox. seems the event isnt making it to the ListBox Item, except it is still allowing the buttons to be clicked.

  1. changed the event to a MouseLeftButtonDown event, in the listBox. result Check box works, the drag and drop function does not. seems like the timing of a PreviewMouseEvent vs a normal MouseEvent is what allows the drag and drop to work.
4
  • 1
    If you take that defined Style off your Checkbox do you get the same result? Commented May 7, 2018 at 20:01
  • yes sir. that was the first thing I tried. the funny thing is the red button is still working, so does that mean the listBox isnt consuming the event? Commented May 7, 2018 at 20:19
  • 1
    hmm, is your red & orange buttons of type Button? That would quasi-explain things since your ListBoxItem inherits from the same ToggleButton class that Checkbox derives from. I mean there's some simple work arounds but let me think for a min, I'm a little rusty on the wpf/xaml side since flipping over to web stuff about a year ago but surely we can figure it out. Commented May 7, 2018 at 20:25
  • Workarounds are ok too, i just need it to let me check the box and drag the item to reposition in the list. Commented May 8, 2018 at 14:07

1 Answer 1

0

So after searching exhaustively, i tried to implement some logic when the events occur to eliminate the consumption of the event. I figured that if I were to put an 'if' statement in the event without an else.

I also found that I needed two event triggers to do this. So here are the event triggers that i used in the view:

 <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">

                <Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
                <Setter Property="Background" Value="Transparent" />
                <Setter Property="Control.HorizontalContentAlignment" Value="Center"/>
                <Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
                <Setter Property="Control.VerticalContentAlignment" Value="Top"/>
                <Setter Property="AllowDrop" Value="True"/>
                <EventSetter Event="PreviewMouseLeftButtonDown" Handler="RequestCheckedOutV_PreviewMouseLeftButtonDown"/>
                <EventSetter Event="PreviewMouseMove" Handler="RequestCheckedOutV_PreviewMouseMove"/>
                <EventSetter Event="Drop" Handler="listbox1_Drop"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}"  >
                            <ContentPresenter />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>

            </Style>
        </ListBox.ItemContainerStyle>

Then, in the code behind, I did all of the logic for the drag and drop in order to move the item without consuming the event: First i needed to create 2 local variables to use throughout the code behind. The _startPoint is a x,y point location that is used to compare movement. the dragAction is a boolean that lets the event handler know when the user is starting a drag action. public Point _startPoint { get; set; } public bool dragAction = false;

In the PreviewMouseButtonDown event i set the starting location of the Mouse cursor to _startPoint.

    private void RequestCheckedOutV_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        _startPoint = e.GetPosition(null);
    }

The PreviewMouseMove handler is where I compare the location of the starting point to the release location of the mouse, then compare the results to the minimum movement specs preset in the system parameters.(mine was preset to 4) If its more that the minimum, the handler knows it is a Drag drop event and calls the StartDrag and sets the dragAction to true.

    private void RequestCheckedOutV_PreviewMouseMove(object sender, MouseEventArgs e)
    {

        if (Mouse.LeftButton == MouseButtonState.Pressed && !dragAction)
        {
            Point position = e.GetPosition(null);
            if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||

                Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)

            {
                dragAction = true;
                this.StartDrag(sender, e);
            }
        }
    }

the StartDrag method below actually calls the DoDragDrop method. then it also resets the drag action variable to false so that if it is dropped in error, the Events can be reset.

    private void StartDrag(object sender, MouseEventArgs e)
    {

        ListBoxItem draggedItem = sender as ListBoxItem;
        draggedItem.IsSelected = true;
        DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
        dragAction = false;
    }

My drop Handler didnt change at all. I dont know if this is the best way to make this happen, but it worked for me. i figure if it saves some time for me, hopefully it will help others out too.

Sign up to request clarification or add additional context in comments.

3 Comments

Yeah... it works like a charm. but now i gotta get a shadow to go with the mouse movement. DragAdorner I think
A shadow on which part? Generally I advise avoiding bitmap pixel shader effects for interactions like that unless it's running on a machine with some decent specs....but if you need it you could just invoke a DropShadowEffect on the ListBoxItem during the drag if I understand right.
technically i would like a faded copy of the ListBoxItem that is being dragged to be attached to the mouse cursor while the drag event is occurring. Similar to how windows 10 drag drop cursor becomes a faded copy of the shortcut on the desktop.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.