19장. WPF MVVM, ListBox의 컬렉션 정렬, 필터링, 탐색 실습(ListCollectionView)

19장. WPF MVVM, ListBox의 컬렉션 정렬, 필터링, 탐색 실습(ListCollectionView)

참조

목적

  • WPF MVVM, ListBox의 컬렉션 정렬, 필터링, 탐색 실습에 대해 학습합니다.

WPF, MVVM, ListBox의 컬렉션 정렬, 필터링, 탐색 실습

  • ListCollectionView 클래스는 List를 구현하는 컬렉션에 대한 컬렉션의 뷰를 나타내며 이를 통해 컬렉션의 정렬, 필터링(검색), 탐색(앞/뒤/맨앞/맨뒤) 기능을 구현할 수 있습니다.
  • 사원의 목록을 ListBox에 출력하고 상단의 버튼을 통해 정렬 기능을 구현하고 하단의 버튼을 통해 탐색, 필터링(검색) 기능을 구현해 보겠습니다.

MVVM 구조

1

  • MVVM 패턴은 (Model - ViewModel- View) 의 줄임말입니다.
  • Model은 Business Logic을 정의하고, Data Model을 정의합니다.
  • 그리고 Model은 ViewModel에게 Property 변경을 통지해야 하기 때문에 INotifyProperty 인터페이스를 상속받습니다.
  • ViewModel 은 Presentation Logic과 State를 정의합니다.
  • ViewModel은 Model에서 속성들을 알고, 데이터를 Model에게 Update 합니다.
  • View와 ViewModel은 Data Binding을 합니다.

실습

MainWindow.xaml

<Window x:Class="WPF17_Test.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:WPF17_Test"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="400">
    <Window.Resources>
        <local:EmpViewModel x:Key="emps"/>
        <DataTemplate x:Key="template">
            <Grid Width="400">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100"/>
                    <ColumnDefinition Width="150"/>
                    <ColumnDefinition Width="150"/>
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Column="0"
                           Text="{Binding Path=Empno}"/>
                <TextBlock Grid.Column ="1" 
                           Text="{Binding Path=Ename}"/>
                <TextBlock Grid.Column="2"
                           Text="{Binding Path=Job}"/>
            </Grid>
        </DataTemplate>
    </Window.Resources>

    <StackPanel x:Name="rootElement"
                DataContext="{Binding Source={StaticResource emps}}"
                DataContextChanged="rootElement_DataContextChanged">
        <Grid Width="400">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"/>
                <ColumnDefinition Width="150"/>
                <ColumnDefinition Width="150"/>
            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition Height="20"/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <TextBlock HorizontalAlignment="Center"
                       Grid.ColumnSpan="3"
                       Text="사원 리스트"/>
            <Button Grid.Row="1" Grid.Column="0"
                    Name="BtnEmpno" Content="Empno"
                    Click="OnClick"/>
            <Button Grid.Row="1" Grid.Column="1"
                    Name="BtnEname" Content="Ename"
                    Click="OnClick"/>
            <Button Grid.Row="1" Grid.Column="2"
                    Name="BtnJob" Content="Job"
                    Click="OnClick"/>
            <ListBox Grid.Row="2" Grid.ColumnSpan="3" Name="empListBox"
                     ItemsSource="{Binding Source={StaticResource emps}}"
                     ItemTemplate="{StaticResource template}"
                     IsSynchronizedWithCurrentItem="True"
                     ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>

            <TextBlock Foreground="Blue" Grid.Row="3" Grid.ColumnSpan="3"
                       Text="이전/이후/데이터필터링"/>
            <Button Grid.Row="4" Grid.Column="0"
                    Content="Previous"
                    x:Name="Previous"
                    Click="OnMove"/>
            <Button Grid.Row ="4" Grid.Column="1"
                    Content="Next"
                    x:Name="Next"
                    Click="OnMove"/>
            <Button Grid.Row="4" Grid.Column="2"
                    Content="Show only Manager"
                    Click="OnFilter"/>

            <TextBlock Grid.Row="5" Grid.Column="0"
                       Name="TblkEmpno"
                       Text="{Binding Path=Empno}"/>
            <TextBlock Grid.Row="5" Grid.Column="1"
                       Name="TblkEname"
                       Text="{Binding Path=Ename}"/>
            <TextBlock Grid.Row="5" Grid.Column="2"
                       Name="TblkJob"
                       Text="{Binding Path=Job}"/>
        </Grid>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPF17_Test
{
    /// <summary>
    /// MainWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainWindow : Window
    {
        //컬렉션의 정렬, 필터링, 탐색 기능을 구현 가능하도록 지원
        public ListCollectionView MycollectionView;
        public Emp emp;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void rootElement_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            //StackPanel의 DataContext로 지정된 emps 컬렉션을 소스로 해서 ListCollectionView 생성
            //이를 이용하여 정렬, 탐색, 필터링 기능 등을 구현한다.
            MycollectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(rootElement.DataContext);
        }

        /// <summary>
        /// ListBox 상단의 정렬 기능 처리
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnClick(object sender, RoutedEventArgs e)
        {
            var b = sender as Button;

            MycollectionView.SortDescriptions.Clear();

            switch (b.Name)
            {
                case "BtnEmpno":
                    MycollectionView.SortDescriptions.Add(new System.ComponentModel.SortDescription
                        ("Empno", System.ComponentModel.ListSortDirection.Ascending));
                    break;
                case "BtnEname":
                    MycollectionView.SortDescriptions.Add(new System.ComponentModel.SortDescription
                        ("Ename", System.ComponentModel.ListSortDirection.Ascending));
                    break;
                case "BtnJob":
                    MycollectionView.SortDescriptions.Add(new System.ComponentModel.SortDescription
                        ("Job", System.ComponentModel.ListSortDirection.Ascending));
                    break;
            }
        }

        private void OnMove(object sender, RoutedEventArgs e)
        {
            var b = sender as Button;

            switch (b.Name)
            {
                case "Previous":
                    if (MycollectionView.MoveCurrentToPrevious())
                        emp = MycollectionView.CurrentAddItem as Emp;
                    else
                        MycollectionView.MoveCurrentToFirst();
                    break;
                case "Next":
                    if (MycollectionView.MoveCurrentToNext())
                        emp = MycollectionView.CurrentAddItem as Emp;
                    else
                        MycollectionView.MoveCurrentToLast();
                    break;
            }
        }

        //필터링 기능, 관리자만 또는 관리자가 아닌 사원 리스트 출력
        private void OnFilter(object sender, RoutedEventArgs e)
        {
            //토글 기능 구현
            switch (MycollectionView.Filter)
            {
                //Filter 델리게이트는 보여줄 데이터인지 아닌지 판단할 수 있는 메서드 참조
                case null: MycollectionView.Filter = IsManager; break; //관리자만
                default: MycollectionView.Filter = null; break; //전체사원
            }
        }

        private bool IsManager(object o)
        {
            var e = o as Emp;
            return e?.Job == "Manager";
        }
    }
}

