2

I have a silverlight 4 TreeView control with a hierarchy of data. I want the items in each level to be sorted alphabetically, so I am using a CollectionViewSource, but don't actually care how the sorting is accomplished.

The CollectionViewSource seems to observe CollectionChanged events, so the sorting works fine as items are added and removed.

The CollectionViewSource does not observe changes to the property being sorted upon, so when the item's text changes, the sorting is not maintained. Calling CollectionViewSource.View.Refresh() re-sorts the list, but discards the selection. How can I keep the TreeView selection without losing and re-setting the selection?

Sample Project:

Description:

This project creates a single-level tree of items. Each item is given an item number, and a numeric prefix to make the sorting actually do something interesting. The buttons will add an item, remove the oldest item, and rename the oldest item.

Building the sample:

  • Create a new silverlight application named "SortTest"
  • Add a reference to System.Windows.Controls (for the treeview)
  • Update the following files:

Behavior to note:

  • The current selection is maintained as items are added and removed.
  • The current selection is lost when an item is renamed (when Refresh() is called from within OnRenameButtonClick()).
  • If the call to Refresh() is removed, the selection is maintained when renaming an item, but the list is not re-sorted to account for the name change.

MainPage.xaml

<UserControl x:Class="SortTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <UserControl.Resources>
        <Style x:Key="expandedStyle" TargetType="sdk:TreeViewItem">
            <Setter Property="IsExpanded" Value="true" />
        </Style>

        <sdk:HierarchicalDataTemplate x:Key="template">
            <TextBlock Text="{Binding Name}" />
        </sdk:HierarchicalDataTemplate>
    </UserControl.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <Button Click="OnAddButtonClick">
                <TextBlock Text="Add an item" />
            </Button>
            <Button Click="OnRemoveButtonClick">
                <TextBlock Text="Remove lowest numbered item" />
            </Button>
            <Button Click="OnRenameButtonClick">
                <TextBlock Text="Rename lowest numbered item" />
            </Button>
        </StackPanel>

        <sdk:TreeView Grid.Row="1" ItemsSource="{Binding Items}" ItemTemplate="{StaticResource template}" />

    </Grid>
</UserControl>

MainPage.xaml.cs

using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Data;
using System.ComponentModel;
using System;
using System.Collections.Specialized;

namespace SortTest
{
    public partial class MainPage : UserControl
    {
        private ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>();
        private CollectionViewSource sortedItems = new CollectionViewSource();
        private int itemNumber = 1;

        public MainPage()
        {
            sortedItems.Source = items;
            sortedItems.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));

            DataContext = this;
            InitializeComponent();
        }

        public ICollectionView Items { get { return sortedItems.View; } }

        private void OnAddButtonClick(object sender, RoutedEventArgs e)
        {
            ItemViewModel item = new ItemViewModel();
            item.Name = DateTime.Now.Millisecond.ToString("D3") + " Item #" + itemNumber;
            itemNumber++;
            items.Add(item);
        }

        private void OnRemoveButtonClick(object sender, RoutedEventArgs e)
        {
            if (items.Count > 0)
            {
                items.RemoveAt(0);
            }
        }

        private void OnRenameButtonClick(object sender, RoutedEventArgs e)
        {
            if (items.Count > 0)
            {
                items[0].Name = DateTime.Now.Millisecond.ToString("D3") + items[0].Name.Substring(3);
                sortedItems.View.Refresh();
            }
        }
    }

    public class ItemViewModel : DependencyObject
    {
        public static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(ItemViewModel), null);
        public string Name
        {
            get { return GetValue(NameProperty) as string; }
            set { SetValue(NameProperty, value); }
        }
    }
}

Thanks!

James Barber
  • 73
  • 1
  • 5

1 Answers1

0

persist the selected item into the isolatedStorage and reselect it after your treeview is reloaded.

user753503
  • 48
  • 5