0

I have a Collection View Source (CVS) implemented much like you see in MSDN or many tutorials. In my case, a Car class, and a Cars Collection class which is shown in XAML via an Object Data Provider (ODP) The CVS is linked to this. It all works well.

I added a sort, then eventually got it to a stage where I could allow the user to select the property of the Car class to sort on.

Next I want to add a secondary sort. My problem is not adding the sort, but removing it. My problem is this. In my code, no secondary sort and happen (secondary control is disabled) unless a primary sort exists first. Lets say it does, now if I do a secondary sort, it works, but if I select another property to sort on, nothing happens. This is because a third sort is added, if I select another property, nothing happens (a fourth sort is added and so on).

I cant find the syntax anywhere that will let me remove the secondary sort applied last, before adding the next secondary sort.

Given there are only ever two items - Primary sort [0] and Secondary sort [1], I should be able to use code like:

lstCars.Items.SortDescriptions.RemoveAt(1);

but this doesnt work and even empties my combo boxes of selection items (not my actual question).

I am trying something like this:

lstCars.Items.SortDescriptions.Remove(new SortDescription(cbxSortSecondary.SelectedItem.ToString(), direction));

To remove an actual Item from the source that I know exists because it was put there when selected from the secondary combobox. But while this should be working, it for some reason is not.

Below is some of my code:

    private void cbxSortPrimary_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        //MessageBox.Show(((ComboBox)sender).Name);

        //Code to check if sorting is required or not, if so then do it.
        if(cbxSortPrimary.SelectedIndex == 0)//Sort Off
        {
            txtPrimary.Foreground = Brushes.Green;
            lstCars.Items.SortDescriptions.Clear();
            cbxSortSecondary.IsEnabled = false;
            chkSortSecAsc.IsEnabled = false;
        }
        else//Sort On
        {
            txtPrimary.Foreground = Brushes.Red;
            ApplyPrimarySort((bool)chkSortPriAsc.IsChecked);
            cbxSortSecondary.IsEnabled = true;
            chkSortSecAsc.IsEnabled = true;
        }
    }

    private void cbxSortSecondary_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        //If there is no primary sort just exit the method.
        if(cbxSortPrimary.SelectedIndex == 0) return;

        if(cbxSortSecondary.SelectedIndex == 0)//Sort Off
        {
            txtSecondary.Foreground = Brushes.Green;
            RemoveSecondarySort((bool)chkSortSecAsc.IsChecked);
        }
        else//Sort On
        {
            txtSecondary.Foreground = Brushes.Red;
            ApplySecondarySort((bool)chkSortSecAsc.IsChecked);
        }
    }

    private void chkSortPriAsc_Checked(object sender, RoutedEventArgs e)
    {
        //Check to see if list is null, if so then exit (nothing to sort).
        if(lstCars == null) return;

        if(cbxSortPrimary.SelectedIndex > 0)
        {
            if(chkSortPriAsc.IsChecked == true) //Sort Ascending
            {
                chkSortPriAsc.Foreground = Brushes.Green;
                chkSortPriAsc.Content = "Asc";
                ApplyPrimarySort((bool)chkSortPriAsc.IsChecked);
            }
            else //Sort Decending
            {
                chkSortPriAsc.Foreground = Brushes.Red;
                chkSortPriAsc.Content = "Dec";
                ApplyPrimarySort((bool)chkSortPriAsc.IsChecked);
            }
        }
    }


    private void chkSortSecAsc_Checked(object sender, RoutedEventArgs e)
    {
        //Check to see if list is null, if so then exit (nothing to sort).
        if(lstCars == null) return;

        //If there is no primary sort just quit.
        if(cbxSortPrimary.SelectedIndex == 0) return;

        if(cbxSortSecondary.SelectedIndex > 0)
        {
            if(chkSortSecAsc.IsChecked == true) //Sort Ascending
            {
                chkSortSecAsc.Foreground = Brushes.Green;
                chkSortSecAsc.Content = "Asc";
                ApplySecondarySort((bool)chkSortPriAsc.IsChecked);
            }
            else //Sort Decending
            {
                chkSortSecAsc.Foreground = Brushes.Red;
                chkSortSecAsc.Content = "Dec";
                ApplySecondarySort((bool)chkSortPriAsc.IsChecked);
            }
        }
    }

    private void ApplyPrimarySort(bool asc)
    {
        ListSortDirection direction = new ListSortDirection();
        //Next determine if the direction of the sort is Ascending or Decending.
        if(asc)
            direction = ListSortDirection.Ascending;
        else
            direction = ListSortDirection.Descending;

        //Finally get the property to be sorted on and apply the sort, else remove any existing sorts.
        lstCars.Items.SortDescriptions.Clear();
        lstCars.Items.SortDescriptions.Add(new SortDescription(cbxSortPrimary.SelectedItem.ToString(), direction));

        //Then refresh the view.
        CollectionViewSource.GetDefaultView(lstCars.ItemsSource).Refresh();
    }

    private void ApplySecondarySort(bool asc)
    {
        ListSortDirection direction = new ListSortDirection();
        //Next determine if the direction of the sort is Ascending or Decending.
        if(asc)
            direction = ListSortDirection.Ascending;
        else
            direction = ListSortDirection.Descending;

        lstCars.Items.SortDescriptions.Remove(new SortDescription(cbxSortSecondary.SelectedItem.ToString(), direction));
        lstCars.Items.SortDescriptions.Add(new SortDescription(cbxSortSecondary.SelectedItem.ToString(), direction));

        //Then refresh the view.
        CollectionViewSource.GetDefaultView(lstCars.ItemsSource).Refresh();
    }


    private void RemoveSecondarySort(bool asc)
    {
        ListSortDirection direction = new ListSortDirection();
        //Next determine if the direction of the sort is Ascending or Decending.
        if(asc)
            direction = ListSortDirection.Ascending;
        else
            direction = ListSortDirection.Descending;

        lstCars.Items.SortDescriptions.Remove(new SortDescription(cbxSortSecondary.SelectedItem.ToString(), direction));
    }

