Datagrid server side paging control

In one of the previous post I have explained about the client side pager control in silverlight and its usage;

 

 

 


 

 

 

 This tutorial is for those who are not using the Ria Services.

Along with this, I have created a helper control (a user control) with a collection of sub controls, which includes a search textbox, a datagird and a custom pager control. We need to provide a table name, required column names and filter (if there are additonal conditions apart from searching text on required column names). Done!!! This will load data to the datagrid based on the search text with a default server side paging. I have made it as generic as possible. But, you need a method in your web service that returns the xml of the result and it should be deserializable. The method is as follows. Change AppConstants.ConnectionString to desired connection string.

   [OperationContract]
        public string SelectQuery(string query, string entityName)
        {
            string xmlData;

            try
            {
                using (var connection = new SqlConnection(AppConstants.ConnectionString))
                {
                    connection.Open();
                    using (var command = new SqlCommand(query, connection))
                    {
                        var dataset = new DataSet("ArrayOf" + entityName);
                        using (var adapter = new SqlDataAdapter(command))
                        {
                            adapter.Fill(dataset, entityName);
                        }

                        xmlData = dataset.GetXml();
                    }

                    connection.Close();
                }
            }
            catch (Exception ex)
            {
                xmlData = "Failed: " + ex.Message;
            }

            return xmlData;
        }

Now, lets see the pager control. Nothing complicated. This is just a simple control that provides a PageChanged event for the developers to get the right page and thus, get the data from the server for that page. A PageIndex property which can be binded, will provide the page change for the view model pattern. A total count of pages should be set for the pager control to behave properly. Too traditional, but effective.

 

 

 

 


 

 

 

 

 

XAML for the control

  <Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <!--First button-->
            <ColumnDefinition Width="25"/>
            <!--Previous button-->
            <ColumnDefinition Width="25"/>
            <ColumnDefinition Width="5"/>
            <ColumnDefinition Width="35"/>
            <!--Current page textBox-->
            <ColumnDefinition Width="35"/>
            <ColumnDefinition Width="15"/>
            <!--Total page count (9999 can be the max value) -->
            <ColumnDefinition Width="30"/>
            <ColumnDefinition Width="5"/>
            <!--Next button-->
            <ColumnDefinition Width="25"/>
            <!--Last button-->
            <ColumnDefinition Width="25"/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" Style="{StaticResource FirstButtonStyle}" mce_Style="{StaticResource FirstButtonStyle}" mce_Style="{StaticResource FirstButtonStyle}" mce_Style="{StaticResource FirstButtonStyle}" mce_Style="{StaticResource FirstButtonStyle}" mce_Style="{StaticResource FirstButtonStyle}" Margin="2" x:Name="FirstButton" 
                Click="OnButtonClick" Tag="First"/>
        <Button Grid.Column="1" Style="{StaticResource PreviousButtonStyle}" mce_Style="{StaticResource PreviousButtonStyle}" mce_Style="{StaticResource PreviousButtonStyle}" mce_Style="{StaticResource PreviousButtonStyle}" mce_Style="{StaticResource PreviousButtonStyle}" mce_Style="{StaticResource PreviousButtonStyle}" Margin="2" 
                x:Name="PreviousButton" Click="OnButtonClick" Tag="Previous"/>
        <Path Grid.Column="2" Data="M0,1 L0,10" Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" Margin="2"/>
        <TextBlock Grid.Column="3" Text="Page" Margin="2,4,2,2" MaxWidth="30"/>
        <TextBox Grid.Column="4" x:Name="PageNumberTextBox" Style="{StaticResource PagerTextBoxStyle}" mce_Style="{StaticResource PagerTextBoxStyle}" mce_Style="{StaticResource PagerTextBoxStyle}" mce_Style="{StaticResource PagerTextBoxStyle}" mce_Style="{StaticResource PagerTextBoxStyle}" mce_Style="{StaticResource PagerTextBoxStyle}" 
                 Margin="2" xamlServices:TextBoxFilterService.Filter="PositiveInteger" MaxLength="4" 
                 LostFocus="OnTextLostFocus"/>
        <TextBlock Grid.Column="5" Text="of" Margin="2,4,2,2" MaxWidth="30"/>
        <TextBlock Grid.Column="6" x:Name="TotalPageNumberLabel" Margin="2,4,2,2"/>
        <Path Grid.Column="7" Data="M0,1 L0,10" Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" mce_Style="{StaticResource VerticalPathStyle}" Margin="2"/>
        <Button Grid.Column="8" Style="{StaticResource NextButtonStyle}" mce_Style="{StaticResource NextButtonStyle}" mce_Style="{StaticResource NextButtonStyle}" mce_Style="{StaticResource NextButtonStyle}" mce_Style="{StaticResource NextButtonStyle}" mce_Style="{StaticResource NextButtonStyle}" Margin="2" x:Name="NextButton" 
                Click="OnButtonClick" Tag="Next"/>
        <Button Grid.Column="9" Style="{StaticResource LastButtonStyle}" mce_Style="{StaticResource LastButtonStyle}" mce_Style="{StaticResource LastButtonStyle}" mce_Style="{StaticResource LastButtonStyle}" mce_Style="{StaticResource LastButtonStyle}" mce_Style="{StaticResource LastButtonStyle}" Margin="2" x:Name="LastButton" 
                Click="OnButtonClick" Tag="Last"/>
    </Grid>

