programing

WPF List View - 선택한 항목을 클릭했을 때 감지

telecom 2023. 4. 20. 20:03
반응형

WPF List View - 선택한 항목을 클릭했을 때 감지

데이터 사운드 항목의 목록을 표시하는 WPF ListView 컨트롤을 사용하고 있습니다.

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

난 지금 이 일과 비슷한 행동을 취하려고 하고 있어.ListView.SelectionChanged이벤트, 현재 선택한 항목이 클릭되었는지 여부도 감지합니다.SelectionChanged동일한 항목을 다시 클릭하면 이벤트가 실행되지 않습니다.

이를 위한 최선의 (가장 깨끗한) 방법은 무엇일까요?

ListView를 사용합니다.ListView를 제공하는 ItemContainerStyle 속성미리보기를 처리하는 EventSetter 항목Mouse Left Button Down 이벤트.그런 다음 핸들러에서 누른 항목이 선택되었는지 확인합니다.

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

List View의 미리보기를 처리할 수 있습니다.Mouse Left Button Up 이벤트.미리보기를 처리하지 않는 이유Mouse Left Button Down 이벤트는 이벤트를 처리할 때 List View의 Selected가항목이 아직 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를 사용하는 것을 좋아합니다.이렇게 하면 나중에 프로젝트를 훨씬 쉽게 수정할 수 있으며 혼합에서 바인딩을 설정할 수 있습니다.

모든 것을 종합하면 XAML은 세르게이가 제안한 것과 같습니다.저는 당신의 시야에서 뒤에 있는 코드를 사용하지 않을 것입니다.이 답변에는 코드를 쓰는 것을 피하겠습니다.여러 가지 예가 있기 때문입니다.

다음 중 하나가 있습니다.MVVM Light 프레임워크에서 RelayCommand를 사용하는 방법

예시가 필요하시면 코멘트를 주시면 추가하겠습니다.

~응원하다

제가 예를 안 든다고 했는데, 하고 있어요.여기 있어요.

  1. 프로젝트에서 MVVM 라이트 라이브러리만 추가합니다.

  2. 뷰에 대한 클래스를 만듭니다.일반적으로 각 뷰의 뷰 모델이 있습니다(뷰: Main Window.xaml 및 뷰 모델: 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);
    }

뒤에 보이는 코드에서는 뷰모델의 속성을 만들고 뷰모델의 데이터콘텍스트를 뷰모델로 설정합니다(다른 방법이 있습니다만, 간단한 예를 들 수 있도록 하겠습니다.

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"을 추가했습니다.

List View는 다음과 같습니다.

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

응용 프로그램을 실행하고 출력 창을 확인합니다.컨버터를 사용하여 선택한 항목의 스타일을 처리할 수 있습니다.

매우 복잡해 보이지만 View Model과 View Model을 분리해야 하는 경우(예: 여러 플랫폼용 View Model 개발)에는 향후 생활이 훨씬 쉬워집니다.또한 Blend 10x에서의 작업도 쉬워집니다.View Model을 개발하면 디자이너에게 넘길 수 있습니다.디자이너는 View Model을 매우 예술적으로 보이게 할 수 있습니다:).MVVM Light는 Blend가 ViewModel을 인식할 수 있도록 몇 가지 기능을 추가합니다.대부분의 경우 ViewModel에서 원하는 모든 작업을 수행하여 보기에 영향을 줄 수 있습니다.

이 글을 읽는 사람이 있다면 도움이 되길 바랍니다.궁금하신 점이 있으시면 알려주세요.이 예에서는 MVVM Light를 사용했지만 MVVM Light 없이도 이 작업을 수행할 수 있습니다.

다음과 같이 목록 보기 항목을 클릭할 수 있습니다.

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

이건 나한테 효과가 있었어.

행을 한 번 클릭하면 코드 배후에 트리거됩니다.

XAML:

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

코드 비하인드:

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

나는 내가 원하는 대로 작동하도록 받아들여진 답변을 얻을 수 없었다(Farrukh의 코멘트 참조).

마우스 버튼에서 항목을 선택하고 마우스 버튼을 놓았을 때 반응할 수 있기 때문에 좀 더 네이티브한 느낌이 드는 솔루션을 생각해냈습니다.

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>

코드 배면:

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

또한 항목을 클릭한 후 선택을 취소하고 Mouse Double Click 이벤트를 사용하는 것이 좋습니다.

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

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

반응형