Click here to Skip to main content
15,893,904 members
Articles / Web Development / ASP.NET

Handling Large Data Sets in Silverlight using WCF and Customized DataGrids

Rate me:
Please Sign up or sign in to vote.
4.57/5 (7 votes)
3 Jul 2010CPOL7 min read 62.8K   1.7K   34  
Display, edit and save large data sets in Silverlight using WCF based services and customized DataGrids
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using MappingDataEditor.DataServiceReference;

namespace MappingDataEditor
{
    public partial class MainPage : UserControl
    {
        private const char myInnerSeparator = ';';
        private SortCollectionView<ReturnTableEntry> myReturnTableView;
        private string myOldValue1 = null;
        private DateTime myTime;

        /// <summary>
        /// Constructor
        /// </summary>
        public MainPage()
        {
            InitializeComponent();

            myTeOutput.Text = "Start: " + DateTime.Now.ToString("HH:mm:ss.fffff");
            myReturnTableView = new SortCollectionView<ReturnTableEntry>();
            myReturnTableView.SortDescriptions.Add(new SortDescription("Field1", ListSortDirection.Ascending));
            myReturnTableView.OnRefresh += new EventHandler<RefreshEventArgs>(ReturnTableView_OnRefresh);
        }

        /// <summary>
        /// OnRefresh: this callback method is executed when the table needs to be refreshed, especially after clicking in the header (user wants to sort)
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void ReturnTableView_OnRefresh(object sender, RefreshEventArgs e)
        {
            if (myReturnTableView.SortDescriptions.Count > 0)
            {
                myTeOutput.Text += "\r\nSort started: " + DateTime.Now.ToString("HH:mm:ss.fffff");
                myTime = DateTime.Now;

                object selectedItem = myDGTable1.SelectedItem;
                SortDescription sortDesc = myReturnTableView.SortDescriptions[0];
                this.myReturnTableView.Content.RemoveAt(this.myReturnTableView.Content.Count - 1);
                if (myChGeneric.IsChecked == true)
                {
                    myReturnTableView.Content.SortQuick(new GenericComparer<ReturnTableEntry>(sortDesc.PropertyName, sortDesc.Direction == ListSortDirection.Ascending, StringComparison.CurrentCulture, myReturnTableView[0]));
                }
                else
                {
                    if (sortDesc.Direction == ListSortDirection.Ascending)
                    {
                        myReturnTableView.Content.SortQuick(new Field1ComparerUp());
                    }
                    else
                    {
                        myReturnTableView.Content.SortQuick(new Field1ComparerDown());
                    }
                }

                myDGTable1.SelectedItem = null;

                myReturnTableView.Content.Add(new ReturnTableEntry());
                myReturnTableView.FireCollectionChanged();
                myDGTable1.SelectedItem = selectedItem;

                TimeSpan tmp = DateTime.Now - myTime;
                myTeOutput.Text += "\r\nSort completed: " + DateTime.Now.ToString("HH:mm:ss.fffff") + " Secs.:" + tmp.TotalSeconds;
            }
        }

        /// <summary>
        /// Button load clicked: call the three services for getting the data
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void BuLoad_Click(object sender, RoutedEventArgs e)
        {
            DataServiceClient dsc = new DataServiceClient();
            dsc.CreateDemoDataCompleted +=new EventHandler<AsyncCompletedEventArgs>(dsc_CreateDemoDataCompleted);
            EnableButtons(false);
            dsc.CreateDemoDataAsync();
        }

        void dsc_CreateDemoDataCompleted(object sender, AsyncCompletedEventArgs e)
        {
            EnableButtons(true);
        }

        /// <summary>
        /// Initiate loading of the data by using the DataServiceClient 
        /// </summary>
        internal void LoadData()
        {
            myTeOutput.Text += "\r\nLoad started: " + DateTime.Now.ToString("HH:mm:ss.fffff");
            myTime = DateTime.Now;
            DataServiceClient dsc = new DataServiceClient();
            dsc.GetReturnTableEntriesCompleted += new EventHandler<GetReturnTableEntriesCompletedEventArgs>(dsc_GetReturnTableEntriesCompleted);
            EnableButtons(false);
            dsc.GetReturnTableEntriesAsync();
        }

        /// <summary>
        /// Dis-Enable all buttons
        /// </summary>
        /// <param name="pDoEnable">true: enable buttons; false: diable</param>
        private void EnableButtons(bool pDoEnable)
        {
            this.myBuLoad.IsEnabled = pDoEnable;
            this.myBuSave.IsEnabled = pDoEnable;
            this.myBuDelete1.IsEnabled = pDoEnable;
        }

        /// <summary>
        /// Callback from the asynchronous loading of the ReturnTableEntries. This one is executed when "GetReturnTableEntriesAsync" is done
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">Result and Error information of the service</param>
        private void dsc_GetReturnTableEntriesCompleted(object sender, GetReturnTableEntriesCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                TimeSpan tmp = DateTime.Now - myTime;
                myTeOutput.Text += "\r\nLoad Service completed: " + DateTime.Now.ToString("HH:mm:ss.fffff") + " Secs.:" + tmp.TotalSeconds;
                myTime = DateTime.Now;

                myReturnTableView.SetInitialContent(e.Result);
                myReturnTableView.Content.Add(new ReturnTableEntry());
                this.myDGTable1.ItemsSource = myReturnTableView;

                tmp = DateTime.Now - myTime;
                myTeOutput.Text += "\r\nSet ReturnTableView completed: " + DateTime.Now.ToString("HH:mm:ss.fffff") + " Secs.:" + tmp.TotalSeconds;
                myTime = DateTime.Now;
            }
            else
            {
                MessageBox.Show("Error loading data for table 1: " + e.Error.ToString());
            }

            EnableButtons(true);
        }

        private bool ValidateReturnValue(string pNewValue)
        {
            return pNewValue.Length < 10;
        }

        /// <summary>
        /// This code is executed before a cell in the first table is edited
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DGTable1_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
        {
            this.myOldValue1 = ((TextBox)e.EditingElement).Text;
        }

        /// <summary>
        /// This code is executed when a cell in the first table was edited
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DGTable1_CellEditEnded(object sender, DataGridCellEditEndedEventArgs e)
        {
            // Enter validation code here
            int colIndex = this.myDGTable1.Columns.IndexOf(e.Column);
            int rowIndex = e.Row.GetIndex();

            string newValue;
            switch (colIndex)
            {
                case 0: // Field1 was edited
                    newValue = this.myReturnTableView.Content[rowIndex].Field1;
                    if (!ValidateReturnValue(newValue))
                    {
                        MessageBox.Show("The entered '" + newValue + "' value had more than ten characters! This is not allowed.");
                        this.myReturnTableView.Content[rowIndex].ReturnValue = this.myOldValue1;
                    }
                    break;
                case 1: // Field2 was edited
                    newValue = this.myReturnTableView.Content[rowIndex].Field2;
                    break;
                case 2: // ReturnValue was edited
                    newValue = this.myReturnTableView.Content[rowIndex].ReturnValue;
                    break;
            }

            this.SetLastRowInTable1();
        }

        /// <summary>
        /// Returns the Content of the first table (ReturnTableEntries) as a string with separators (can be stored directly in a file)
        /// </summary>
        /// <returns>Content of the first table (ReturnTableEntries) as a string with separators</returns>
        private string GetReturnTableContent()
        {
            if (this.myReturnTableView != null && this.myReturnTableView.Content.Count > 1)
            {
                StringBuilder sb = new StringBuilder();
                ReturnTableEntry tmp = this.myReturnTableView.Content[0];
                sb.Append(tmp.Field1);
                sb.Append(myInnerSeparator);
                sb.Append(tmp.Field2);
                sb.Append(myInnerSeparator);
                sb.Append(tmp.ReturnValue);
                for (int i = 1; i < this.myReturnTableView.Content.Count - 1; i++)
                {
                    tmp = this.myReturnTableView.Content[i];
                    sb.Append("\r\n");
                    sb.Append(tmp.Field1);
                    sb.Append(myInnerSeparator);
                    sb.Append(tmp.Field2);
                    sb.Append(myInnerSeparator);
                    sb.Append(tmp.ReturnValue);
                }

                return sb.ToString();
            }

            return "";
        }

        /// <summary>
        /// Button save clicked: save the content of the three tables
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BuSave_Click(object sender, RoutedEventArgs e)
        {
            this.myBuSave.IsEnabled = false;
            this.myBuSave.Content = "Uploading: 0%";
            UploadState state1 = new UploadState("ReturnTable", GetReturnTableContent());
            state1.ProgressChanged += new EventHandler<ProgressEventArgs>(state_ProgressChanged);
            state1.Completed += new EventHandler<CompleteCompletedEventArgs>(state1_Completed);
            state1.StartUpload();
        }

        /// <summary>
        /// Callback method which shows the state of the upload
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">Percent done</param>
        void state_ProgressChanged(object sender, ProgressEventArgs e)
        {
            this.myBuSave.Content = "Uploading: " + Math.Round(e.Progress) + "%";
        }

