2012-04-18 17 views
43

Estoy usando un control WPF ListView que muestra una lista de elementos de datos.Lista de WPF - detectar cuando se hace clic en el elemento seleccionado

<ListView ItemsSource={Binding MyItems}> 
    <ListView.View> 
     <GridView> 
      <!-- declare a GridViewColumn for each property --> 
     </GridView> 
    </ListView.View> 
</ListView> 

estoy tratando de obtener un comportamiento similar al evento ListView.SelectionChanged, solamente quiero para detectar también si se hace clic en el elemento seleccionado en ese momento. El evento SelectionChanged no se activa si se hace clic nuevamente en el mismo elemento (obviamente).

¿Cuál sería la mejor (más limpia) forma de abordar esto?

Respuesta

50

Utilice la propiedad ListView.ItemContainerStyle para dar a su ListViewItems un EventSetter que manejará el evento PreviewMouseLeftButtonDown. Luego, en el controlador, verifique si el elemento en el que se hizo clic está seleccionado.

XAML:

<ListView ItemsSource={Binding MyItems}> 
    <ListView.View> 
     <GridView> 
      <!-- declare a GridViewColumn for each property --> 
     </GridView> 
    </ListView.View> 
    <ListView.ItemContainerStyle> 
     <Style TargetType="ListViewItem"> 
      <EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" /> 
     </Style> 
    </ListView.ItemContainerStyle> 
</ListView> 

de código subyacente:

private void ListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
{ 
    var item = sender as ListViewItem; 
    if (item != null && item.IsSelected) 
    { 
     //Do your stuff 
    } 
} 
+8

En realidad, se puede establecer el controlador directamente en el 'ListView', no hay necesidad de un EventSetter. –

16

Puede manejar evento PreviewMouseLeftButtonUp del ListView. El motivo por el que no se gestiona el evento PreviewMouseLeftButtonDown es que, en el momento en que maneja el evento, el elemento Selected de ListView puede seguir siendo nulo.

XAML:

<ListView ... PreviewMouseLeftButtonUp="listView_Click"> ... 

Código atrás:

private void listView_Click(object sender, RoutedEventArgs e) 
{ 
    var item = (sender as ListView).SelectedItem; 
    if (item != null) 
    { 
     ... 
    } 
} 
+0

Técnicamente, esta es una respuesta correcta, pero yo no haría esto. Yo recomendaría no usar código detrás. No voté, pero recomendaría establecer esto en su modelo de vista. – Rogala

+4

Esta no es una respuesta correcta porque el usuario puede hacer clic en el espacio vacío en el ListView y si hay un elemento actualmente seleccionado, la acción se activará como si el usuario hiciera clic en el elemento seleccionado. –

3

Puede manejar haga clic en la lista de vista de un elemento como este:

<ListView.ItemTemplate> 
    <DataTemplate> 
    <Button BorderBrush="Transparent" Background="Transparent" Focusable="False"> 
     <i:Interaction.Triggers> 
       <i:EventTrigger EventName="Click"> 
        <i:InvokeCommandAction Command="{Binding DataContext.MyCommand, ElementName=ListViewName}" CommandParameter="{Binding}"/> 
       </i:EventTrigger> 
     </i:Interaction.Triggers> 
     <Button.Template> 
     <ControlTemplate> 
     <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> 
    ... 
+0

Su respuesta es la mejor de todas, pero ampliaría el modelo de vista al que está vinculado, ya que las respuestas anteriores mencionan el uso del código. +1 – Rogala

11

Estas son todas buenas sugerencias, pero si Estabas tú, yo haría esto en tu modelo de vista. Dentro de su modelo de vista, puede crear un comando de retransmisión que luego puede vincular al evento de clic en su plantilla de elemento. Para determinar si se seleccionó el mismo elemento, puede almacenar una referencia al elemento seleccionado en su modelo de vista. Me gusta usar MVVM Light para manejar el enlace. Esto hace que su proyecto sea mucho más fácil de modificar en el futuro, y le permite configurar el enlace en Blend.

Cuando todo está dicho y hecho, su XAML se verá como lo sugirió Sergey. Evitaría usar el código en tu vista. Voy a evitar escribir código en esta respuesta, porque hay un montón de ejemplos por ahí.

Aquí está uno: How to use RelayCommand with the MVVM Light framework

Si necesita un ejemplo, por favor, comentar, y voy a agregar uno.

~ Saludos

dije que no iba a hacer un ejemplo, pero lo estoy. Aqui tienes.

1) En su proyecto, agregue solo las bibliotecas de luces MVVM.

2) Cree una clase para su vista. En general, tiene un modelo de vista para cada vista (ver: MainWindow.xaml & & viewModel: MainWindowViewModel.cs)

3) Aquí está el código para el modelo de vista muy, muy, muy básico:

Todo espacio de nombre incluido (si es que aparecen aquí, estoy suponiendo que ya ha añadido la referencia a ellos MVVM Light. está en Nuget)

using GalaSoft.MvvmLight; 
using GalaSoft.MvvmLight.CommandWpf; 
using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

Ahora añadir una clase pública básica:

/// <summary> 
/// Very basic model for example 
/// </summary> 
public class BasicModel 
{ 
    public string Id { get; set; } 
    public string Text { get; set; } 

