2020년 7월 3일 금요일

WPF - List, ComboBox

1. 데이터 컨테이너

데이터를 보관할 때 List<> 나 ObservableCollection<> 등을 사용할 수 있음.
후자는 INotifyPropertyChanged 참조하는 곳에 자동으로 호출되서 잘 쓰임.


2. ICollectionView 

private ICollectionView GetList()
{
    CustList list = (CustList)this.FindResource("custList");
 
    return CollectionViewSource.GetDefaultView(list);
}
데이터 컨테이너는 ICollectionView 를 위처럼 얻을 수 있다.
이건 다양한 기능을 할 수 있다.


1. 현재 선택한 데이터
private void btnPre_Click(object senderRoutedEventArgs e)
{
    ICollectionView view = GetList();
    view.MoveCurrentToPrevious();
    if (view.IsCurrentBeforeFirst)
        view.MoveCurrentToFirst();
}
 
private void btnNext_Click(object senderRoutedEventArgs e)
{
    ICollectionView view = GetList();
    view.MoveCurrentToNext();
    if (view.IsCurrentAfterLast)
        view.MoveCurrentToLast();
}
위처럼 현재 선택된 데이터를 조절할 수 있다.
이는 List, ComboBox 등에서의 Focus 와 자동으로 연동된다. 


2. 정렬
private void btnSortAge_Click(object senderRoutedEventArgs e)
{
    ICollectionView list = GetList();
 
    if (list.SortDescriptions.Count == 0)
    {
        list.SortDescriptions.Add(new SortDescription("Age"ListSortDirection.Descending));
        list.SortDescriptions.Add(new SortDescription("Name"ListSortDirection.Ascending));
    }
    else
    {
        list.SortDescriptions.Clear();
    }
}
ICollectionView 는 다음과 같이 정렬시킬 수 있음. 
인덱스가 작을 수록 우선도 높은 정렬이 됨.

private void btnSortName_Click(object senderRoutedEventArgs e)
{
    ListCollectionView list = (ListCollectionView)GetList();
 
    if(list.CustomSort == null)
    {
        list.CustomSort = new CustSorter();
    }
    else
    {
        list.CustomSort = null;
    }
}
List 의 경우 ListCollectionView 를 사용해서 정렬을 커스터마이즈 할 수 있음.
이건 ICollectionView 를 상속하는 클래스임.
양수일 때 바꾼단 말임.


3. 필터링
private void btnFilter_Click(object senderRoutedEventArgs e)
{
    ICollectionView list = GetList();
 
    if (list.Filter == null)
    {
        list.Filter = delegate (object item) { return ((Cust)item).Age >= 20; };
    }
    else list.Filter = null;
}
ICollectionView 에는 Filter 가 있어서 함수 오버라이딩으로 해결.


4. 그룹핑
private void btnGroupName_Click(object senderRoutedEventArgs e)
{
    ICollectionView list = GetList();
 
    if (list.GroupDescriptions.Count == 0)
        list.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
    else
        list.GroupDescriptions.Clear();
}
ICollectionView 에는 GroupDescriptions 가 있어서 여기에 위처럼 추가.
더해진 만큼 Tree 처럼 계층화가 됨.
private void btnGroupAge_Click(object senderRoutedEventArgs e)
{
    ICollectionView list = GetList();
 
    if (list.GroupDescriptions.Count == 0)
        list.GroupDescriptions.Add(
            new PropertyGroupDescription("Age"new AgeToRangeConverter()));
    else
        list.GroupDescriptions.Clear();
}
전 글에서 설명한 IValueConverter 를 이용해 위처럼 그룹별 커스터마이즈



2. ListBox

<ListBox x:Name="lstCust" Grid.Column="0" ItemsSource="{Binding}"
         DisplayMemberPath="Name"
         SelectedValuePath="Age"
         IsSynchronizedWithCurrentItem="True"
         MouseDoubleClick="lstCust_MouseDoubleClick"
         />
여기서 리소스는 Item 자료형의 ToString 이라는 함수를 통해 변환되어 가져옴.
그러므로 ToString 함수를 오버라이드하면 잘 안쓰이지만 커스터마이즈 가능.

ItemSource 는 DataContext 랑 다른 자기가 사용할 데이터을 말함.

DisplayMember 와 SelectedItem, SelectedValue 가 중요함.

DisplayMember 로는 화면에 무엇이 보여지는지 설정 가능함.

SelectedValue 와 SelectedItem 은 구분되어 
SelectedValuePath 은 위처럼 Item 중 원하는 속성만 정할 수 있음.


3. ComboBox

<ComboBox Grid.Row="6" Grid.Column="1" 
          ItemsSource="{Binding}" 
          DisplayMemberPath="Name" 
          SelectedValuePath="Age" 
          SelectedValue="{Binding Path=Age}"
          IsSynchronizedWithCurrentItem="True"/>
ListBox 랑 사용법이 거의 같음.



4. List Customize

1. ItemTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<ListBox.ItemTemplate>
    <DataTemplate DataType="{x:Type local:Cust}">
        <Grid  HorizontalAlignment="Stretch"  Background="AliceBlue">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" 
                               Source ="https://ww.namu.la/s/a5947ceb0c55cb5eefcb5370f5a39e7"
                               Width="100" Height="50"/>
            <TextBlock Grid.Row="0" Grid.Column="1" Text="성명 : "/>
            <TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Path=name}" />
            <TextBlock Grid.Row="1" Grid.Column="1" Text="나이 : " />
            <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding Path=age}" />
            <Button x:Name="btnDelete" Grid.Row="0" Grid.Column="3" Content="삭제" Click="btnDelete_Click"/>
            <Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4"
                                VerticalAlignment="Bottom" BorderBrush="Black" BorderThickness="0, 0, 0, 1"/>
        </Grid>
    </DataTemplate>
</ListBox.ItemTemplate>
cs

List 에서 아이템을 표현하는 형식을 지정 가능함.

DataTemplate 은 여러군데에서도 쓰임.
그래서 적용할 데이터 타입만 위처럼 지정하고, 윈도우의 Resource 등으로 빼도 적용됨.
해당 타입은 다 자동으로 쓰이는 것. 하지만 자주 쓰이진 않음.

보통은 List.ItemTemplate 에서 DataTemplate 을 Content 로 넣는 걸로 많이 사용함.


2. GroupStyle

1
2
3
<ListBox.GroupStyle>
         <x:Static Member="GroupStyle.Default"/>
</ListBox.GroupStyle>
cs
위를 통해 그룹별로 UI 에 표시 가능. 
이건 디폴트 적용

3. GroupStyle.HeaderTemplate


1
2
3
4
5
6
7
8
9
10
11
12
<ListBox.GroupStyle>
    <GroupStyle>
        <GroupStyle.HeaderTemplate>
            <DataTemplate>
                <TextBlock Background="#333" Foreground="#ffffff" FontWeight="Bold">
                                <TextBlock Text="{Binding Name}"/>
                                (<TextBlock Text="{Binding ItemCount}"/>)
                            </TextBlock>
            </DataTemplate>
        </GroupStyle.HeaderTemplate>
    </GroupStyle>
</ListBox.GroupStyle>
cs
이렇게 그룹 헤더 커스터마이즈 가능함.
얘도 DataTemplate 처럼 Resource 로 옮기기도 가능함


4. GroupStyle.ContainerStyle


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<GroupStyle.ContainerStyle>
    <Style TargetType="{x:Type GroupItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Expander IsExpanded="True">
                        <Expander.Header>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#333" FontFamily="16"/>
                                <TextBlock Text="{Binding ItemCount}" FontWeight="Bold" Foreground="#333" FontFamily="16" Margin="10,0,0,0"/>
                            </StackPanel>
                        </Expander.Header>
                        <ItemsPresenter/>
                    </Expander>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</GroupStyle.ContainerStyle>
cs

이거는 HeaderTemplate 과 다르게 클릭을 통해 접는 것도 가능함.
이는 위 코드대로 하면 되는데, 복잡해서 복붙하면서 쓸듯.

댓글 없음:

댓글 쓰기

List