Trigger WPF Checkbox on List Item from ListBox that has Drag and Drop

[亡魂溺海] 提交于 2020-01-06 14:25:41

问题


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.

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.

回答1:


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.



来源:https://stackoverflow.com/questions/50219002/trigger-wpf-checkbox-on-list-item-from-listbox-that-has-drag-and-drop

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!