简体   繁体   English

如何将双击绑定到列表框项

[英]How to bind a double click to a listbox item

I have two list boxes that both hold collections.我有两个列表框,它们都包含集合。 The current setup is so that when a item is selected in the left listbox, you can click a button to add that selected state the right listbox.当前的设置是,当在左侧列表框中选择一个项目时,您可以单击一个按钮将所选状态添加到右侧列表框中。 There is an add and remove button for the listboxes that are tied to a custom command with the listbox selected item being the command parameter.与自定义命令相关的列表框有一个添加和删除按钮,列表框所选项目是命令参数。

I would like to add a double click functionality to each box so that items can be double clicked to add and remove.我想为每个框添加双击功能,以便可以双击项目来添加和删除。 I should be able to use my current command execute methods to do this, but have not found a solution to implementing this into a listbox, or listboxitem.我应该能够使用我当前的命令执行方法来执行此操作,但还没有找到将其实现到列表框或列表框项中的解决方案。 I would like to follow MVVM as much as possible, but I've already side stepped that a bit with the current execute methods as i'll show below, but any help would be appreciated.我想尽可能多地遵循 MVVM,但我已经对当前的执行方法进行了一些调整,如下所示,但任何帮助将不胜感激。 I have not had luck finding anything regarding my specific issue.我没有找到任何关于我的具体问题的运气。

<ListBox x:Name="List" ItemContainerStyle="{StaticResource ListBoxItem}" DataContext="{StaticResource VM}" 
                            ItemsSource="{Binding Names, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" DisplayMemberPath="Name"
                                 Style="{StaticResource ResourceKey=ListBox}"/>


<Button Content="&gt;&gt;" Margin="5" Style="{StaticResource ResourceKey=MultiButton}" 
                            CommandParameter="{Binding ElementName=List}"
                            Command="{Binding Path=AddSelectedItemCommand}"/>

public void AddSelectedItem(object obj)
    {
        ListBox ListBox = obj as ListBox;
        List<Type> List = new List<Type>();
        if (Name == null)
            Name = new ObservableCollection<Type>();
        if (Name != null)
        {
            foreach (Type item in ListBox.SelectedItems.Cast<object>().ToList())
            {
                List.Add(item);
                Names.Remove(item);
            }

            foreach (Type listItem in List)
            {
                var state = Name.FirstOrDefault(aa => aa.Name == listItem.Name);
                if (state == null)
                {
                    Name.Add(listItem);
                }
            }
        }
        OnPropertyChanged("Name");
        OnPropertyChanged("Names");
    }

Firstly I would like to let you know that your View Model should know nothing at all about the View itself, so it should know nothing about ListBoxes.首先我想让你知道你的视图模型应该对视图本身一无所知,所以它应该对列表框一无所知。

Objects should only know about they things which they depend upon, and not those which depend upon it.对象应该只知道它们所依赖的事物,而不是那些依赖它的事物。 Therefore the ViewModel should only know about the collections of data which it is making available to any client.因此 ViewModel 应该只知道它提供给任何客户端的数据集合。

In your example, what happens when the control is changed from a ListBox -you will have to change your Command.在您的示例中,当控件从 ListBox 更改时会发生什么 - 您将不得不更改您的命令。

So, first things first, you will need to change your view model implementation, what you have currently is not MVVM.因此,首先,您需要更改您的视图模型实现,您目前拥有的不是 MVVM。

Here is an entire listing which should help you along your way:这是一个完整的清单,可以帮助您一路走来:

<Window x:Class="WpfExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="140" Width="410">
   <Grid>
      <Grid.ColumnDefinitions>
         <ColumnDefinition />
         <ColumnDefinition />
      </Grid.ColumnDefinitions>
      <ListBox ItemsSource="{Binding Path=Names, Mode=OneWay}"
               SelectedItem="{Binding Path=SelectedName}">
         <ListBox.ItemTemplate>
            <DataTemplate>
               <TextBlock Text="{Binding .}">

                  <TextBlock.InputBindings>
                     <MouseBinding MouseAction="LeftDoubleClick" 
                                   Command="{Binding Path=DataContext.MyDoubleClickCommand, 
                                                     RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor} }" />
                  </TextBlock.InputBindings>
               </TextBlock>
            </DataTemplate>
         </ListBox.ItemTemplate>
      </ListBox>
      <ListBox Grid.Column="1" Margin="10,0,0,0" ItemsSource="{Binding Path=NamesTwo, Mode=OneWay}"
               SelectedItem="{Binding Path=SelectedNameTwo}">
         <ListBox.ItemTemplate>
            <DataTemplate>
               <TextBlock Text="{Binding .}">

                  <TextBlock.InputBindings>
                     <MouseBinding MouseAction="LeftDoubleClick" 
                                   Command="{Binding Path=DataContext.MyOtherDoubleClickCommand, 
                                                     RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor} }" />
                  </TextBlock.InputBindings>
               </TextBlock>
            </DataTemplate>
         </ListBox.ItemTemplate>
      </ListBox>
   </Grid>
</Window>

And the code behind以及背后的代码

