CollectionViewSource Class를 이용해 정렬하기

MVVM 패턴 적용 중에 ViewModel에 현재 View ListBox에 바인딩 되어있는 Collection개체의 현재 선택된 항목을 이동시키거나 원본 Collection 개체를 수정하지 않고 View에만 정렬시켜 보여주고 싶을 때에는 CollectionViewSource Class를 사용하면 유용합니다. (정렬, 그룹화, 필터링 제공) 아래의 두 기능을 구현한 프로젝트입니다.

1.     SelectedIndex를 바인딩하지 않고 CollectionViewSource를 이용해 선택항목 이동 기능

2.     속성을 기준으로한 정렬 기능

예제 프로젝트에는 GalaSoftGalaSoft.MvvmLight툴을 이용하였습니다.

완성된 프로그램입니다. 선택된 나이와 이름을 표시하고, 이전,다음 버튼은 선택한 Item을 변경합니다. 정렬은 나이 및 이름으로 오름차순 정렬합니다.

 

                                                                                  

 

 

우선 Model입니다. 간단하게 이름과 나이로 Person Class를 구성하였습니다.

 

public class Person

    {

        private int _age;

        /// <summary>

        /// 나이를 가져옵니다.

        /// </summary>

        public int Age

        {

            get { return _age; }

            private set { _age = value; }

        }

 

        private string _name;

        /// <summary>

        /// 이름을 가져옵니다.

        /// </summary>

        public string Name

        {

            get { return _name; }

            private set { _name = value; }

        }

 

        public Person(int age, string name)

        {

            this.Age = age;

            this.Name = name;

        }

    }

 

 

다음은 ViewModel 입니다. ICommand 인터페이스를 이용해 버튼들의 Command를 바인딩하였습니다. 세부 내용은 주석을 참고해주세요.

 

 public class MainViewModel : ViewModelBase

    {

        private CollectionViewSource _personCollection;

        /// <summary>

        /// Person CollectionViewSource

        /// </summary>

        public CollectionViewSource PersonCollection

        {

            get { return _personCollection; }

            set { _personCollection = value; }

        }

 

        private RelayCommand<bool> _cmdChangeSelectedItem;

        /// <summary>

        /// 현재 선택된 아이템을 변경합니다.

        /// </summary>

        public RelayCommand<bool> CmdChangeSelectedItem

        {

            get

            {

                if (_cmdChangeSelectedItem == null)

                    _cmdChangeSelectedItem = new RelayCommand<bool>(ChangeSelectedItem, CanChangeSelectedItem);

               

                       

                return _cmdChangeSelectedItem;

            }

        }

 

        private RelayCommand<string> _cmdSortName;

 

        /// <summary>

        /// 오름차순 정렬

        /// </summary>

        public RelayCommand<string> CmdSortAsc

        {

            get

            {

                if (_cmdSortName == null)

                    _cmdSortName = new RelayCommand<string>(SortAsc);

                       

                return _cmdSortName;

            }

        }

 

 

        /// <summary>

        /// Initializes a new instance of the MainViewModel class.

        /// </summary>

        public MainViewModel()

        {

            if (IsInDesignMode)

            {

                // Code runs in Blend --> create design time data.

            }

            else

            {

                // Code runs "for real"

            }

            this.PersonCollection = new CollectionViewSource();

 

            List<Person>  persons = new List<Person>()

            {

                new Person(13, "김가나"),

                new Person(12, "감가나"),

                new Person(25, "하이노"),

                new Person(55, "김동휴"),

                new Person(15, "감강찬"),

                new Person(14, "백설왕"),

                new Person(95, "배고파"),

                new Person(35, "이삼사"),

            };

            this.PersonCollection.Source = persons;

        }

 

        /// <summary>

        /// 현재 선택한 Item 변경합니다.

        /// </summary>

        /// <param name="isNext">true=다음, false=이전</param>

        private void ChangeSelectedItem(bool isNext)

        {

            if (isNext)

            {

                this.PersonCollection.View.MoveCurrentToNext();

            }

            else

            {

                this.PersonCollection.View.MoveCurrentToPrevious();

            }

        }

 

        /// <summary>

        /// 현재 선택한 Item 변경할수있는지 검사합니다.

        /// </summary>

        /// <param name="isNext"></param>

        /// <returns></returns>

        private bool CanChangeSelectedItem(bool isNext)

        {

            //현재 컬렉션의 마지막 Index 가져온다

            int viewLastIdx = this.PersonCollection.View.Cast<object>().Count() - 1;

 

            //다음으로 이동가능한지 체크

            if (isNext && this.PersonCollection.View.CurrentPosition == viewLastIdx)

            {

                return false;

            }

            else if (isNext == false && this.PersonCollection.View.CurrentPosition == 0)

            {

                //이전으로 이동가능한지 체크

                return false;

            }

 

            return true;

        }

 

        /// <summary>

        /// 오름차순으로 정렬합니다.

        /// </summary>

        /// <param name="proName">속성명</param>

        private void SortAsc(string proName)

        {

            this.PersonCollection.View.SortDescriptions.Clear();

            this.PersonCollection.View.SortDescriptions.Add(new System.ComponentModel.SortDescription(proName, System.ComponentModel.ListSortDirection.Ascending));

        }

        ////public override void Cleanup()

        ////{

        ////    // Clean up if needed

 

        ////    base.Cleanup();

        ////}

    }

 

 

 

