How can I sort a ListBox using only XAML and no code-behind?

Viewed 32242

I need to sort the strings in a ListBox, but it is bound to the view model by another component via the DataContext. So I can't directly instantiate the view model in XAML, as in this example, which uses the ObjectDataProvider.

In my XAML:

<ListBox ItemsSource="{Binding CollectionOfStrings}" />

In my view model:

public ObservableCollection<string> CollectionOfStrings
{
    get { return collectionOfStrings; }
}

In another component:

view.DataContext = new ViewModel();

There is no code behind! So using purely XAML, how would I sort the items in the ListBox? Again, the XAML doesn't own the instantiation of the view model.

2 Answers

If attached property qualifies as no code the following can be used:

public static class Sort
{
    public static readonly DependencyProperty DirectionProperty = DependencyProperty.RegisterAttached(
        "Direction",
        typeof(ListSortDirection?),
        typeof(Sort),
        new PropertyMetadata(
            default(ListSortDirection?),
            OnDirectionChanged));

    [AttachedPropertyBrowsableForType(typeof(ItemsControl))]
    public static ListSortDirection? GetDirection(ItemsControl element)
    {
        return (ListSortDirection)element.GetValue(DirectionProperty);
    }

    public static void SetDirection(ItemsControl element, ListSortDirection? value)
    {
        element.SetValue(DirectionProperty, value);
    }

    private static void OnDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is ItemsControl { Items: { } items })
        {
            if (e.NewValue is ListSortDirection direction)
            {
                items.SortDescriptions.Add(new SortDescription(string.Empty, direction));
            }
            else if (e.OldValue is ListSortDirection old &&
                     items.SortDescriptions.FirstOrDefault(x => x.Direction == old) is { } oldDescription)
            {
                items.SortDescriptions.Remove(oldDescription);
            }
        }
    }
}

Then in xaml:

<ListBox local:Sort.Direction="Ascending"
         ... />

Another attached property that is of type SortDescription probably makes sense in many cases.

Related