namespace WpfExample
{
   /// <summary>
   /// Interaction logic for MainWindow.xaml
   /// </summary>
   public partial class MainWindow : Window
   {
      public MainWindow()
      {
         InitializeComponent();
         DataContext = new MyViewModel();
      }
   }
}

Then there is the ViewModel, which you should notice only modifies the collections which are exposed for the View to consume然后是 ViewModel,您应该注意到它只会修改暴露给 View 使用的集合

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Prism.Commands;

namespace WpfExample
{
   public class MyViewModel : INotifyPropertyChanged
   {
      private string _selectedName;
      private string _selectedNameTwo;

      protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
      {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }

      public ObservableCollection<string> Names { get; } 
         = new ObservableCollection<string>(new List<string>
         {
            "Name1",
            "Name2",
            "Name3",
            "Name4",
            "Name5"
         });

      public ObservableCollection<string> NamesTwo { get; } = new ObservableCollection<string>(new List<string>());

      public string SelectedName
      {
         get { return _selectedName; }
         set { _selectedName = value; OnPropertyChanged(); }
      }

      public string SelectedNameTwo
      {
         get { return _selectedNameTwo; }
         set { _selectedNameTwo = value; OnPropertyChanged(); }
      }

      public ICommand MyOtherDoubleClickCommand
      {
         get
         {
            return new DelegateCommand<string>(name =>
            {
               NamesTwo.Remove(name);
               Names.Add(name);
               SelectedNameTwo = "";
            });
         }
      }

      public ICommand MyDoubleClickCommand
      {
         get
         {
            return new DelegateCommand<string>(name =>
            {
               Names.Remove(name);
               NamesTwo.Add(name);
               SelectedName = "";
            });
         }
      }

      public event PropertyChangedEventHandler PropertyChanged;
   }
}

I have used the Prism.Core package for the DelegateCommand object.我已经将 Prism.Core 包用于 DelegateCommand 对象。 This is not essential, I just did it for ease这不是必需的,我只是为了方便

You don't even need the SelectedName and SelectedNameTwo properties if they will not be used whilst processing the ViewModel.如果在处理 ViewModel 时不会使用 SelectedName 和 SelectedNameTwo 属性,您甚至不需要它们。 I included them for completeness.为了完整性,我将它们包括在内。

. .

Edited: I did not originally notice that this is for a UWP project.编辑:我最初没有注意到这是针对 UWP 项目的。 I believe the following will work -though it is untested here since I am not set up for UWP on my machine at the moment.我相信以下内容会起作用 - 尽管这里没有经过测试,因为我目前没有在我的机器上设置 UWP。 I'm not certain of the DoubleClick EventName .我不确定DoubleClick EventName

<Page xmlns:i="using:Microsoft.Xaml.Interactivity"
      xmlns:core="using:Microsoft.Xaml.Interactions.Core>
  <ListBox Grid.Column="1" Margin="10,0,0,0" ItemsSource="{Binding Path=NamesTwo, Mode=OneWay}"
           SelectedItem="{Binding Path=SelectedNameTwo}">
     <ListBox.ItemTemplate>
        <DataTemplate>
           <TextBlock Text="{Binding .}" >
              <i:Interaction.Behaviors>
                 <core:EventTriggerBehavior EventName="DoubleClick">
                    <core:InvokeCommandAction Command="{Binding Path=DataContext.MyDoubleClickCommand, 
                                              RelativeSource={RelativeSource AncestorType=Page, Mode=FindAncestor} }" 
                                              CommandParameter="{Binding .}" />
                 </core:EventTriggerBehavior>
              </i:Interaction.Behaviors>
           </TextBlock>
        </DataTemplate>
     </ListBox.ItemTemplate>
  </ListBox>

Credit to Bill, as the UWP edit pointed me toward a satisfactory solution.感谢 Bill,因为 UWP 编辑向我指出了一个令人满意的解决方案。

Firstly, I added a NuGet reference to Microsoft.Xaml.Behaviors.Uwp.Managed首先,我添加了对Microsoft.Xaml.Behaviors.Uwp.Managed的 NuGet 引用

Secondly I added the namespaces Bill mentions to the xaml in which my control is located:其次,我将 Bill 提到的命名空间添加到我的控件所在的 xaml 中:

xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:core="using:Microsoft.Xaml.Interactions.Core" xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:core="using:Microsoft.Xaml.Interactions.Core"

Then I added some XAML in my control (List View in this example):然后我在我的控件中添加了一些 XAML(本例中的列表视图):

<ListView ...>
  <i:Interaction.Behaviors>
    <core:EventTriggerBehavior EventName="DoubleTapped">
      <core:InvokeCommandAction Command="{Binding NavigateUpCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
    </core:EventTriggerBehavior>
  </i:Interaction.Behaviors>
  ...
</ListView>

In my case, this was a templated control - and the "DoubleTapped" event name was used successfully :)就我而言,这是一个模板化控件 - 并且成功使用了“DoubleTapped”事件名称:)

The Command was set up in the best way I know;指挥部是以我所知道的最好的方式建立的; made available as an ICommand get accessor on the in the control class, which used a stock "RelayCommand" implementation作为控制类中的ICommand get访问器提供,该类使用了库存的“RelayCommand”实现

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM