[WPF] Automatically sort a GridView (continued)

Very poorPoorAverageGoodExcellent (22 votes) 
Loading ... Loading ...

A few months ago, I wrote a post where I explained how to automatically sort a GridView when a column header is clicked. I had mentioned a possible improvement : add a sort glyph in the column header to show which column is sorted. In today’s post, I present a new version of the GridViewSort class, which displays the sort glyph.

GridViewSort sample with sort glyph

GridViewSort sample with sort glyph

To achieve this result, I used an Adorner : this is a component which allows to draw over existing UI elements, on an independant rendering layer.

The new version of the GridViewSort class can be used as before, in that case the grid displays default sort glyphs. These default glyphs are not particularly good-looking, so if you have some artistic skills you can provide you own images, as shown in the code below :

        <ListView ItemsSource="{Binding Persons}"
                  IsSynchronizedWithCurrentItem="True"
                  util:GridViewSort.AutoSort="True"
                  util:GridViewSort.SortGlyphAscending="/Images/up.png"
                  util:GridViewSort.SortGlyphDescending="/Images/down.png">

It is also possible to disable the sort glyphs, by setting the ShowSortGlyph attached property to false :

        <ListView ItemsSource="{Binding Persons}"
                  IsSynchronizedWithCurrentItem="True"
                  util:GridViewSort.AutoSort="True"
                  util:GridViewSort.ShowSortGlyph="False">

Note that in the current version, the sort glyph is only displayed when using the automatic sort mode (AutoSort = true). The case of a custom sort using the Command property is not handled yet.

Here is the complete code of the new version of the class :

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Documents;

namespace Wpf.Util
{
    public class GridViewSort
    {
        #region Public attached properties

