2012-04-18 16 views
43

Używam formant ListView WPF, który wyświetla listę z danymi przedmiotów.WPF ListView - wykrywa, gdy wybrana pozycja kliknięciu

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

Próbuję uzyskać zachowanie podobne do zdarzenia ListView.SelectionChanged, tylko chcę również wykryć, czy aktualnie wybrany element jest kliknięty. Impreza SelectionChanged nie ognia, jeśli ten sam element jest kliknięty ponownie (oczywiście).

Jaki byłby najlepszy (najczystszym) sposobem podejścia to?

Odpowiedz

50

Użyj właściwości ListView.ItemContainerStyle dać wasze ListViewItems się EventSetter która będzie obsługiwać zdarzenie PreviewMouseLeftButtonDown. Następnie w module obsługi sprawdź, czy wybrano element, który został kliknięty.

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> 

Code-tył:

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

Właściwie można ustawić obsługi bezpośrednio na 'ListView', nie istnieje potrzeba EventSetter. –

16

można obsługiwać zdarzenie ListView za PreviewMouseLeftButtonUp. Powodem nie obsłużyć zdarzenia PreviewMouseLeftButtonDown jest to, że w momencie, gdy obsłużyć zdarzenia, SelectedItem ListView mogą nadal być null.

XAML:

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

Kod za:

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

Technicznie, to jest poprawna odpowiedź, ale nie zrobiłbym tego. Polecam nie używać kodu z tyłu. Nie głosowałem, ale polecam ustawienie tego w twoim modelu widoku. – Rogala

+4

To nie jest poprawna odpowiedź, ponieważ użytkownik może kliknąć puste miejsce w ListView i jeśli jest aktualnie wybrany element, akcja zostanie uruchomiona tak, jakby użytkownik kliknął wybrany element. –

3

można obsłużyć kliknij na widoku listy pozycji jak ta:

<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

Twoja odpowiedź jest najlepsza ze wszystkich odpowiedzi, ale chciałbym rozwinąć twój model widoku, do którego jesteś zobowiązany, ponieważ poprzednie odpowiedzi wspominają o używaniu kodu. +1 – Rogala

11

Są to wszystkie wielkie sugestii, ale jeśli byłbyś, zrobiłbym to w twoim modelu widzenia. W modelu widoku można utworzyć polecenie przekazywania, które następnie można powiązać z zdarzeniem kliknięcia w szablonie elementu. Aby określić, czy ten sam element został wybrany, możesz zapisać odniesienie do wybranego elementu w swoim modelu widoku. Lubię używać MVVM Light do obsługi wiązania. To sprawia, że ​​twój projekt jest znacznie łatwiejszy do modyfikowania w przyszłości i umożliwia ustawienie wiązania w Blend.

Kiedy wszystko jest powiedziane i zrobione, twój XAML będzie wyglądać jak sugerował Sergey. Unikałbym używania kodu w twoim widoku. W tej odpowiedzi zamierzam unikać pisania kodu, ponieważ jest tam mnóstwo przykładów.

Oto jeden: How to use RelayCommand with the MVVM Light framework

Jeśli wymagają przykład, prosimy o komentarz, a dodam jeden.

~ Cheers

Powiedziałem, że nie zamierza zrobić przykład, ale jestem. Proszę bardzo.

1) Dodaj do swojego projektu tylko MVVM Light Libraries.

2) Utwórz klasę dla widoku. Ogólnie rzecz biorąc, masz model widoku dla każdego widoku (widok: MainWindow.xaml & & viewModel: MainWindowViewModel.cs)

3) Oto kod na bardzo, bardzo, bardzo podstawowy model Widok:

Wszystko wliczone nazw (jeśli pojawi się tutaj, jestem zakładając, że już dodane odniesienie do nich MVVM światła. jest w 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; 

teraz dodaj podstawową klasę publiczną:

/// <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; 
    } 
} 

teraz utworzyć ViewModel:

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

W swoim widokumodułu dodaj ponowny odnośnik. Zauważ, że zrobiłem to asynchronicznie i kazałem mu przekazać parametr.

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

W kodzie tyłu dla przeglądać, tworzyć właściwość Ci ViewModel i ustawić datacontext dla swojego celu viewmodel (proszę pamiętać, że istnieją inne sposoby, aby to zrobić, ale staram się robić to prosty przykład.)

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

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

W swojej XAML, trzeba dodać kilka nazw w górnej części kodu

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

dodałem "i" i "Custom".

Oto ListView:

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

Oto ItemTemplate dla 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> 

Uruchom aplikację i sprawdź okno wyjściowe. Możesz użyć konwertera do obsługi stylizacji wybranego elementu.

To może wydawać się bardzo skomplikowane, ale znacznie ułatwia życie w czasie, gdy trzeba oddzielić widok od ViewModel (np. Stworzyć ViewModel dla wielu platform.) Dodatkowo ułatwia pracę w Blend 10x. Kiedy już stworzysz swój ViewModel, możesz przekazać go projektantowi, który może sprawić, że będzie wyglądać bardzo artystycznie :). MVVM Light dodaje trochę funkcjonalności, aby Blend rozpoznawał twój ViewModel. W większości można zrobić prawie wszystko, co chcesz w ViewModel, aby wpłynąć na widok.

Jeśli ktokolwiek to przeczyta, mam nadzieję, że ci się to przyda. Jeśli masz pytania, daj mi znać. W tym przykładzie użyłem MVVM Light, ale można to zrobić bez MVVM Light.

~ Cheers

Powiązane problemy