【问题标题】:Trigger WPF Checkbox on List Item from ListBox that has Drag and Drop从具有拖放功能的 ListBox 中触发列表项上的 WPF 复选框
【发布时间】:2018-05-09 02:40:41
【问题描述】:

我正在使用 WPF 客户端应用程序,它使用列表框来显示行程。我的任务是实现拖放功能以重新排序工作列表中的列表项,从而重新排序行程。工作列表中的每个项目都有自己的视图和视图模型,橙色箭头(开始许可证检查),红色许可证编号(显示有关许可证的更多信息)和复选框(选择项目以便检查上传检查或保存更改。还会触发列表框视图上的按钮变为活动状态)都位于该视图中。

我已经使用列表框后面的代码和 PreviewLeftMouseButtonDown 触发器完成了拖放操作。但是,上述触发器似乎消耗了复选框的触发器,而不是红色按钮或橙色按钮。但是,列表框视图上的“全选”按钮仍然有效。
这是列表项的 XAML 代码,压缩后仅显示复选框代码:

<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>

这是红色许可证号框的代码,仍然可以进行比较:

 <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>

下面是实例化列表框中每个项目的 XAML 代码:

        <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>

这是列表框后面允许拖放的代码:

 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();
    }
}

感谢您提供的任何帮助,如果您需要更多信息,请告诉我。

Update#1 故障排除尝试和结果 1、去掉checkbox的动态样式,使用checkbox。结果 - 复选框没有变化。似乎该事件没有进入 ListBox 项,但它仍然允许单击按钮。

  1. 在列表框中将事件更改为 MouseLeftButtonDown 事件。结果复选框有效,拖放功能无效。似乎 PreviewMouseEvent 与普通 MouseEvent 的时间安排是允许拖放工作的原因。

【问题讨论】:

  • 如果您从复选框中删除定义的样式,您会得到相同的结果吗?
  • 是的,先生。那是我尝试的第一件事。有趣的是红色按钮仍在工作,这是否意味着 listBox 没有消耗该事件?
  • 嗯,你的红色和橙色按钮是Button 类型的吗?这可以解释一些事情,因为您的 ListBoxItem 继承自 Checkbox 派生的同一个 ToggleButton 类。我的意思是有一些简单的解决方法,但让我想一想,自从大约一年前翻到 Web 内容后,我在 wpf/xaml 方面有点生疏,但我们肯定可以解决。
  • 解决方法也可以,我只需要它让我选中复选框并拖动项目以在列表中重新定位。

标签: c# wpf xaml triggers


【解决方案1】:

所以在穷尽搜索之后,我尝试在事件发生时实现一些逻辑,以消除对事件的消耗。我想如果我要在没有 else 的情况下在事件中添加一个 'if' 语句。

我还发现我需要两个事件触发器来执行此操作。所以这里是我在视图中使用的事件触发器:

 <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>

然后,在后面的代码中,我完成了拖放的所有逻辑,以便在不消耗事件的情况下移动项目: 首先,我需要创建 2 个局部变量以在后面的代码中使用。 _startPoint 是用于比较运动的 x,y 点位置。 dragAction 是一个布尔值,让事件处理程序知道用户何时开始拖动动作。 公共点 _startPoint { 获取;放; } public bool dragAction = false;

在 PreviewMouseButtonDown 事件中,我将鼠标光标的起始位置设置为 _startPoint。

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

PreviewMouseMove 处理程序是我将起点的位置与鼠标的释放位置进行比较的地方,然后将结果与系统参数中预设的最小移动规格进行比较。(我的预设为 4)如果它更多最小值,处理程序知道这是一个拖放事件并调用 StartDrag 并将 dragAction 设置为 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);
            }
        }
    }

下面的 StartDrag 方法实际上调用了 DoDragDrop 方法。然后它还会将拖动动作变量重置为 false,这样如果它被错误放置,事件可以被重置。

    private void StartDrag(object sender, MouseEventArgs e)
    {

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

我的 drop Handler 根本没有改变。 我不知道这是否是实现这一目标的最佳方式,但它对我有用。我想如果它为我节省了一些时间,希望它也能帮助其他人。

【讨论】:

  • 是的......它就像一个魅力。但现在我必须得到一个阴影来配合鼠标的移动。 DragAdorner 我认为
  • 阴影在哪一部分?一般来说,我建议避免位图像素着色器效果用于这样的交互,除非它在具有一些不错规格的机器上运行......但如果你需要它,如果我理解正确的话,你可以在拖动期间在 ListBoxItem 上调用DropShadowEffect
  • 从技术上讲,我希望在拖动事件发生时将正在拖动的 ListBoxItem 的褪色副本附加到鼠标光标上。类似于 Windows 10 拖放光标如何成为桌面上快捷方式的淡化副本。
猜你喜欢
  • 1970-01-01
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-01
  • 1970-01-01
  • 2013-08-18
  • 1970-01-01
相关资源
最近更新 更多