열 머리글 클릭시 WPF ListView / GridView 정렬을 만드는 가장 좋은 방법은 무엇입니까?


84

있습니다 많은 WPF에서이 겉으로는 매우-기본 누락을 채우기 위해 시도하는 인터넷 솔루션은. "가장 좋은"방법이 무엇인지 정말 혼란 스럽습니다. 예를 들어 ... 정렬 방향을 나타 내기 위해 열 머리글에 작은 위쪽 / 아래쪽 화살표가 있어야합니다. 이를 수행하는 3 가지 다른 방법이 있습니다. 일부는 코드를 사용하고, 일부는 마크 업을 사용하고, 일부는 마크 업 플러스 코드를 사용하고, 모두 해킹처럼 보입니다.

이전에이 문제에 직면 한 사람이 있고 완전히 만족하는 해결책을 찾았습니까? 이러한 기본 WinForms 기능이 WPF에서 누락되어 해킹이 필요하다는 것은 이상하게 보입니다.


util을 인식하는 방법에 대한 위의 질문에 대한 답장. xaml 문서 맨 위에있는 네임 스페이스에 xmlns : util = "clr-namespace : Wpf.Util"추가
meldo

가능하다면 .. DataGrid를 사용하십시오.
Abhijeet Nagre

답변:



111

자동으로 정렬하기 위해 첨부 된 속성 집합을 작성했습니다 . 여기에서GridView 확인할 수 있습니다 . 위쪽 / 아래쪽 화살표를 처리하지 않지만 쉽게 추가 할 수 있습니다.

<ListView ItemsSource="{Binding Persons}"
          IsSynchronizedWithCurrentItem="True"
          util:GridViewSort.AutoSort="True">
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="Name"
                                DisplayMemberBinding="{Binding Name}"
                                util:GridViewSort.PropertyName="Name"/>
                <GridViewColumn Header="First name"
                                DisplayMemberBinding="{Binding FirstName}"
                                util:GridViewSort.PropertyName="FirstName"/>
                <GridViewColumn Header="Date of birth"
                                DisplayMemberBinding="{Binding DateOfBirth}"
                                util:GridViewSort.PropertyName="DateOfBirth"/>
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

8
Thomas에게 감사합니다. 분류 문제에 대한 귀하의 솔루션은 우아하고 사용하기 쉽고 매우 유연합니다. 즉, 완벽합니다! 다른 사용자를위한 팁 : 1) Thomas의 기사에 링크 된 업데이트 된 버전을 사용하고, 2) 주석에 Alex의 더 예쁜 글리프 버전을 사용하십시오.
Helge Klein

뛰어난 유틸리티 Mr. L!
Metro Smurf

저는 WPF를 처음 접했고이 "유틸리티"비트를 잘 이해하지 못합니다. 그 참조는 무엇입니까? 편집 : nm ... 바로 나에게 튀어 나오지 않은 "소스보기"라는 작은 링크가있었습니다. 이 클래스의 소스 코드를 확장
oscilatingcretin

나는 그것을 매우 좋아한다! 항목을 동적으로 추가 / 제거하고 있으며 순서를 변경하지 않고도 훌륭하게 작동합니다. 그러나 정렬을위한 초기 상태를 어떻게 설정할 수 있습니까? CollectionViewSource.GetDefaultView(MyList.ItemsSource).SortDescriptions.Add(new SortDescription("Number", ListSortDirection.Ascending));작동하지 않습니다.
zee 2011

@zee, 작동해야하지만 정렬 문양이 표시되지 않습니다 ... 초기 순서를 설정하는 방법을 구현하지 않았지만 언제든지 내 코드를 수정할 수 있습니다.)
Thomas Levesque

23

MSDN에는 위쪽 / 아래쪽 글리프가있는 열 정렬을 쉽게 수행 할 수있는 방법이 있습니다. 그러나 예제는 완전하지 않습니다. 글리프에 데이터 템플릿을 사용하는 방법을 설명하지 않습니다. 아래는 ListView로 작업해야하는 것입니다. 이것은 .Net 4에서 작동합니다.

ListView에서 GridViewColumnHeader를 클릭 할 때 발생하는 이벤트 처리기를 지정해야합니다. 내 ListView는 다음과 같습니다.

<ListView Name="results" GridViewColumnHeader.Click="results_Click">
    <ListView.View>
        <GridView>
            <GridViewColumn DisplayMemberBinding="{Binding Path=ContactName}">
                <GridViewColumn.Header>
                    <GridViewColumnHeader Content="Contact Name" Padding="5,0,0,0" HorizontalContentAlignment="Left" MinWidth="150" Name="ContactName" />
                </GridViewColumn.Header>
            </GridViewColumn>
            <GridViewColumn DisplayMemberBinding="{Binding Path=PrimaryPhone}">
                <GridViewColumn.Header>
                    <GridViewColumnHeader Content="Contact Number" Padding="5,0,0,0" HorizontalContentAlignment="Left" MinWidth="150" Name="PrimaryPhone"/>
                </GridViewColumn.Header>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

코드 숨김에서 정렬을 처리 할 코드를 설정합니다.

// Global objects
BindingListCollectionView blcv;
GridViewColumnHeader _lastHeaderClicked = null;
ListSortDirection _lastDirection = ListSortDirection.Ascending;

// Header click event
void results_Click(object sender, RoutedEventArgs e)
{
    GridViewColumnHeader headerClicked =
    e.OriginalSource as GridViewColumnHeader;
    ListSortDirection direction;

    if (headerClicked != null)
    {
    if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
    {
        if (headerClicked != _lastHeaderClicked)
        {
            direction = ListSortDirection.Ascending;
        }
        else
        {
            if (_lastDirection == ListSortDirection.Ascending)
            {
                direction = ListSortDirection.Descending;
            }
            else
            {
                direction = ListSortDirection.Ascending;
            }
        }

        string header = headerClicked.Column.Header as string;
        Sort(header, direction);

        if (direction == ListSortDirection.Ascending)
        {
            headerClicked.Column.HeaderTemplate =
              Resources["HeaderTemplateArrowUp"] as DataTemplate;
        }
        else
        {
            headerClicked.Column.HeaderTemplate =
              Resources["HeaderTemplateArrowDown"] as DataTemplate;
        }

        // Remove arrow from previously sorted header
        if (_lastHeaderClicked != null && _lastHeaderClicked != headerClicked)
        {
            _lastHeaderClicked.Column.HeaderTemplate = null;
        }

        _lastHeaderClicked = headerClicked;
        _lastDirection = direction;
    }
}

// Sort code
private void Sort(string sortBy, ListSortDirection direction)
{
    blcv.SortDescriptions.Clear();
    SortDescription sd = new SortDescription(sortBy, direction);
    blcv.SortDescriptions.Add(sd);
    blcv.Refresh();
}

그런 다음 XAML에서 정렬 방법에 지정한 두 개의 DataTemplate을 추가해야합니다.

<DataTemplate x:Key="HeaderTemplateArrowUp">
    <DockPanel LastChildFill="True" Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GridViewColumnHeader}}}">
        <Path x:Name="arrowUp" StrokeThickness="1" Fill="Gray" Data="M 5,10 L 15,10 L 10,5 L 5,10" DockPanel.Dock="Right" Width="20" HorizontalAlignment="Right" Margin="5,0,5,0" SnapsToDevicePixels="True"/>
        <TextBlock Text="{Binding }" />
    </DockPanel>
</DataTemplate>

<DataTemplate x:Key="HeaderTemplateArrowDown">
    <DockPanel LastChildFill="True" Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GridViewColumnHeader}}}">
        <Path x:Name="arrowDown" StrokeThickness="1" Fill="Gray"  Data="M 5,5 L 10,10 L 15,5 L 5,5" DockPanel.Dock="Right" Width="20" HorizontalAlignment="Right" Margin="5,0,5,0" SnapsToDevicePixels="True"/>
        <TextBlock Text="{Binding }" />
    </DockPanel>
</DataTemplate>

를 사용 DockPanel하여 LastChildFilltrue로 설정하면 헤더의 오른쪽에있는 문양을 유지하고 라벨이 공간의 나머지 부분을 채울 수있게된다. 열에 너비가 없기 때문에 DockPanel너비를의 ActualWidth에 바인딩 GridViewColumnHeader하여 내용에 자동으로 맞출 수 있습니다. MinWidth그래도 열에 s를 설정 하여 글리프가 열 제목을 가리지 않도록했습니다. 은 TextBlock Text열 이름 헤더에서 지정되는 표시 빈 결합으로 설정된다.


