LiveShaping – repositioning elements when its bound data changes

With WPF 4.0, when an item in a collection was added or removed, the CollectionView interface that it belonged to had its filtering, sorting, and ordering updated. However, this did not happen when we modified one of its item's properties.

Now, with WPF 4.5, we can implement this behavior in real-time with the new ICollectionViewLiveShaping interface, so if the data is updated, so will its filtering, sorting, and ordering. Let's see how it's done.

Getting ready

In order to use this recipe you should have Visual Studio 2012 installed.

How to do it...

Here we are going to see how to implement the ICollectionViewLiveShaping interface to make our collection update its sorting of the collection of data and will make it change over time as well.

  1. First open Visual Studio 2012 and create a new project. We will select the WPF Application template from the Visual C# category and name it WPFLiveShaping.
  2. We will add the BaseClass.cs from the Implementing asynchronous error handling with INotifyDataErrorInfo recipe, which provides an implementation of the INotifyDataErrorInfo and the INotifyPropertyChanged interfaces.
  3. Add a class and name it BookModel.cs, adding the following code:
    public class BooksModel : BaseClass
    {
        private string name;
        public string Name
        {
            get { return name; }
            set 
            { 
                name = value;
                OnPropertyChanged("Name");
            }
        }
    
        private String isbn;
        public String ISBN
        {
            get { return isbn; }
            set { 
                isbn = value;
                OnPropertyChanged("ISBN");
            }
        }
    
        private Double bookPrice;
        public Double BookPrice
        {
            get { return bookPrice; }
            set { 
                bookPrice = value;
                OnPropertyChanged("BookPrice");
            }
        }
    }
  4. Add the following code to the MainWindow.xaml.cs class:
    public partial class MainWindow : Window
    {
        public ObservableCollection<BooksModel> myBooks { get; set; }
        DispatcherTimer dt = new DispatcherTimer();
        public ICollectionViewLiveShaping cvls;
        public MainWindow()
        {
            InitializeComponent();
    
            InitializeData();
            BindData();
            StartUpdatingData();
        }
    
        private void InitializeData()
        {
            myBooks = new ObservableCollection<BooksModel>();
            Random r = new Random(DateTime.Now.Millisecond);
    
            for (int i = 0; i < 15; i++)
            {
                BooksModel bm = new BooksModel() { 
                        BookPrice = r.Next(1, 10),
                        ISBN = i.ToString(),
                        Name = "Book Nº" + i.ToString()
                };
    
                myBooks.Add(bm);
            }
        }
    
        private void BindData()
        {
            cvls = (ICollectionViewLiveShaping)CollectionViewSource.GetDefaultView(myBooks);
            cvls.IsLiveSorting = true;
            liveShapingDataGrid.ItemsSource = (IEnumerable)cvls;
        }
    
        private void StartUpdatingData()
        {
            dt.Tick += dt_Tick;
            dt.Interval = new TimeSpan(0, 0, 0, 0, 500);
            dt.Start();
        }
        void dt_Tick(object sender, EventArgs e)
        {
            foreach (BooksModel bm in myBooks)
            {
                Random r = new Random(DateTime.Now.Millisecond);
                bm.BookPrice = r.Next(1, 10);
            }
        }
    }
  5. On the MainWindow.xaml class, we will add two DataGrid controls that split the view vertically with the following XAML code:
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <DataGrid x:Name="liveShapingDataGrid"
                    AutoGenerateColumns="False" 
                    IsReadOnly="True" Margin="0,30,0.4,0.4" Grid.RowSpan="2">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="90"/>
                <DataGridTextColumn Header="ISBN" Binding="{Binding ISBN}" Width="60"/>
                <DataGridTextColumn Header="Price" Binding="{Binding BookPrice}" Width="50" />                                
            </DataGrid.Columns>
        </DataGrid>
        <Label Content="DataGrid With LiveShaping" FontWeight="Bold" Margin="0,0,0.4,0.4"/>
    </Grid>
  6. Execute the application and click on the Price header on the DataGrid, so it gets ordered in an ascending way. Notice that the ordering of the elements is updated live as the values change, as shown in the following screenshot:
    How to do it...

How it works...

We started by creating a BooksModel class with a double BookPrice property.

Next, in the MainWindow.xaml code, we created an ObservableCollection interface of BooksModel entities, which we initialized and populated with a few elements on the InitializeData() method.

We did our magic on the BindData() method, getting the default CollectionView from our ObservableCollection and casting it as ICollectionViewLiveShaping, with the following instruction:

cvls = (ICollectionViewLiveShaping)CollectionViewSource.GetDefaultView(myBooks);

We enabled the real-time sorting for the resulting ICollectionViewLiveShaping interface. Next, we set it as ItemsSource of DataGrid, casting this CollectionViewLiveShaping interface into an IEnumerable.

We then created the StartUpdatingData() method, configuring DispatcherTimer to run every half second and update the price of the books, so we could see some action, live from DataGrid. The method simply runs over all the BooksModel elements and updates their price with a random generated value.

There is nothing special about the view, we just created DataGrid to display the three properties of BookModel.

Finally, we executed the application, ordered the DataGrid by its price header, and let the real-time ordering happen live. We could observe how the ordering reacts to the property changes taking place every half second through our dispatcher timer.

There's more...

It would be interesting to explore the other options of the ICollectionViewLiveShaping interface, such as IsLiveFiltering and IsLiveGrouping.

We also have the option of changing how the collection behaves. It is possible to deactivate its LiveFiltering, LiveGrouping, and LiveSorting options with the CanChangeLiveFiltering, CanChangeLiveSorting, and CanChangeLiveGrouping properties.

This interface is implemented by the ListCollectionView, BindingListCollectionView, ItemCollection, and CollectionViewSource classes by default, thanks to WPF 4.5.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset