Programing

WPF ListView-선택한 항목을 클릭 할 때 감지

lottogame 2021. 1. 7. 07:32
반응형

WPF ListView-선택한 항목을 클릭 할 때 감지


데이터 바인딩 된 항목 목록을 표시하는 WPF ListView 컨트롤을 사용하고 있습니다.

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

ListView.SelectionChanged이벤트 와 유사한 동작을 얻으려고 노력하고 있지만 현재 선택한 항목이 클릭되었는지도 감지하고 싶습니다. SelectionChanged같은 항목이 (명백하게)를 다시 클릭하면 이벤트가 발생하지 않습니다.

이것에 접근하는 가장 좋은 (가장 깨끗한) 방법은 무엇입니까?


ListView.ItemContainerStyle 속성을 사용하여 ListViewItems에 PreviewMouseLeftButtonDown 이벤트를 처리 할 EventSetter를 제공합니다. 그런 다음 핸들러에서 클릭 한 항목이 선택되었는지 확인합니다.

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>

코드 숨김 :

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

ListView의 PreviewMouseLeftButtonUp 이벤트를 처리 할 수 ​​있습니다. PreviewMouseLeftButtonDown 이벤트를 처리하지 않는 이유는 이벤트를 처리 할 때까지 ListView의 SelectedItem이 여전히 null 일 수 있기 때문입니다.

XAML :

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

뒤에있는 코드 :

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

이것들은 모두 훌륭한 제안이지만 내가 당신이라면 뷰 모델에서 이것을 할 것입니다. 보기 모델 내에서 항목 템플릿의 클릭 이벤트에 바인딩 할 수있는 릴레이 명령을 만들 수 있습니다. 동일한 항목이 선택되었는지 확인하기 위해 선택한 항목에 대한 참조를 뷰 모델에 저장할 수 있습니다. 저는 MVVM Light를 사용하여 바인딩을 처리하고 싶습니다. 이렇게하면 나중에 프로젝트를 훨씬 쉽게 수정할 수 있으며 Blend에서 바인딩을 설정할 수 있습니다.

모든 것을 말하고 완료하면 XAML이 Sergey가 제안한 것처럼 보일 것입니다. 나는 당신의 관점에서 코드를 사용하지 않을 것입니다. 많은 예제가 있기 때문에이 답변에 코드 작성을 피할 것입니다.

다음은 MVVM Light 프레임 워크에서 RelayCommand를 사용하는 방법입니다 .

예가 필요하시면 의견을 남겨 주시면 추가하겠습니다.

~ 건배

나는 예시를하지 않겠다고 말했지만 나는 그렇다. 여기 있습니다.

1) 프로젝트에서 MVVM Light Libraries Only를 추가합니다.

2)보기에 대한 클래스를 만듭니다. 일반적으로 각 뷰에 대한 뷰 모델이 있습니다 (view : MainWindow.xaml && viewModel : MainWindowViewModel.cs).

3) 매우 기본적인 뷰 모델의 코드는 다음과 같습니다.

포함 된 모든 네임 스페이스 (여기에 표시되면 이미 참조를 추가했다고 가정합니다. MVVM Light는 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;

이제 기본 공개 클래스를 추가하십시오.

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

이제 뷰 모델을 만듭니다.

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

뷰 모델에서 릴레이 명령을 추가하십시오. 나는 이것을 비동기로 만들고 매개 변수를 전달했습니다.

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

보기 코드에서 viewmodel에 대한 속성을 만들고보기에 대한 데이터 컨텍스트를 viewmodel로 설정합니다 (이 작업을 수행하는 다른 방법이 있지만 간단한 예제로 만들려고합니다.)

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

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

XAML에서 코드 맨 위에 네임 스페이스를 추가해야합니다.

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

"i"와 "Custom"을 추가했습니다.

다음은 ListView입니다.

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

다음은 ListView의 ItemTemplate입니다.

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

애플리케이션을 실행하고 출력 창을 확인하십시오. 변환기를 사용하여 선택한 항목의 스타일을 처리 할 수 ​​있습니다.

This may seem really complicated, but it makes life a lot easier down the road when you need to separate your view from your ViewModel (e.g. develop a ViewModel for multiple platforms.) Additionally, it makes working in Blend 10x easier. Once you develop your ViewModel, you can hand it over to a designer who can make it look very artsy :). MVVM Light adds some functionality to make Blend recognize your ViewModel. For the most part, you can do just about everything you want to in the ViewModel to affect the view.

If anyone reads this, I hope you find this helpful. If you have questions, please let me know. I used MVVM Light in this example, but you could do this without MVVM Light.

~Cheers


You can handle click on list view item like this:

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

I would also suggest deselecting an item after it has been clicked and use the MouseDoubleClick event

private void listBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    try {
        //Do your stuff here
        listBox.SelectedItem = null;
        listBox.SelectedIndex = -1;
    } catch (Exception ex) {
        System.Diagnostics.Debug.WriteLine(ex.Message);
    }
}

I couldn't get the accepted answer to work the way I wanted it to (see Farrukh's comment).

I came up with a slightly different solution which also feels more native because it selects the item on mouse button down and then you're able to react to it when the mouse button gets released:

XAML:

<ListView Name="MyListView" ItemsSource={Binding MyItems}>
<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" />
        <EventSetter Event="PreviewMouseLeftButtonUp" Handler="ListViewItem_PreviewMouseLeftButtonUp" />
    </Style>
</ListView.ItemContainerStyle>

Code behind:

private void ListViewItem_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    MyListView.SelectedItems.Clear();

    ListViewItem item = sender as ListViewItem;
    if (item != null)
    {
        item.IsSelected = true;
        MyListView.SelectedItem = item;
    }
}

private void ListViewItem_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    ListViewItem item = sender as ListViewItem;
    if (item != null && item.IsSelected)
    {
        // do stuff
    }
}

This worked for me.

Single-clicking a row triggers the code-behind.

XAML:

<ListView x:Name="MyListView" MouseLeftButtonUp="MyListView_MouseLeftButtonUp">
    <GridView>
        <!-- Declare GridViewColumns. -->
    </GridView>
</ListView.View>

Code-behind:

private void MyListView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    System.Windows.Controls.ListView list = (System.Windows.Controls.ListView)sender;
    MyClass selectedObject = (MyClass)list.SelectedItem;
    // Do stuff with the selectedObject.
}

ReferenceURL : https://stackoverflow.com/questions/10207888/wpf-listview-detect-when-selected-item-is-clicked

반응형

'Programing' 카테고리의 다른 글

R : 모든 경고를 지우는 방법  (0) 2021.01.07
jsfiddle에서 무한 루프 실행 취소  (0) 2021.01.07
Eclipse Juno를 사용한 Maven  (0) 2021.01.07
IPython 노트북 간의 링크  (0) 2021.01.07
crontab을 파일로 내보내기  (0) 2021.01.07