Build Your Own Navigation Bar for Data Navigation in WPF

You may have known that drag-drop data-binding experience has been introduced to WPF application development story from VS2010 Beta1, thanks to a lot of posts before this one. The whole experience in WPF is pretty similar to what we’ve already had with WinForms: developers configure the bindings in Data Source Window and associate controls with data entities or properties, and drag-and-drop from Data Source Window to the designer generates controls with data binding set in the code. All looks familiar. But wait a second, you may find there is still something missing if you tend to generate a DataGrid or DetailsView for a data entity. You are right. In WPF, there’s no navigation bar facility, so drag-and-drop will only leave the exact control you are binding with the entity. However, using WPF, you will find it’s easy to do data navigation, thanks to the CollectionViewSource. So why not build your own one and make it as whatever you like?

In this article, we will build a simple navigation bar leveraging CollectionViewSource. Below is what it looks like:

clip_image002

To start, we use drag-drop WPF data-binding feature in VS2010 Beta 1 to generate a Details View of Employee entity. We use EDM here as data source. Let’s update the generated code behind a little bit and declare the entity, the CollectionViewSource, and the query in a class wide. We also need to declare an int field member to get the total row counts of an entity, which looks like below:

    public partial class Window1 : Window

    {

        private WpfDataNavigationApplication.NorthwindEntities northwindEntities = new WpfDataNavigationApplication.NorthwindEntities();

        private System.Windows.Data.CollectionViewSource employeesViewSource__NorthwindEntities;

        private System.Data.Objects.ObjectQuery<WpfDataNavigationApplication.Employee> employeesQuery;

      private int totalRowCount;

 

        public Window1()

        {

            InitializeComponent();

        }

        private System.Data.Objects.ObjectQuery<Employee> GetEmployeesQuery(NorthwindEntities northwindEntities)

        {

            // Auto generated code

            System.Data.Objects.ObjectQuery<WpfDataNavigationApplication.Employee> employeesQuery = northwindEntities.Employees;

           

            // Returns an ObjectQuery.

            return employeesQuery;

        }

        private void Window_Loaded(object sender, RoutedEventArgs e)

        {

           employeesViewSource__NorthwindEntities = ((System.Windows.Data.CollectionViewSource)(this.FindResource("employeesViewSource__NorthwindEntities")));

           employeesQuery = this.GetEmployeesQuery(northwindEntities);

           employeesViewSource__NorthwindEntities.Source = employeesQuery.Execute(System.Data.Objects.MergeOption.AppendOnly);

        }
}

Then it’s the time to build the navigation bar! Let’s start with UI design and make something like this:

clip_image004

From the left to the right, they are buttons for navigation to the first row, the previous row, the next row, and the last row; a text box for users to input the index of the row for navigation; a label to show how many row in all; and a button for navigation to the specific row based on what users input into the text box. We also add event handler for each button. The XAML of this navigation bar looks like below:

        <StackPanel Height="28" HorizontalAlignment="Left" Margin="10,12,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="206" Orientation="Horizontal">

            <Button Content="|&lt;&lt;" Height="23" Name="goToFirstButton" Width="Auto" FontWeight="Bold" Background="BlanchedAlmond" Click="goToFirstButton_Click"></Button>

            <Button Content="&lt;" Height="23" Name="goToPreviousButton" Width="Auto" FontWeight="Bold" Background="BlanchedAlmond" Click="goToPreviousButton_Click"></Button>

            <Button Content="&gt;" Height="23" Name="goToNextButton" Width="Auto" FontWeight="Bold" Background="BlanchedAlmond" Click="goToNextButton_Click"></Button>

            <Button Content="&gt;&gt;|" Height="23" Name="goToLastButton" Width="Auto" FontWeight="Bold" Background="BlanchedAlmond" Click="goToLastButton_Click"></Button>

            <TextBox Height="23" Name="goTextBox" Width="37" />

            <Label Content="of " Height="28" Name="label1" Width="20" />

            <Label Height="28" Name=" totalRowCount" Width="25" />

            <Button Content="Go!" Height="23" Name="goButton" Width="Auto" Background="BlanchedAlmond" FontWeight="Bold" Click="goButton_Click"></Button>

        </StackPanel>

And in the code behind, we get a set of event handler methods for each button’s Click event.

Now it’s the time to implement navigation! Still remember we load data into a CollectionViewSource? Now it’s the time to leverage its power. CollectionViewSource is actually a XAML proxy for CollectionView class, which represents a View for grouping, sorting, filtering and navigating a data collection. The View provides a currency point and allows you to move the pointer. As a proxy of CollectionView class, CollectionViewSource has a View property, which is a dependency property. We’ll use this to move the currency and do navigation. So, for each Click event handler, the implementation is somewhat like below:

      private void GoToFirstButton_Click(object sender, RoutedEventArgs e)

        {

            employeesViewSource_NorthwindEntities.View.MoveCurrentToFirst();

        }

        private void GoToPreviousButton_Click(object sender, RoutedEventArgs e)

       {

  employeesViewSource_NorthwindEntities.View.MoveCurrentToPrevious();

        }

        private void GoToNextButton_Click(object sender, RoutedEventArgs e)

        {

  employeesViewSource_NorthwindEntities.View.MoveCurrentToNext();

        }

        private void GoToLastButton_Click(object sender, RoutedEventArgs e)

        {

  employeesViewSource_NorthwindEntities.View.MoveCurrentToLast();

        }

        private void GoButton_Click(object sender, RoutedEventArgs e)

  {

            int position;

         int.TryParse(goTextBox.Text, out position);

            if (position > 0 && position <= totalRowCount)

{

employeesViewSource_NorthwindEntities.View.MoveCurrentToPosition(position - 1);

             }

            else

            {

                MessageBox.Show("The input index is not valid.");

            }

        }

To get the total row counts, we need to use the Count method in entity’s ObjectSet. Let’s put the following code into the Window_Loaded method:

       totalRowCount = northwindEntities.Employees.Count();

        totalRowCountLabel.Content = totalRowCount;

So far, we’ve already had our navigation work. Pretty simple, isn’t it? To make it even better, let’s do something more on the button, and disable the button when it doesn’t make sense to click it. We’ll add a new method here to set buttons’ ability:

        private void SetButtonIsEnabled()

        {

           bool isFirst = (employeesViewSource__NorthwindEntities.View.CurrentPosition == 0);

           bool isLast = employeesViewSource__NorthwindEntities.View.CurrentPosition > (totalRowCounts - 1);

            goToFirstButton.IsEnabled = goToPreviousButton.IsEnabled = !isFirst;

            goToNextButton.IsEnabled = goToLastButton.IsEnabled = !isLast;

        }

Also, we’d like the index input text box always shows the corresponding index number for the current selection. So we need another method here:

        private void SetGoTextBoxOnCurrentPosition()

        {

            goTextBox.Text = (employeesViewSource__NorthwindEntities.View.CurrentPosition + 1).ToString();

        }

Then we can call these two methods in the Click event handler methods (or wrap these two methods into a new method, say UpdateNavigatorUI, and call the method), and we’ll get a complete functionality of a basic navigation bar. The full code can be found here.

Based on that, we can add even more features into our navigation bar. To make it more powerful, you can also add CRUD functionality into it. However, hmm… In this article, the navigation bar we build only work for one page and one entity. There still some work we can do to make this useful utility more reusable. How we can do that? Bingo! Building a user control. And we will talk about that in the future post. Wait and see!