1
이것은 XAML에서 DataTemplates Grid.Resources를 배치 할 위치를 지정하지 않습니다.

4
@Mark 이것은 아마도 당신을 돕기에는 너무 늦었을 것입니다. 그러나 템플릿은 루트 요소의 Resources, 일반적으로 <Window.Resources>또는 <UserControl.Resources>. HTHS;)
CptRobby 2013

1
@CptRobby 안녕하세요 .. blcv가 초기화되지 않았기 때문에 null 참조 예외가 발생하므로이 코드는 누구에게나 어떻게 작동합니까?
Jay Nirgudkar

@JayNirgudkar 나는 이것의 저자가 아닙니다, Jared Harley는입니다. 그러나 blcv는 그가 ListView의 ItemsSource로 사용했던 것임을 말할 수 있습니다. 같은 일을 할 필요가 없습니다. ItemsSource를 처리하는 다른 방법으로 그가 제공 한 MSDN 링크를 클릭하십시오.
CptRobby

2
MSDN 예제에서는 headerClicked.Column.Header(헤더 텍스트)가 (headerClicked.Column.DisplayMemberBinding as Binding).Path.Path(바인딩 경로) 와 동일 하다고 가정합니다 . 헤더 텍스트에 대한 정렬이 작동하지 않습니다. 엄청 이상해.
Chris

5

저는 MVVM을 사용하므로 Thomas를 참조로 사용하여 몇 가지 첨부 된 속성을 만들었습니다. 헤더를 클릭하면 한 번에 한 열씩 정렬하여 오름차순과 내림차순 사이를 전환합니다. 첫 번째 열을 사용하여 처음부터 정렬합니다. 그리고 그것은 Win7 / 8 스타일 글리프를 보여줍니다.

일반적으로 기본 속성을 true로 설정하기 만하면됩니다 (그러나 GridViewColumnHeaders를 명시 적으로 선언해야 함).

<Window xmlns:local="clr-namespace:MyProjectNamespace">
  <Grid>
    <ListView local:App.EnableGridViewSort="True" ItemsSource="{Binding LVItems}">
      <ListView.View>
        <GridView>
          <GridViewColumn DisplayMemberBinding="{Binding Property1}">
            <GridViewColumnHeader Content="Prop 1" />
          </GridViewColumn>
          <GridViewColumn DisplayMemberBinding="{Binding Property2}">
            <GridViewColumnHeader Content="Prop 2" />
          </GridViewColumn>
        </GridView>
      </ListView.View>
    </ListView>
  </Grid>
<Window>

디스플레이와 다른 속성을 기준으로 정렬하려면 다음을 선언해야합니다.

<GridViewColumn DisplayMemberBinding="{Binding Property3}"
                local:App.GridViewSortPropertyName="Property4">
    <GridViewColumnHeader Content="Prop 3" />
</GridViewColumn>