Esentially I am looking for the code I need to first remove the previous secondary sort, then add a new one so there is always only two sorts in the collection view source. If I were to then expand this to allow a third, fourth or whatever other level of sorting there would always only be that number of items in the list. However, because of the way I have it set up, a 3rd level cant exist unless a second level exists first. So there can be no mix up of sorts.

Any ideas on how to implement this would be appreicated.

Francis Rodgers
  • 4,565
  • 8
  • 46
  • 65

1 Answers1

1

The way you are removing the items is incorrect: the Remove method that you are using removes the specified object from the collection (specifically, the first instance of the specified object), but since you are passing a new object, it won't be in the collection and thus won't be removed.

Your best bet is to cycle through the collection and see which item meets your criteria for removal and remove that item.

For example:

        var sortDescriptions = lstCars.Items.SortDescriptions;

        for (int nI = sortDescriptions.Count; nI >= 0; nI--)
        {
            if (sortDescriptions[nI].PropertyName == cbxSortSecondary.SelectedItem.ToString())
            {
                sortDescriptions.RemoveAt(nI);
            }
        }

Update

The alternative to this line:

                sortDescriptions.RemoveAt(nI);

is to use this line, but they should be functionally equivalent:

                sortDescriptions.Remove(sortDescriptions[nI]);
competent_tech
  • 44,465
  • 11
  • 90
  • 113
  • This is what I was thinking. A NEW SortDescription doesnt make sense. So how can I do as you sugested. i.e. I have a primary sort on car Maker property. A secondary sort on Price property. The remove method requires me to give it a SortDescription. So how do I get the identity of the SortDescription I want to remove. (in this case the secondary). Thanks for answering. – Francis Rodgers Jan 02 '12 at 04:59
  • 1
    @FrancisRodgers: That's exactly what the code I answered with does: it loops until it finds the item with the same property name as the secondary sort. – competent_tech Jan 02 '12 at 05:22
  • Sorry, I answered too quickly and didnt see your code. Apologies and thanks again for your help. – Francis Rodgers Jan 02 '12 at 05:56
  • The only reason I have not yet marked it as the accepted answer is because it causes my combo boxes to go blank, See part 2. But thanks for answering what you have so far. – Francis Rodgers Jan 03 '12 at 00:35
  • 1
    Sorry, unclear on the comboboxes going blank. Did you edit your question and add something or was this a part of the original question? If you edited it, it could have been rejected as too much of a modification to the original question. I don't see it in the review list, but that doesn't mean anything. You may need to open a new question if it is considered a new problem. – competent_tech Jan 03 '12 at 01:35
  • I think I edited and added the last paragraph, but when writing the origional question I did put the following:---> Given there are only ever two items - Primary sort [0] and Secondary sort [1], I should be able to use code like: lstCars.Items.SortDescriptions.RemoveAt(1); but this doesnt work and even empties my combo boxes of selection items (not my actual question). <--- But I do appreicate your answer so far, and I apologise if I seem a little ungrateful, I am just hoping for other answers to come along that may do it without RemoveAt(). – Francis Rodgers Jan 03 '12 at 18:38
  • 1
    Unfortunately, I don't think you are going to find any other way to remove the items and, even if one of the other remove methods worked, it would most likely result in the same behavior since the remove methods internally all end up calling the exact same code. I have updated the answer to show how to use Remove instead of RemoveAt, which should show the same behavior. Although why it clears the comboboxes completely stumps me. You may want to consider opening another question to see if anyone else has ideas on that. – competent_tech Jan 03 '12 at 19:35
  • I opened a part 2 yesterday --> http://stackoverflow.com/questions/8700632/remove-a-sort-from-collectionviewsource-c-sharp-part-2 <-- Once again, thanks for your help. – Francis Rodgers Jan 03 '12 at 20:41