        /// <summary>
        /// Saving of content of third table done: reset content of the button 
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">Return value of the service</param>
        void state1_Completed(object sender, CompleteCompletedEventArgs e)
        {
            this.myBuSave.Content = "Save";
        }

        /// <summary>
        /// Delete row in first table
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void BuDelete1_Click(object sender, RoutedEventArgs e)
        {
            if (this.myDGTable1.SelectedIndex >= 0 && this.myDGTable1.SelectedIndex < this.myReturnTableView.Count)
            {
                int tmp = this.myDGTable1.SelectedIndex;
                this.myReturnTableView.Remove((ReturnTableEntry)this.myDGTable1.SelectedItem);
                if (tmp >= this.myReturnTableView.Count)
                {
                    tmp--;
                }
                if (tmp >= 0)
                {
                    this.myDGTable1.SelectedIndex = tmp;
                }
            }
        }

        /// <summary>
        /// Checks if the last row of the DataGrid myDGTable1 (bound to myCollectionView1) is not empty: if so add an empty row
        /// </summary>
        private void SetLastRowInTable1()
        {
            if (this.myReturnTableView.Content.Count > 0)
            {
                ReturnTableEntry tmp = this.myReturnTableView.Content[this.myReturnTableView.Content.Count - 1];
                if (!string.IsNullOrEmpty(tmp.Field1) || !string.IsNullOrEmpty(tmp.Field2) || !string.IsNullOrEmpty(tmp.ReturnValue))
                {
                    this.myReturnTableView.Content.Add(new ReturnTableEntry());
                }
            }
            else
            {
                this.myReturnTableView.Content.Add(new ReturnTableEntry());
            }
        }

        private string myFilterString;

        /// <summary>
        /// Executes the filter
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void BuFilter_Click(object sender, RoutedEventArgs e)
        {
            myTeOutput.Text += "\r\nFilter started: " + DateTime.Now.ToString("HH:mm:ss.fffff");
            myTime = DateTime.Now;

            this.myFilterString = this.myTeFilter.Text;
            if (!string.IsNullOrEmpty(this.myFilterString))
            {
                this.myReturnTableView.Filter = new Predicate<object>(FilterCallback);
            }
            else
            {
                this.myReturnTableView.Filter = null;
            }

            this.myReturnTableView.FireCollectionChanged();
            TimeSpan tmp = DateTime.Now - myTime;
            myTeOutput.Text += "\r\nFilter completed: " + DateTime.Now.ToString("HH:mm:ss.fffff") + " Secs.:" + tmp.TotalSeconds;
        }

        private bool FilterCallback(object pItem)
        {
            ReturnTableEntry tmp = (ReturnTableEntry)pItem;
            bool isEmpty = true;
            if (!string.IsNullOrEmpty(tmp.Field1))
            {
                if (tmp.Field1.StartsWith(myFilterString))
                {
                    return true;
                }

                isEmpty = false;
            }

            if (!string.IsNullOrEmpty(tmp.Field2))
            {
                if (tmp.Field2.StartsWith(myFilterString))
                {
                    return true;
                }

                isEmpty = false;
            }

            if (!string.IsNullOrEmpty(tmp.ReturnValue))
            {
                if (tmp.ReturnValue.StartsWith(myFilterString))
                {
                    return true;
                }

                isEmpty = false;
            }

            return isEmpty;
        } 
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader maxment GmbH
Germany Germany
I wrote my first program more than 30 years ago when I was 8 years old. It was written in Pascal on an Apple ][e and calculated the Greatest Common Divisor. I was so proud when it finally work - from that day on computers where part of my life.

After successfully studying computer science I worked in a large cardiologic department as software developer. A few years later I went back to university and my PhD in medical computer science (Dr. sc. inf. biomed.). During this time a lead small team of really smart develeopers and we implemented the market leading cardiologic information system for the German market. In the following years I worked for Siemens but I missed this inspiring, creative athmosphere of a small company. So I changed back to a small start-up.

We are specialized on Webdevelopment and Windows technologies (C#, .Net, Silverlight, WPF, WCF, WinForms, Prism etc.). We are also experts in in Databases (ORACLE, MS-SQL-Server, PostgreSQL), medical standards (HL7, DICOM, IHE) and coordination of larger projects (including techniques like Agile Development and SCRUM). So if you are looking for a smart and efficient developer just contact me.

I started with C# and .NET in 2003. When Silverlight 2 hit the market I took a look at it and never let go again. Now - after two years and more than 500,000 lines of code - I think that I have a good theoretical and pratical understanding of this technology. So I would like to give back something to the community and share some tricks with you. I hope that it helps somebody - like all the interesting articles I read while learning the secrets of Silverlight, C#, .NET ...

Comments and Discussions