You can use your own style for first, previous, next and last buttons. I created a similar style as that of existing pager control in silverlight. The code behind for the control is as follows.

  using System;
    using System.Windows;
    using System.Windows.Controls;

    /// <summary>
    /// Difines a custom pager control to implement server side paging
    /// </summary>
    public partial class CustomPager : UserControl
    {
        #region Dependency Properties

        /// <summary>
        /// Using a DependencyProperty as the backing store for PageIndex.
        /// </summary>
        public static readonly DependencyProperty PageIndexProperty =
            DependencyProperty.Register("PageIndex", typeof(int), typeof(CustomPager), 
            new PropertyMetadata((sender, e) => ((CustomPager)sender).PageNumberTextBox.Text = Convert.ToString(e.NewValue)));

        /// <summary>
        /// Using a DependencyProperty as the backing store for PageCount. 
        /// Total pages should be set to this property
        /// </summary>
        public static readonly DependencyProperty PageCountProperty =
            DependencyProperty.Register("PageCount", typeof(int), typeof(CustomPager), 
            new PropertyMetadata((sender, e) => ((CustomPager)sender).OnPageChange()));

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomPager"/> class
        /// </summary>
        public CustomPager()
        {
            InitializeComponent();
        }

        #endregion

        /// <summary>
        /// Action to trigger the page changed event
        /// </summary>
        public event Action PageChanged;

        #region Public Properties - For Dependency

        /// <summary>
        /// Gets or sets the value of PageCount
        /// </summary>
        public int PageCount
        {
            get { return (int)GetValue(PageCountProperty); }
            set { SetValue(PageCountProperty, value); }
        }

        /// <summary>
        /// Gets or sets the value of PageIndex
        /// </summary>
        public int PageIndex
        {
            get { return (int)GetValue(PageIndexProperty); }
            set { SetValue(PageIndexProperty, value); }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Triggers on page change of the dependency property
        /// </summary>
        private void OnPageChange()
        {
            this.PageIndex = 1;
            this.TotalPageNumberLabel.Text = this.PageCount == 0 ? "1" : Convert.ToString(this.PageCount);
            this.SetButtonEnabled();
        }

        /// <summary>
        /// Triggers on lost focus from textbox
        /// </summary>
        /// <param name="sender">Textbox control</param>
        /// <param name="e">Event args</param>
        private void OnTextLostFocus(object sender, RoutedEventArgs e)
        {
            this.SetPageIndex();
        }

        /// <summary>
        /// Set the page number on lost focus
        /// </summary>
        private void SetPageIndex()
        {
            if (string.IsNullOrEmpty(this.PageNumberTextBox.Text))
            {
                // Set to default page number
                this.PageIndex = this.PageCount > 0 ? 1 : 0;
            }
            else
            {
                // Valid content
                var pageNumber = Convert.ToInt32(this.PageNumberTextBox.Text);
                this.PageIndex = pageNumber > this.PageCount ? this.PageCount : pageNumber <= 0 && this.PageCount > 0 ? 1 : pageNumber;
            }

            this.SetButtonEnabled();
        }

        /// <summary>
        /// Triggers on button click
        /// </summary>
        /// <param name="sender">Button control</param>
        /// <param name="e">Routed event args</param>
        private void OnButtonClick(object sender, RoutedEventArgs e)
        {
            var selectedButton = (Button)sender;
            switch (Convert.ToString(selectedButton.Tag))
            {
                case "First":
                    this.PageIndex = 1;
                    break;
                case "Previous":
                    if (this.PageIndex > 0)
                    {
                        this.PageIndex--;
                    }

                    break;
                case "Next":
                    this.PageIndex++;
                    break;
                case "Last":
                    this.PageIndex = this.PageCount;
                    break;
            }

            this.SetButtonEnabled();
        }

        /// <summary>
        /// Check current page and enabled/disabled button controls based on page change
        /// </summary>
        private void SetButtonEnabled()
        {
            if (this.PageChanged != null)
            {
                this.PageChanged();
            }

            // Only 1 available page
            if (this.PageIndex == 1 && this.PageCount == this.PageIndex)
            {
                this.SetButtonEnabled(false, false);
                return;
            }

            // No other pages prior to this and more than 1 available page
            if (this.PageIndex == 1 && this.PageCount > this.PageIndex)
            {
                this.SetButtonEnabled(false, true);
                return;
            }

            // Pages prior to this and more than 1 available page
            if (this.PageIndex > 1 && this.PageCount > this.PageIndex)
            {
                this.SetButtonEnabled(true, true);
                return;
            }

            // Prior to this and no pages beyond this
            if (this.PageIndex > 1 && this.PageCount == this.PageIndex)
            {
                this.SetButtonEnabled(true, false);
            }
        }

        /// <summary>
        /// Set button control to enable/disable based on page change
        /// </summary>
        /// <param name="isPrevEnabled">Is Previous button to be enabled</param>
        /// <param name="isNextEnabled">Is Next button to be enabled</param>
        private void SetButtonEnabled(bool isPrevEnabled, bool isNextEnabled)
        {
            this.FirstButton.IsEnabled = isPrevEnabled;
            this.PreviousButton.IsEnabled = isPrevEnabled;
            this.NextButton.IsEnabled = isNextEnabled;
            this.LastButton.IsEnabled = isNextEnabled;
        }

        #endregion
    }

Done!!!

You can use this control anywhere in your application. The usage is as follows

  <customControls:CustomPager Grid.Row="2" x:Name="Pager"/>

Pager.PageChanged will provide the page changed and you will be able to call data for the data grid using the ROWNUMBER function query with start index and number of rows

 In the next post, I will be adding the helper control that will provide a window with a datagrid control that fetch data based on the parameters provided.

Comments