다음은 연결된 속성에 대한 코드입니다. 나는 게으르고 제공된 App.xaml.cs에 넣는 것을 좋아합니다.

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data.
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace MyProjectNamespace
{
  public partial class App : Application
  {
      #region GridViewSort
      public static DependencyProperty GridViewSortPropertyNameProperty =
          DependencyProperty.RegisterAttached(
              "GridViewSortPropertyName", 
              typeof(string), 
              typeof(App), 
              new UIPropertyMetadata(null)
          );

      public static string GetGridViewSortPropertyName(GridViewColumn gvc)
      {
          return (string)gvc.GetValue(GridViewSortPropertyNameProperty);
      }

      public static void SetGridViewSortPropertyName(GridViewColumn gvc, string n)
      {
          gvc.SetValue(GridViewSortPropertyNameProperty, n);
      }

      public static DependencyProperty CurrentSortColumnProperty =
          DependencyProperty.RegisterAttached(
              "CurrentSortColumn", 
              typeof(GridViewColumn), 
              typeof(App), 
              new UIPropertyMetadata(
                  null, 
                  new PropertyChangedCallback(CurrentSortColumnChanged)
              )
          );

      public static GridViewColumn GetCurrentSortColumn(GridView gv)
      {
          return (GridViewColumn)gv.GetValue(CurrentSortColumnProperty);
      }

      public static void SetCurrentSortColumn(GridView gv, GridViewColumn value)
      {
          gv.SetValue(CurrentSortColumnProperty, value);
      }

      public static void CurrentSortColumnChanged(
          object sender, DependencyPropertyChangedEventArgs e)
      {
          GridViewColumn gvcOld = e.OldValue as GridViewColumn;
          if (gvcOld != null)
          {
              CurrentSortColumnSetGlyph(gvcOld, null);
          }
      }

      public static void CurrentSortColumnSetGlyph(GridViewColumn gvc, ListView lv)
      {
          ListSortDirection lsd;
          Brush brush;
          if (lv == null)
          {
              lsd = ListSortDirection.Ascending;
              brush = Brushes.Transparent;
          }
          else
          {
              SortDescriptionCollection sdc = lv.Items.SortDescriptions;
              if (sdc == null || sdc.Count < 1) return;
              lsd = sdc[0].Direction;
              brush = Brushes.Gray;
          }

          FrameworkElementFactory fefGlyph = 
              new FrameworkElementFactory(typeof(Path));
          fefGlyph.Name = "arrow";
          fefGlyph.SetValue(Path.StrokeThicknessProperty, 1.0);
          fefGlyph.SetValue(Path.FillProperty, brush);
          fefGlyph.SetValue(StackPanel.HorizontalAlignmentProperty, 
              HorizontalAlignment.Center);

          int s = 4;
          if (lsd == ListSortDirection.Ascending)
          {
              PathFigure pf = new PathFigure();
              pf.IsClosed = true;
              pf.StartPoint = new Point(0, s);
              pf.Segments.Add(new LineSegment(new Point(s * 2, s), false));
              pf.Segments.Add(new LineSegment(new Point(s, 0), false));

              PathGeometry pg = new PathGeometry();
              pg.Figures.Add(pf);

              fefGlyph.SetValue(Path.DataProperty, pg);
          }
          else
          {
              PathFigure pf = new PathFigure();
              pf.IsClosed = true;
              pf.StartPoint = new Point(0, 0);
              pf.Segments.Add(new LineSegment(new Point(s, s), false));
              pf.Segments.Add(new LineSegment(new Point(s * 2, 0), false));

              PathGeometry pg = new PathGeometry();
              pg.Figures.Add(pf);

              fefGlyph.SetValue(Path.DataProperty, pg);
          }

          FrameworkElementFactory fefTextBlock = 
              new FrameworkElementFactory(typeof(TextBlock));
          fefTextBlock.SetValue(TextBlock.HorizontalAlignmentProperty,
              HorizontalAlignment.Center);
          fefTextBlock.SetValue(TextBlock.TextProperty, new Binding());

          FrameworkElementFactory fefDockPanel = 
              new FrameworkElementFactory(typeof(StackPanel));
          fefDockPanel.SetValue(StackPanel.OrientationProperty,
              Orientation.Vertical);
          fefDockPanel.AppendChild(fefGlyph);
          fefDockPanel.AppendChild(fefTextBlock);

          DataTemplate dt = new DataTemplate(typeof(GridViewColumn));
          dt.VisualTree = fefDockPanel;

          gvc.HeaderTemplate = dt;
      }

      public static DependencyProperty EnableGridViewSortProperty =
          DependencyProperty.RegisterAttached(
              "EnableGridViewSort", 
              typeof(bool), 
              typeof(App), 
              new UIPropertyMetadata(
                  false, 
                  new PropertyChangedCallback(EnableGridViewSortChanged)
              )
          );

      public static bool GetEnableGridViewSort(ListView lv)
      {
          return (bool)lv.GetValue(EnableGridViewSortProperty);
      }

      public static void SetEnableGridViewSort(ListView lv, bool value)
      {
          lv.SetValue(EnableGridViewSortProperty, value);
      }

      public static void EnableGridViewSortChanged(
          object sender, DependencyPropertyChangedEventArgs e)
      {
          ListView lv = sender as ListView;
          if (lv == null) return;

          if (!(e.NewValue is bool)) return;
          bool enableGridViewSort = (bool)e.NewValue;

          if (enableGridViewSort)
          {
              lv.AddHandler(
                  GridViewColumnHeader.ClickEvent,
                  new RoutedEventHandler(EnableGridViewSortGVHClicked)
              );
              if (lv.View == null)
              {
                  lv.Loaded += new RoutedEventHandler(EnableGridViewSortLVLoaded);
              }
              else
              {
                  EnableGridViewSortLVInitialize(lv);
              }
          }
          else
          {
              lv.RemoveHandler(
                  GridViewColumnHeader.ClickEvent,
                  new RoutedEventHandler(EnableGridViewSortGVHClicked)
              );
          }
      }

      public static void EnableGridViewSortLVLoaded(object sender, RoutedEventArgs e)
      {
          ListView lv = e.Source as ListView;
          EnableGridViewSortLVInitialize(lv);
          lv.Loaded -= new RoutedEventHandler(EnableGridViewSortLVLoaded);
      }

      public static void EnableGridViewSortLVInitialize(ListView lv)
      {
          GridView gv = lv.View as GridView;
          if (gv == null) return;

          bool first = true;
          foreach (GridViewColumn gvc in gv.Columns)
          {
              if (first)
              {
                  EnableGridViewSortApplySort(lv, gv, gvc);
                  first = false;
              }
              else
              {
                  CurrentSortColumnSetGlyph(gvc, null);
              }
          }
      }

      public static void EnableGridViewSortGVHClicked(
          object sender, RoutedEventArgs e)
      {
          GridViewColumnHeader gvch = e.OriginalSource as GridViewColumnHeader;
          if (gvch == null) return;
          GridViewColumn gvc = gvch.Column;
          if(gvc == null) return;            
          ListView lv = VisualUpwardSearch<ListView>(gvch);
          if (lv == null) return;
          GridView gv = lv.View as GridView;
          if (gv == null) return;

          EnableGridViewSortApplySort(lv, gv, gvc);
      }

      public static void EnableGridViewSortApplySort(
          ListView lv, GridView gv, GridViewColumn gvc)
      {
          bool isEnabled = GetEnableGridViewSort(lv);
          if (!isEnabled) return;

          string propertyName = GetGridViewSortPropertyName(gvc);
          if (string.IsNullOrEmpty(propertyName))
          {
              Binding b = gvc.DisplayMemberBinding as Binding;
              if (b != null && b.Path != null)
              {
                  propertyName = b.Path.Path;
              }

              if (string.IsNullOrEmpty(propertyName)) return;
          }

          ApplySort(lv.Items, propertyName);
          SetCurrentSortColumn(gv, gvc);
          CurrentSortColumnSetGlyph(gvc, lv);
      }

      public static void ApplySort(ICollectionView view, string propertyName)
      {
          if (string.IsNullOrEmpty(propertyName)) return;

          ListSortDirection lsd = ListSortDirection.Ascending;
          if (view.SortDescriptions.Count > 0)
          {
              SortDescription sd = view.SortDescriptions[0];
              if (sd.PropertyName.Equals(propertyName))
              {
                  if (sd.Direction == ListSortDirection.Ascending)
                  {
                      lsd = ListSortDirection.Descending;
                  }
                  else
                  {
                      lsd = ListSortDirection.Ascending;
                  }
              }
              view.SortDescriptions.Clear();
          }

          view.SortDescriptions.Add(new SortDescription(propertyName, lsd));
      }
      #endregion

      public static T VisualUpwardSearch<T>(DependencyObject source) 
          where T : DependencyObject
      {
          return VisualUpwardSearch(source, x => x is T) as T;
      }

      public static DependencyObject VisualUpwardSearch(
                          DependencyObject source, Predicate<DependencyObject> match)
      {
          DependencyObject returnVal = source;

          while (returnVal != null && !match(returnVal))
          {
              DependencyObject tempReturnVal = null;
              if (returnVal is Visual || returnVal is Visual3D)
              {
                  tempReturnVal = VisualTreeHelper.GetParent(returnVal);
              }
              if (tempReturnVal == null)
              {
                  returnVal = LogicalTreeHelper.GetParent(returnVal);
              }
              else
              {
                  returnVal = tempReturnVal;
              }
          }

          return returnVal;
      }
  }
}

3

나는 마이크로 소프트 방식을 수정하여 ListView컨트롤을 재정 의하여 다음 을 만들었습니다 SortableListView.

public partial class SortableListView : ListView
    {        
        private GridViewColumnHeader lastHeaderClicked = null;
        private ListSortDirection lastDirection = ListSortDirection.Ascending;       

        public void GridViewColumnHeaderClicked(GridViewColumnHeader clickedHeader)
        {
            ListSortDirection direction;

            if (clickedHeader != null)
            {
                if (clickedHeader.Role != GridViewColumnHeaderRole.Padding)
                {
                    if (clickedHeader != lastHeaderClicked)
                    {
                        direction = ListSortDirection.Ascending;
                    }
                    else
                    {
                        if (lastDirection == ListSortDirection.Ascending)
                        {
                            direction = ListSortDirection.Descending;
                        }
                        else
                        {
                            direction = ListSortDirection.Ascending;
                        }
                    }

                    string sortString = ((Binding)clickedHeader.Column.DisplayMemberBinding).Path.Path;

                    Sort(sortString, direction);

                    lastHeaderClicked = clickedHeader;
                    lastDirection = direction;
                }
            }
        }

        private void Sort(string sortBy, ListSortDirection direction)
        {
            ICollectionView dataView = CollectionViewSource.GetDefaultView(this.ItemsSource != null ? this.ItemsSource : this.Items);

            dataView.SortDescriptions.Clear();
            SortDescription sD = new SortDescription(sortBy, direction);
            dataView.SortDescriptions.Add(sD);
            dataView.Refresh();
        }
    }