        public static ICommand GetCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(CommandProperty, value);
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached(
                "Command",
                typeof(ICommand),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    null,
                    (o, e) =>
                    {
                        ItemsControl listView = o as ItemsControl;
                        if (listView != null)
                        {
                            if (!GetAutoSort(listView)) // Don't change click handler if AutoSort enabled
                            {
                                if (e.OldValue != null && e.NewValue == null)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (e.OldValue == null && e.NewValue != null)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static bool GetAutoSort(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoSortProperty);
        }

        public static void SetAutoSort(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoSortProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoSort.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoSortProperty =
            DependencyProperty.RegisterAttached(
                "AutoSort",
                typeof(bool),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    false,
                    (o, e) =>
                    {
                        ListView listView = o as ListView;
                        if (listView != null)
                        {
                            if (GetCommand(listView) == null) // Don't change click handler if a command is set
                            {
                                bool oldValue = (bool)e.OldValue;
                                bool newValue = (bool)e.NewValue;
                                if (oldValue && !newValue)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (!oldValue && newValue)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static string GetPropertyName(DependencyObject obj)
        {
            return (string)obj.GetValue(PropertyNameProperty);
        }

        public static void SetPropertyName(DependencyObject obj, string value)
        {
            obj.SetValue(PropertyNameProperty, value);
        }

        // Using a DependencyProperty as the backing store for PropertyName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.RegisterAttached(
                "PropertyName",
                typeof(string),
                typeof(GridViewSort),
                new UIPropertyMetadata(null)
            );

        public static bool GetShowSortGlyph(DependencyObject obj)
        {
            return (bool)obj.GetValue(ShowSortGlyphProperty);
        }

        public static void SetShowSortGlyph(DependencyObject obj, bool value)
        {
            obj.SetValue(ShowSortGlyphProperty, value);
        }

        // Using a DependencyProperty as the backing store for ShowSortGlyph.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShowSortGlyphProperty =
            DependencyProperty.RegisterAttached("ShowSortGlyph", typeof(bool), typeof(GridViewSort), new UIPropertyMetadata(true));

        public static ImageSource GetSortGlyphAscending(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(SortGlyphAscendingProperty);
        }

        public static void SetSortGlyphAscending(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(SortGlyphAscendingProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortGlyphAscending.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SortGlyphAscendingProperty =
            DependencyProperty.RegisterAttached("SortGlyphAscending", typeof(ImageSource), typeof(GridViewSort), new UIPropertyMetadata(null));

        public static ImageSource GetSortGlyphDescending(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(SortGlyphDescendingProperty);
        }

        public static void SetSortGlyphDescending(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(SortGlyphDescendingProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortGlyphDescending.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SortGlyphDescendingProperty =
            DependencyProperty.RegisterAttached("SortGlyphDescending", typeof(ImageSource), typeof(GridViewSort), new UIPropertyMetadata(null));

        #endregion

        #region Private attached properties

        private static GridViewColumnHeader GetSortedColumnHeader(DependencyObject obj)
        {
            return (GridViewColumnHeader)obj.GetValue(SortedColumnHeaderProperty);
        }

        private static void SetSortedColumnHeader(DependencyObject obj, GridViewColumnHeader value)
        {
            obj.SetValue(SortedColumnHeaderProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortedColumn.  This enables animation, styling, binding, etc...
        private static readonly DependencyProperty SortedColumnHeaderProperty =
            DependencyProperty.RegisterAttached("SortedColumnHeader", typeof(GridViewColumnHeader), typeof(GridViewSort), new UIPropertyMetadata(null));

        #endregion

        #region Column header click event handler

        private static void ColumnHeader_Click(object sender, RoutedEventArgs e)
        {
            GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
            if (headerClicked != null && headerClicked.Column != null)
            {
                string propertyName = GetPropertyName(headerClicked.Column);
                if (!string.IsNullOrEmpty(propertyName))
                {
                    ListView listView = GetAncestor<ListView>(headerClicked);
                    if (listView != null)
                    {
                        ICommand command = GetCommand(listView);
                        if (command != null)
                        {
                            if (command.CanExecute(propertyName))
                            {
                                command.Execute(propertyName);
                            }
                        }
                        else if (GetAutoSort(listView))
                        {
                            ApplySort(listView.Items, propertyName, listView, headerClicked);
                        }
                    }
                }
            }
        }

        #endregion

        #region Helper methods

        public static T GetAncestor<T>(DependencyObject reference) where T : DependencyObject
        {
            DependencyObject parent = VisualTreeHelper.GetParent(reference);
            while (!(parent is T))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }
            if (parent != null)
                return (T)parent;
            else
                return null;
        }

        public static void ApplySort(ICollectionView view, string propertyName, ListView listView, GridViewColumnHeader sortedColumnHeader)
        {
            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();

                GridViewColumnHeader currentSortedColumnHeader = GetSortedColumnHeader(listView);
                if (currentSortedColumnHeader != null)
                {
                    RemoveSortGlyph(currentSortedColumnHeader);
                }
            }
            if (!string.IsNullOrEmpty(propertyName))
            {
                view.SortDescriptions.Add(new SortDescription(propertyName, direction));
                if (GetShowSortGlyph(listView))
                    AddSortGlyph(
                        sortedColumnHeader,
                        direction,
                        direction == ListSortDirection.Ascending ? GetSortGlyphAscending(listView) : GetSortGlyphDescending(listView));
                SetSortedColumnHeader(listView, sortedColumnHeader);
            }
        }

        private static void AddSortGlyph(GridViewColumnHeader columnHeader, ListSortDirection direction, ImageSource sortGlyph)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(columnHeader);
            adornerLayer.Add(
                new SortGlyphAdorner(
                    columnHeader,
                    direction,
                    sortGlyph
                    ));
        }

        private static void RemoveSortGlyph(GridViewColumnHeader columnHeader)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(columnHeader);
            Adorner[] adorners = adornerLayer.GetAdorners(columnHeader);
            if (adorners != null)
            {
                foreach (Adorner adorner in adorners)
                {
                    if (adorner is SortGlyphAdorner)
                        adornerLayer.Remove(adorner);
                }
            }
        }

        #endregion

        #region SortGlyphAdorner nested class

        private class SortGlyphAdorner : Adorner
        {
            private GridViewColumnHeader _columnHeader;
            private ListSortDirection _direction;
            private ImageSource _sortGlyph;

            public SortGlyphAdorner(GridViewColumnHeader columnHeader, ListSortDirection direction, ImageSource sortGlyph)
                : base(columnHeader)
            {
                _columnHeader = columnHeader;
                _direction = direction;
                _sortGlyph = sortGlyph;
            }

            private Geometry GetDefaultGlyph()
            {
                double x1 = _columnHeader.ActualWidth - 13;
                double x2 = x1 + 10;
                double x3 = x1 + 5;
                double y1 = _columnHeader.ActualHeight / 2 - 3;
                double y2 = y1 + 5;

                if (_direction == ListSortDirection.Ascending)
                {
                    double tmp = y1;
                    y1 = y2;
                    y2 = tmp;
                }

                PathSegmentCollection pathSegmentCollection = new PathSegmentCollection();
                pathSegmentCollection.Add(new LineSegment(new Point(x2, y1), true));
                pathSegmentCollection.Add(new LineSegment(new Point(x3, y2), true));

                PathFigure pathFigure = new PathFigure(
                    new Point(x1, y1),
                    pathSegmentCollection,
                    true);

                PathFigureCollection pathFigureCollection = new PathFigureCollection();
                pathFigureCollection.Add(pathFigure);

                PathGeometry pathGeometry = new PathGeometry(pathFigureCollection);
                return pathGeometry;
            }

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

                if (_sortGlyph != null)
                {
                    double x = _columnHeader.ActualWidth - 13;
                    double y = _columnHeader.ActualHeight / 2 - 5;
                    Rect rect = new Rect(x, y, 10, 10);
                    drawingContext.DrawImage(_sortGlyph, rect);
                }
                else
                {
                    drawingContext.DrawGeometry(Brushes.LightGray, new Pen(Brushes.Gray, 1.0), GetDefaultGlyph());
                }
            }
        }

        #endregion
    }
}

I hope you’ll find that useful :)

Update: uploaded example project to demonstrate how to use the code

86 Comments

  1. Simon says:

    C”est superb!

    Merci bien for this article. I love the way you can do things like this with WPF. Just a note to others: if you find this article first, check out the earlier article to find out how to tell your GridView which columns should be sortable. The above samples are missing those bits of XAML, e.g.
    util:GridViewSort.PropertyName=”DateOfBirth”

  2. Alex says:

    For anyone interested, I changed the default sort glyph to be more subtle by replacing:

    drawingContext.DrawGeometry(Brushes.LightGray, new Pen(Brushes.Gray, 1.0), GetDefaultGlyph());

    with:

    drawingContext.DrawGeometry(new SolidColorBrush(Colors.LightGray) { Opacity = 0.5 },
    new Pen(Brushes.Gray, 0.5), GetDefaultGlyph());

  3. ppcanodehuelva says:

    Hi,

    Is there any way to use this feature by creating the GridView from c# code instead of XAML?

    Regards

  4. Colin says:

    I”m trying to sort an integer column and it is sorting as if it where a string. Will I need to alter the code or is there something I”m missing?
    thanks,
    Colin

    • Hi Colin,
      It works fine for me… are you sure that the property you specified for GridViewSort.PropertyName is of type int ?

      • Colin says:

        Sorry for the late reply Thomas. I”ve been too busy to get back to the code where I was use the int column. When I finally get back to it i”ll post an update. I”m sure it”s something on my side. Thanks for the awesome piece of code. It works great for the rest of the columns I used it on.

  5. Kaare says:

    I cannot get it to work. If i click the headers, they correctly switch between the two arrows. However, no sorting occurs at all.

    The item source for the listview is a LINQ IQueryable collection returned from MS SQL database.

    I set the GridViewSort.PropertyName to the name of the LINQ class property.

    So if i have a DB field named “Amount” i set GridViewSort.PropertyName=”Amount”.

    • Hi Kaare,

      I”m not sure it can work if the source is an IQueryable… I didn”t try. Try to call ToList() on your IQueryable collection before assigning it to the ItemsSource

      • Kaare says:

        You are right! It works perfectly if i convert it to a list.

        Thank you for your help and thanks for making this great class available

  6. Martin Cook says:

    Legend! Thank you.

  7. Tommaso says:

    Hi!
    I”m new to WPF and I”d like to sort data in my listview, your solution looks very smart! Unfortunately I can”t get it working…when I click on headers nothing happens.
    In my app I”m using MVVM pattern and the ListView is binded to a DataTable.
    I”d like to try using Command attached properties, but then in the viewmodel class how can I get GridViewSort making its magic stuff?
    The only method suitable seems to be ApplySort but it expects input from the view and I don”t have it in the viewmodel.
    I”m sure I”m missing something…maybe the problem is the DataTable?
    Thanks,
    Tommy

    • Hi Tommy,

      I just tried with a DataTable, it works fine for me. Did you set the AutoSort property to true on the ListView ? Did you set the PropertyName property on each column ? See my previous article on the subject for details.

      Regards,
      Thomas

      • Tommaso says:

        Hi Thomas,
        due to many other problems I”ve encountered following MVVM pattern, I”ve decided to abandon it and develop the “old school” way.
        It”s not the better way at all, I know. But I”ve a deadline and it”s my first WPF project.
        With no MVVM in the project I”m now able to sort listview columns using your class with no problems.
        Just a little suggestion more…which book do you advise to start learning WPF? Maybe “WPF Unleashed”?
        Many thanks for your help and good luck!
        Tommy

      • Hi Tommy,

        I”ve never read any book about WPF, so I couldn”t tell which one is the best. Have a look at that discussion on StackOverflow for advice ;)

        Regards,
        Thomas

  8. David says:

    Excellent helper class.
    One reccomendation – in ColumnHeader_Click after command.Execute(propertyName);
    and after
    ApplySort(listView.Items, propertyName, listView, headerClicked);
    add e.Handled = true; to prevent the column click from being passed up the other another ancestor. This caused me some grief when I put a list in a list with column sorting on both…

  9. enoya says:

    This Glyph which is lay on top of column header. in this case, the arrow will cover column header content. anyone any suggestion to sort this out?

  10. Sylvie Haddad says:

    Thank you so much for this very clever solution. I am new to WPF so I have no idea how you got this to work but I do intend to step through it 100 times until I understand it ….
    Sylvie

  11. Andrii V says:

    Great solution! The only drawback I discovered is when column width is too small to fit sort glyph and text, glyh is over the text, which looks quite bad.
    Did you also encounter the same issue?

    • Hi Andrii,

      Yes, I encountered that issue too. I usually solve it by making the column a bit wider. I intended to look for a better solution, but I didn”t find the time to do it…

      Regards,
      Thomas

  12. Dardino says:

    Yeah!! very good job! you are magician!

  13. Kik says:

    You”re a legend !

  14. Schedule says:

    Maybe you could change the page name [WPF] Automatically sort a GridView (continued) Thomas Levesque's .NET blog to something more generic for your blog post you write. I enjoyed the the writing however.

  15. Jason says:

    Sorry to sound like a complete newbie, but when I add your code to my project as a new class file and change the namespace to be the same as the rest of my project, I keep getting errors that the attachable properties for GridViewSort are not found. This is for PropertyName and AutoSort. Is there something else I have to do in order to use your sort class? These were errors in my Xaml.

    Thanks,

    Jason

    • Hi Jason,
      Is it a compile error, or just a designer error ? In the latter case, try to rebuild the project and reopen the designer

      • Jason says:

        Hi Thomas,

        I rebuilt the project and the following error appeared “util is an undeclared namespace. Line 95, position 103. XML is not valid”. This is in my .xaml code. My listview looks exactly like your example. Do I need some kind of codebehind declarations to enable this? I even tried adding .util to the namespace (which matches my other .cs files) to no avail.. Sorry, I”m pretty new to this but your sort program would be really useful to me..

        • Hi Jason,

          “util” is the XML namespace mapped to the C# namespace where the GridViewSort is declared. You need to add a xmlns attribute on the root of you XAML file :

          xmlns:util=&quot;clr-namespace:YourNamespace&quot;
  16. Turox says:

    thanks man. the code is awesome!

  17. Someone says:

    That code looks cool but what”s about more complex bindings?
    E.g.:

    That don”t work. :-(

  18. Someone says:

    That code looks cool but what”s about more complex bindings?
    E.g.:

    &lt;GridViewColumn Header=&quot;Some Info&quot; DisplayMemberBinding=&quot;{Binding Property.Subproperty, Mode=OneWay}&quot; util:GridViewSort.PropertyName=&quot;Property.Subproperty&quot;/&gt;

    That don”t work. :-(

    • Well, it should work… What doesn”t work for you exactly? The binding itself, or the sort? And were you able to make it work with a simpler binding? As you can see in the code, I”m not doing anything special with the property name, I”m just adding a new SortDescription to the CollectionView, which supports sorting on subproperties

  19. Dave says:

    Hi Thomas,

    Thanks so much for this, it is awesome! Do you have any hints to expand this control so it will stay sorted as data changes?

    • Hi Dave,

      It should stay sorted when the data changes, as long as the items implement INotifyPropertyChanged

      Regards,
      Thomas

      • Dave says:

        Hi Thomas,

        I”ve created classes that implement INotifyPropertyChanged, added these objects to an ObservableCollection in my view model and update the class properties using a Timer. The ListView sorts fine and properties are updated in my GUI, but it doesn”t re-sort with the updated properties. Not sure why not.

        • Hi Dave,

          I just tried, and you”re right, it doesn”t work… I thought CollectionViews handled that case automatically but it”s not the case, you need to refresh the view manually:

          CollectionViewSource.GetDefaultView(yourCollection).Refresh();
  20. Samuel Jack says:

    Thanks for this – just what I needed.

    This solution really highlights one of the benefits of WPF: its amazing composability. I can just drop your file into my project, then add a few properties to my own XAML, and it works. In the olden days of WinForms this level of re-usability was well-nigh impossible.

  21. samk says:

    Thanks mate.

  22. Marcos Sousa says:

    Hi Thomas, your Automatically sort GridView is really good, it help me a lot, thank you. I”ve implemented an improvement on it, on my code it”s possible to sort multiple columns. If you want the code to review and maybe public as one more continuation of this post =D, let me know.

    Best regards

    • Hi Marcos, thanks for your comment. Your improvement seems very interesting, I”d be glad to have a look at it!

      • IceFrog says:

        Hey i would also be glad to see this piece of code – maybe you can add this as “addon” or sth, so everyone can have a look into it?
        Thanks

      • Marcos Sousa says:

        Hi Thomas, sorry for the long time without any reply… I noticed today that the notify me of this topic was going to my spam box. I`m on vacation and will be back to office in 2 weeks. If you don`t have implement the multiple columns code I`ll send to you when I come back to work (the code is at my laptop office).
        Thank you

  23. Paul says:

    Thanks! If anyone is having an issue with the decorator covering up text, I fixed it with the following:

    When the glyph is being added:
    ContentPresenter c = (ContentPresenter)columnHeader.Template.FindName(“HeaderContent”, columnHeader);
    c.SetValue(Grid.ColumnSpanProperty, 1);

    When the glyph is being removed, the same two lines are used, but the ColumnSpan is one.

    Next, I pulled the GridViewColumnHeaderStyle from ExpressionBlend and stuck the ContentPresenter (named HeaderContent) into a grid:

  24. William says:

    Hi mate excellent solution! Do you think you could post a copy of the project for download?
    Cheers

  25. alainbryden says:

    Great project. I”ll be glad not to have to re-invent the wheel on this one.

  26. This project is going in my personal set of utilities. Fantastic job.

  27. x says:

    Thank you for that nice solution.
    I think it”s even better if you set
    IsHitTestVisible=false;
    in the constructor of the SortGlyphAdorner.

  28. LonesomeCoder says:

    I notice a severe memory consumption on EVERY sort!
    Do we have a memory leak here?

    • Hi LonesomeCoder,
      Thanks for bringing this to my attention. I”ll look into it as soon as possible.
      Regards,
      Thomas

    • Hi LonesomeCoder,
      I just checked with a memory profiler, and I didn”t notice any managed memory leak. I did notice an increase of unmanaged memory, but no more significant than when I manipulate the ListView without the GridViewSort behavior (just scrolling, or moving or resizing the columns has the same effect).
      How did you observe the increase of memory consumption? Did it disappear after garbage collection ? Also, note that Windows” Task Manager is not a reliable tool to monitor memory usage…

  29. huberthomas says:

    Hi,

    Is there a way to make this sort my list view after a new element was added? I have to click the header manually again once a new element was added to my data source.

    Regards,
    Thomas

    • Hi Thomas,
      It should be automatic if the source collection implement INotifyCollectionChanged (e.g. ObservableCollection)

      • huberthomas says:

        Hi,

        My source is a CollectionViewSource which uses as DefaultView an observable collection. The sort however is not auto updating I have to click the header after an entry was added to sort it properly.

        DeviceInfos = new ObservableCollection();
        DeviceInfosView = CollectionViewSource.GetDefaultView(DeviceInfos);

        DeviceInfosView is used as Binding for the ItemsSource of the ListView. The sorting works so far it just does not update the sorting once a new object DeviceInfo was added into the DeviceInfos collection.

      • huberthomas says:

        Update:

        I have investigated my issue a bit more and found out that the problem is that initially not all fields used to display within the list view are set. Once I ensured that the object was completely initialized it worked as expected. Thank you very much for sharing the code.

  30. J.R. says:

    So I have a property called Image of type ImageSource. However, when I try to sort via ImageSource, I get an error when it tries to add the property name to the view collection:
    view.SortDescriptions.Add(new SortDescription(propertyName, direction));

    Saying that I can”t compare these two objects. What”s the best way around this? Because I love your code, but I can”t seem to get sorting Image Source to work. Thanks. Here”s my column snippet:

    • Hi J.R.,
      Sorting images doesn”t make sense… how would you decide whether an image is “less than” or “greater than” another?

      • J.R. says:

        I only have 3 states. Error, Info, and Warning. All enum. So I could arbitrarily give the Error image perhaps the highest precedence, followed by Warning then Info. Each Image is in a dictionary mapping to a Enum type.

        • Sort on the enum then (exposed by a Status property for instance), not the image…

          • J.R. says:

            But the property I am passing to the grid column is an ImageSource type. I guess I”m too much of a beginner to understand how to bind to ImageSource, yet also sort on enum. Is there an intermediate step I”m missing?

          • The SortProperty doesn”t have to be the same as the displayed property. You could do something like this:

            &lt;GridViewColumn DisplayMemberPath=&quot;Image&quot; util:GridViewSort.SortProperty=&quot;Status&quot; /&gt;
            
          • J.R. says:

            Works like a charm! Thank you.

  31. Robin says:

    Is there any way to set the sorting through XAML or programmatically from C# so that the list will be sorted to begin with? (as if a certain column was clicked once during initialization)

    • Thomas Levesque says:

      Hi Robin,

      You can sort the list from code using the CollectionView:

      ICollectionView view = CollectionViewSource.GetDefaultView(collection);
      view.SortDescriptions.Add(new SortDescription("Property", ListSortDirection.Ascending));

      However it won”t show the sorting glyph… I think have an updated version of the code somewhere which handles this case, I”ll post it if I can put my hands on it.

      • Klaus Nji says:

        Great article. Thanks. I have made some adjustments to allow sorting to happen initially before any user interaction. It includes the following new DP properties:

        public static readonly DependencyProperty DataSourceProperty =
        DependencyProperty.RegisterAttached(
        “DataSource”,
        typeof(IEnumerable),
        typeof(GridViewSort),
        new UIPropertyMetadata(null,
        (o, e) =>
        {
        ListView listView = o as ListView;

        if (listView != null)
        {
        listView.ItemsSource = (IEnumerable)e.NewValue;
        if (GetAutoSort(listView)) // Don”t change click handler if AutoSort enabled
        {
        var gview = listView.View as GridView;
        if (gview != null && gview.Columns.Count > 0)
        {
        var header = gview.Columns[0].Header as GridViewColumnHeader;
        if (header != null && header.Column != null)
        {
        ApplySort(listView.Items, GetPropertyName(header.Column), listView, header);
        }
        }
        }
        }
        }
        )
        );

        public static IEnumerable GetDataSource(DependencyObject obj)
        {
        return (IEnumerable) obj.GetValue(DataSourceProperty);
        }

        public static void SetDataSource(DependencyObject obj, IEnumerable value)
        {
        obj.SetValue(DataSourceProperty, value);
        }

        and required change in the XAML to this:

        Last Name

        • Klaus Nji says:

          Change in the XAML:

          Last Name

          • Klaus Nji says:

            change in XAML requires use of DataSource property for initialization and usage of GridViewColumnHeader to define a header.

        • Thomas Levesque says:

          @Klaus, thanks for the suggestion.

          The main problem is that it will only work in a limited set of cases.
          - typically the value of the Header property is not a GridViewColumnHeader, it”s often just a string (of course it will be rendered as a GridViewColumnHeader in the visual tree, but the Header property doesn”t reflect that)
          - it will sort the first column, which may not be what you want, whether or not you already applied a sort to the source collection

      • Keri says:

        Great solution! Were you able to find in your code how you get the sorting glyph to show on the default sorted column? Thanks!

  32. Noel Dowling says:

    Great article. Worked like a charm. Cheers.

  33. Mikhail Afanasiev says:

    Great job! Very, very useful.

  34. Florian says:

    Hi I´ve read that it is possible to create the GridView with this feature from c# code instead of XAML.

    Could someone please post a code snippet of the implementation of this feature in c# code?

    I´m sure it´s simple and I am missing something, but i can´t get my code to work.

  35. Alessio says:

    Hi,

    In the MVVM contest I’m trying to resort with (the original sort) listview after an user click “search” button.

    CollectionViewSource.GetDefaultView(mylist).Refresh();
    or
    ICollectionView view = CollectionViewSource.GetDefaultView(NewAcquisti);
    view.SortDescriptions.Add(new SortDescription(“property”, ListSortDirection.Ascending));

    has no effect. Any idea?
    thanks

  36. Jesson says:

    how about for generated Columns via code? how can i use it code-behind?

    • Thomas Levesque says:

      You can just use the static methods from the GridViewSort class, for instance GridViewSort.SetAutoSort(theListView, true)

3 Trackbacks

Leave a comment

css.php