Emp.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WPF17_Test
{
    /// <summary>
    /// Model Class
    /// </summary>
    public class Emp : INotifyPropertyChanged
    {
        private int _empno;
        private string _ename;
        private string _job;

        public event PropertyChangedEventHandler PropertyChanged;

        public int Empno
        {
            get
            {
                return _empno;
            }
            set
            {
                _empno = value;
                OnPropertyChanged("Empno");
            }
        }

        public string Ename
        {
            get
            {
                return _ename;
            }
            set
            {
                _ename = value;
                OnPropertyChanged("Ename");
            }
        }

        public string Job
        {
            get
            {
                return _job;
            }
            set
            {
                _job = value;
                OnPropertyChanged("Job");
            }
        }

        public void OnPropertyChanged(string PName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PName));
        }
    }
}

EmpViewModel.cs

using System.Collections.ObjectModel;

namespace WPF17_Test
{
    public class EmpViewModel : ObservableCollection<Emp>
    {
        public EmpViewModel()
        {
            Add(new Emp() { Empno = 1, Ename = "김길동", Job = "Salesman" });
            Add(new Emp() { Empno = 2, Ename = "박길동", Job = "Clerk" });
            Add(new Emp() { Empno = 3, Ename = "정길동", Job = "Clerk" });
            Add(new Emp() { Empno = 4, Ename = "남길동", Job = "Clerk" });
            Add(new Emp() { Empno = 5, Ename = "황길동", Job = "Salesman" });
            Add(new Emp() { Empno = 6, Ename = "홍길동", Job = "Manager" });
        }
    }
}

실행 결과

2

728x90

이 글을 공유하기

댓글

Designed by JB FACTORY