라인 ((Binding)clickedHeader.Column.DisplayMemberBinding).Path.Path비트는 열 이름이 바인딩 경로와 동일하지 않은 경우를 처리하지만 Microsoft 메서드는 수행하지 않습니다.

GridViewColumnHeader.Click더 이상 생각할 필요가 없도록 이벤트 를 가로 채고 싶었지만 할 방법을 찾지 못했습니다. 결과적으로 모든 XAML에 다음을 추가합니다 SortableListView.

GridViewColumnHeader.Click="SortableListViewColumnHeaderClicked"

그런 다음 s Window가 포함 SortableListView된 항목에 다음 코드를 추가하면됩니다.

private void SortableListViewColumnHeaderClicked(object sender, RoutedEventArgs e)
        {
            ((Controls.SortableListView)sender).GridViewColumnHeaderClicked(e.OriginalSource as GridViewColumnHeader);
        }

컨트롤 Controls을 만든 네임 스페이스의 XAML ID는 어디에 있습니까 SortableListView?

따라서 이것은 정렬 측면에서 코드 중복을 방지하므로 위와 같이 이벤트를 처리하는 것을 기억해야합니다.


1
솔루션에서 영감을 얻어 같은 길을 따라 GridViewColumnHeader.Click 이벤트에 액세스하여 생성자에 처리기를 추가 할 수 있습니다. this.AddHandler (GridViewColumnHeader.ClickEvent, new RoutedEventHandler (GridViewColumnHeaderClicked));
Derrick Moeller 2015

3

리스트 뷰가 있고 그것을 그리드 뷰로 바꾸면 쉽게 클릭 할 수있는 그리드 뷰 열 헤더를 만들 수 있습니다.

        <Style TargetType="GridViewColumnHeader">
            <Setter Property="Command" Value="{Binding CommandOrderBy}"/>
            <Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self},Path=Content}"/>
        </Style>

그런 다음 코드에 위임 명령을 설정하십시오.

    public DelegateCommand CommandOrderBy { get { return new DelegateCommand(Delegated_CommandOrderBy); } }

    private void Delegated_CommandOrderBy(object obj)
    {
        throw new NotImplementedException();
    }

여기에서 ICommand DelegateCommand를 만드는 방법을 모두 알고 있다고 가정하겠습니다. 이를 통해 ViewModel에서 모든 뷰를 계속 클릭 할 수있었습니다.

나는 동일한 일을 수행하는 여러 가지 방법이 있도록 이것을 추가했습니다. 헤더에 화살표 버튼을 추가하는 코드는 작성하지 않았지만 XAML 스타일로 수행되므로 JanDotNet이 코드에 포함하는 전체 헤더를 다시 디자인해야합니다.


0