마지막으로 View Xaml코드입니다.

ListBox의 ItemSource는 ViewModel의 PersonCollection 개체의 View속성에 바인딩하였습니다.

 

    

<Grid x:Name="LayoutRoot">

        <ListBox ItemsSource="{Binding PersonCollection.View}" Margin="0,0,0,119" SelectionMode="Single">

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <StackPanel Orientation="Horizontal">

                        <TextBlock Text="{Binding Age}"/>

                        <TextBlock Text="{Binding Name}" Margin="50,0,0,0"/>

                    </StackPanel>

                </DataTemplate>

            </ListBox.ItemTemplate>

        </ListBox>

        <TextBlock DataContext="{Binding PersonCollection.View}" Height="23" HorizontalAlignment="Left" Margin="12,148,0,0"        Text="{Binding Age}" VerticalAlignment="Top" />

        <TextBlock DataContext="{Binding PersonCollection.View}"  Height="23" HorizontalAlignment="Left" Margin="91,148,0,0" Text="{Binding Name}" VerticalAlignment="Top" />

        <Button Command="{Binding CmdChangeSelectedItem}" CommandParameter="{StaticResource False}" Content="이전" Height="23" HorizontalAlignment="Left" Margin="12,177,0,0" VerticalAlignment="Top" Width="40"/>

        <Button Command="{Binding CmdChangeSelectedItem}" CommandParameter="{StaticResource True}" Content="다음" Height="23" HorizontalAlignment="Left" Margin="58,177,0,0" VerticalAlignment="Top" Width="40" />

        <Button Command="{Binding CmdSortAsc}" CommandParameter="Age"  Content="나이 정렬" Height="23" HorizontalAlignment="Left" Margin="14,221,0,0" VerticalAlignment="Top" Width="71" />

        <Button Command="{Binding CmdSortAsc}" CommandParameter="Name" Content="이름 정렬" Height="23" HorizontalAlignment="Left" Margin="91,221,0,0" VerticalAlignment="Top" Width="71" />

</Grid>

 

 

 

 

 

CmdChangeSelectedItem의 CommandParameter의 True, False는 다음과 같습니다.

<sys:Boolean x:Key="True">True</sys:Boolean>

       <sys:Boolean x:Key="False">False</sys:Boolean>

 

CmdSortAsc CommandParameter는 정렬할 속성명을 인자로 넘겨주고 있습니다.

 

 

압축한 데모 프로젝트입니다.

 AboutCollectionViewSource.zip

 

 

+ Recent posts