    /// <summary> 
    /// Constructor 
    /// </summary> 
    /// <param name="text"></param> 
    public BasicModel(string text) 
    { 
     this.Id = Guid.NewGuid().ToString(); 
     this.Text = text; 
    } 
} 

Ahora crear su modelo de vista:

public class MainWindowViewModel : ViewModelBase 
{ 
    public MainWindowViewModel() 
    { 
     ModelsCollection = new ObservableCollection<BasicModel>(new List<BasicModel>() { 
      new BasicModel("Model one") 
      , new BasicModel("Model two") 
      , new BasicModel("Model three") 
     }); 
    } 

    private BasicModel _selectedBasicModel; 

    /// <summary> 
    /// Stores the selected mode. 
    /// </summary> 
    /// <remarks>This is just an example, may be different.</remarks> 
    public BasicModel SelectedBasicModel 
    { 
     get { return _selectedBasicModel; } 
     set { Set(() => SelectedBasicModel, ref _selectedBasicModel, value); } 
    } 

    private ObservableCollection<BasicModel> _modelsCollection; 

    /// <summary> 
    /// List to bind to 
    /// </summary> 
    public ObservableCollection<BasicModel> ModelsCollection 
    { 
     get { return _modelsCollection; } 
     set { Set(() => ModelsCollection, ref _modelsCollection, value); } 
    }   
} 

En su viewmodel, agregue un comando de retransmisión. Tenga en cuenta que hice este asincrónico y lo hice pasar un parámetro.

private RelayCommand<string> _selectItemRelayCommand; 
    /// <summary> 
    /// Relay command associated with the selection of an item in the observablecollection 
    /// </summary> 
    public RelayCommand<string> SelectItemRelayCommand 
    { 
     get 
     { 
      if (_selectItemRelayCommand == null) 
      { 
       _selectItemRelayCommand = new RelayCommand<string>(async (id) => 
       { 
        await selectItem(id); 
       }); 
      } 

      return _selectItemRelayCommand; 
     } 
     set { _selectItemRelayCommand = value; } 
    } 

    /// <summary> 
    /// I went with async in case you sub is a long task, and you don't want to lock you UI 
    /// </summary> 
    /// <returns></returns> 
    private async Task<int> selectItem(string id) 
    { 
     this.SelectedBasicModel = ModelsCollection.FirstOrDefault(x => x.Id == id); 
     Console.WriteLine(String.Concat("You just clicked:", SelectedBasicModel.Text)); 
     //Do async work 

     return await Task.FromResult(1); 
    } 

En el código subyacente para usted ver, crear una propiedad para usted modelo de vista y establecer el DataContext para su visión para el modelo de vista (tenga en cuenta, hay otras maneras de hacer esto, pero estoy tratando de hacer esto un ejemplo sencillo.)

public partial class MainWindow : Window 
{ 
    public MainWindowViewModel MyViewModel { get; set; } 
    public MainWindow() 
    { 
     InitializeComponent(); 

     MyViewModel = new MainWindowViewModel(); 
     this.DataContext = MyViewModel; 
    } 
} 

En el XAML, es necesario agregar algunos espacios de nombres a la parte superior de su código

<Window x:Class="Basic_Binding.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    xmlns:Custom="clr-namespace:GalaSoft.MvvmLight;assembly=GalaSoft.MvvmLight" 
    Title="MainWindow" Height="350" Width="525"> 

añadí "i" y "Custom".

Aquí es el ListView:

<ListView 
     Grid.Row="0" 
     Grid.Column="0" 
     HorizontalContentAlignment="Stretch" 
     ItemsSource="{Binding ModelsCollection}" 
     ItemTemplate="{DynamicResource BasicModelDataTemplate}"> 
    </ListView> 

Aquí es ItemTemplate para el ListView:

<DataTemplate x:Key="BasicModelDataTemplate"> 
     <Grid> 
      <TextBlock Text="{Binding Text}"> 
       <i:Interaction.Triggers> 
        <i:EventTrigger EventName="MouseLeftButtonUp"> 
         <i:InvokeCommandAction 
          Command="{Binding DataContext.SelectItemRelayCommand, 
           RelativeSource={RelativeSource FindAncestor, 
             AncestorType={x:Type ItemsControl}}}" 
          CommandParameter="{Binding Id}">         
         </i:InvokeCommandAction> 
        </i:EventTrigger> 
       </i:Interaction.Triggers> 
      </TextBlock> 
     </Grid> 
    </DataTemplate> 

ejecutar su aplicación, y echa un vistazo a la ventana de salida. Puede usar un convertidor para manejar el estilo del elemento seleccionado.

Esto puede parecer muy complicado, pero hace la vida mucho más fácil en el futuro cuando necesita separar su vista de su ViewModel (por ejemplo, desarrollar un ViewModel para múltiples plataformas). Además, hace que trabajar en Blend 10x sea más fácil. Una vez que desarrolle su ViewModel, puede entregarlo a un diseñador que puede hacer que se vea muy artístico :). MVVM Light agrega algunas funcionalidades para que Blend reconozca su ViewModel. En su mayor parte, puede hacer prácticamente todo lo que desee en ViewModel para afectar la vista.

Si alguien lee esto, espero que encuentre esto útil. Si tiene preguntas, hágamelo saber. Usé MVVM Light en este ejemplo, pero podrías hacer esto sin MVVM Light.

~ Saludos

Cuestiones relacionadas