열 머리글 템플릿을 포함하여 기존 답변 및 주석의 모든 작업 부분을 요약하는 솔루션 :

전망:

<ListView x:Class="MyNamspace.MyListView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             ItemsSource="{Binding Items}"
             GridViewColumnHeader.Click="ListViewColumnHeaderClick">
    <ListView.Resources>

        <Style TargetType="Grid" x:Key="HeaderGridStyle">
            <Setter Property="Height" Value="20" />
        </Style>

        <Style TargetType="TextBlock" x:Key="HeaderTextBlockStyle">
            <Setter Property="Margin" Value="5,0,0,0" />
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>

        <Style TargetType="Path" x:Key="HeaderPathStyle">
            <Setter Property="StrokeThickness" Value="1" />
            <Setter Property="Fill" Value="Gray" />
            <Setter Property="Width" Value="20" />
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="Margin" Value="5,0,5,0" />
            <Setter Property="SnapsToDevicePixels" Value="True" />
        </Style>

        <DataTemplate x:Key="HeaderTemplateDefault">
            <Grid Style="{StaticResource HeaderGridStyle}">
                <TextBlock Text="{Binding }" Style="{StaticResource HeaderTextBlockStyle}" />
            </Grid>
        </DataTemplate>

        <DataTemplate x:Key="HeaderTemplateArrowUp">
            <Grid Style="{StaticResource HeaderGridStyle}">
                <Path Data="M 7,3 L 13,3 L 10,0 L 7,3" Style="{StaticResource HeaderPathStyle}" />
                <TextBlock Text="{Binding }" Style="{StaticResource HeaderTextBlockStyle}" />
            </Grid>
        </DataTemplate>

        <DataTemplate x:Key="HeaderTemplateArrowDown">
            <Grid Style="{StaticResource HeaderGridStyle}">
                <Path Data="M 7,0 L 10,3 L 13,0 L 7,0"  Style="{StaticResource HeaderPathStyle}" />
                <TextBlock Text="{Binding }" Style="{StaticResource HeaderTextBlockStyle}" />
            </Grid>
        </DataTemplate>

    </ListView.Resources>

    <ListView.View>
        <GridView ColumnHeaderTemplate="{StaticResource HeaderTemplateDefault}">

            <GridViewColumn Header="Name" DisplayMemberBinding="{Binding NameProperty}" />
            <GridViewColumn Header="Type" Width="45" DisplayMemberBinding="{Binding TypeProperty}"/>

            <!-- ... -->

        </GridView>
    </ListView.View>
</ListView>

코드 비하인드 :

public partial class MyListView : ListView
{
    GridViewColumnHeader _lastHeaderClicked = null;

    public MyListView()
    {
        InitializeComponent();
    }

    private void ListViewColumnHeaderClick(object sender, RoutedEventArgs e)
    {
        GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;

        if (headerClicked == null)
            return;

        if (headerClicked.Role == GridViewColumnHeaderRole.Padding)
            return;

        var sortingColumn = (headerClicked.Column.DisplayMemberBinding as Binding)?.Path?.Path;
        if (sortingColumn == null)
            return;

        var direction = ApplySort(Items, sortingColumn);

        if (direction == ListSortDirection.Ascending)
        {
            headerClicked.Column.HeaderTemplate =
                Resources["HeaderTemplateArrowUp"] as DataTemplate;
        }
        else
        {
            headerClicked.Column.HeaderTemplate =
                Resources["HeaderTemplateArrowDown"] as DataTemplate;
        }

        // Remove arrow from previously sorted header
        if (_lastHeaderClicked != null && _lastHeaderClicked != headerClicked)
        {
            _lastHeaderClicked.Column.HeaderTemplate =
                Resources["HeaderTemplateDefault"] as DataTemplate;
        }

        _lastHeaderClicked = headerClicked;
    }


    public static ListSortDirection ApplySort(ICollectionView view, string propertyName)
    {
        ListSortDirection direction = ListSortDirection.Ascending;
        if (view.SortDescriptions.Count > 0)
        {
            SortDescription currentSort = view.SortDescriptions[0];
            if (currentSort.PropertyName == propertyName)
            {
                if (currentSort.Direction == ListSortDirection.Ascending)
                    direction = ListSortDirection.Descending;
                else
                    direction = ListSortDirection.Ascending;
            }
            view.SortDescriptions.Clear();
        }
        if (!string.IsNullOrEmpty(propertyName))
        {
            view.SortDescriptions.Add(new SortDescription(propertyName, direction));
        }
        return direction;
    }
}

1
그들이 무슨 일을하는지 설명하지 않는 경우 코드 수단 아무것도 라인의 수백을 던지기
schizoid04

0

누군가가 WPF ListView를 정렬 할 수있는 또 다른 간단한 방법을 추가하고 싶었습니다.

void SortListView(ListView listView)
{
    IEnumerable listView_items = listView.Items.SourceCollection;
    List<MY_ITEM_CLASS> listView_items_to_list = listView_items.Cast<MY_ITEM_CLASS>().ToList();

    Comparer<MY_ITEM_CLASS> scoreComparer = Comparer<MY_ITEM_CLASS>.Create((first, second) => first.COLUMN_NAME.CompareTo(second.COLUMN_NAME));

    listView_items_to_list.Sort(scoreComparer);
    listView.ItemsSource = null;
    listView.Items.Clear();
    listView.ItemsSource = listView_items_to_list;
}

0

검색 많이 후, 마침내 내가 여기에 간단한을 발견 https://www.wpf-tutorial.com/listview-control/listview-how-to-column-sorting/

private GridViewColumnHeader listViewSortCol = null;
private SortAdorner listViewSortAdorner = null;
private void GridViewColumnHeader_Click(object sender, RoutedEventArgs e)
{
  GridViewColumnHeader column = (sender as GridViewColumnHeader);
  string sortBy = column.Tag.ToString();
  if (listViewSortCol != null)
  {
    AdornerLayer.GetAdornerLayer(listViewSortCol).Remove(listViewSortAdorner);
    yourListView.Items.SortDescriptions.Clear();
  }

  ListSortDirection newDir = ListSortDirection.Ascending;
  if (listViewSortCol == column && listViewSortAdorner.Direction == newDir)
    newDir = ListSortDirection.Descending;

  listViewSortCol = column;
  listViewSortAdorner = new SortAdorner(listViewSortCol, newDir);
  AdornerLayer.GetAdornerLayer(listViewSortCol).Add(listViewSortAdorner);
  yourListView.Items.SortDescriptions.Add(new SortDescription(sortBy, newDir));
}

수업:

public class SortAdorner : Adorner
{
    private static Geometry ascGeometry =
        Geometry.Parse("M 0 4 L 3.5 0 L 7 4 Z");

    private static Geometry descGeometry =
        Geometry.Parse("M 0 0 L 3.5 4 L 7 0 Z");

    public ListSortDirection Direction { get; private set; }

    public SortAdorner(UIElement element, ListSortDirection dir)
        : base(element)
    {
        this.Direction = dir;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        if(AdornedElement.RenderSize.Width < 20)
            return;

        TranslateTransform transform = new TranslateTransform
            (
                AdornedElement.RenderSize.Width - 15,
                (AdornedElement.RenderSize.Height - 5) / 2
            );
        drawingContext.PushTransform(transform);

        Geometry geometry = ascGeometry;
        if(this.Direction == ListSortDirection.Descending)
            geometry = descGeometry;
        drawingContext.DrawGeometry(Brushes.Black, null, geometry);

        drawingContext.Pop();
    }
}

Xaml

<GridViewColumn Width="250">
  <GridViewColumn.Header>
    <GridViewColumnHeader Tag="Name" Click="GridViewColumnHeader_Click">Name</GridViewColumnHeader>
  </GridViewColumn.Header>
  <GridViewColumn.CellTemplate>
    <DataTemplate>
        <TextBlock Text="{Binding Name}" ToolTip="{Binding Name}"/>
    </DataTemplate>
  </GridViewColumn.CellTemplate>
</GridViewColumn>

1
감사. 추가 포인트에 대해 설명 요약을 추가 할 수 있습니까? 이것은 현재 링크 및 코드 전용입니다 (이미 링크 전용보다 낫습니다 ...).
Yunnosch

-2

이 시도:

using System.ComponentModel;
youtItemsControl.Items.SortDescriptions.Add(new SortDescription("yourFavoritePropertyFromItem",ListSortDirection.Ascending);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.