OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


A Simple Silverlight CRUD Example

Silverlight is different because it communicates with the website that launches it using asynchronous communication. Learning how to design applications this way can be a bit challenging.

So I created an example, end-to-end, that achieves these goals:

  • Creates, Reads, Updates, and Deletes records from the database
  • Implements Forms based security
  • Implements "Granular Security" ("only allow User One to see, edit, and create their own Tasks") 
  • Implements View Model Style

View Model Style

View Model Style allows a programmer to create an application that has absolutely no UI (user interface). The programmer only creates a ViewModel and a Model. A designer with no programming ability at all, is then able to start with a blank page and completely create the View (UI) in Microsoft Expression Blend 4 (or higher). If you are new to View Model Style it is suggested that you read Silverlight View Model Style : An (Overly) Simplified Explanation for an introduction.

Website Application

Make a database table:

Make web service methods:

    [WebService(Namespace = "http://OpenLightGroup.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    public class WebService : System.Web.Services.WebService
    {
        #region GetCurrentUserID
        private int GetCurrentUserID()
        {
            int intUserID = -1;
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                // Get the current user
                intUserID = Convert.ToInt32(HttpContext.Current.User.Identity.Name);
            }
            return intUserID;
        }
        #endregion
 
        // Web Methods
 
        #region GetTasks
        [WebMethod]
        public List<Task> GetTasks()
        {
            // Create a collection to hold the results
            List<Task> colResult = new List<Task>();
 
            DataClasses1DataContext DB = new DataClasses1DataContext();
 
            var colTasks = from Tasks in DB.Tasks
                           where Tasks.UserID == GetCurrentUserID()
                           select Tasks;
 
            // Loop thru the Tasks
            foreach (var item in colTasks)
            {
                // Create a Task
                Task tmpTask = new Task();
 
                // Set only the TaskID and the Name
                // We do this because Description could be 
                // a large amount of data that will slow down
                // the application and we don't need it now
                tmpTask.TaskID = item.TaskID;
                tmpTask.TaskName = item.TaskName;
 
                // Add to the final results
                colResult.Add(tmpTask);
            }
 
            return colResult;
        } 
        #endregion
 
        #region GetTask
        [WebMethod]
        public Task GetTask(int TaskID)
        {
            DataClasses1DataContext DB = new DataClasses1DataContext();
 
            var result = (from Tasks in DB.Tasks
                          where Tasks.TaskID == TaskID
                          where Tasks.UserID == GetCurrentUserID()
                          select Tasks).FirstOrDefault();
 
            return result;
        } 
        #endregion
 
        #region DeleteTask
        [WebMethod]
        public string DeleteTask(int TaskID)
        {
            string strError = "";
            DataClasses1DataContext DB = new DataClasses1DataContext();
 
            try
            {
                var result = (from Tasks in DB.Tasks
                              where Tasks.TaskID == TaskID
                              where Tasks.UserID == GetCurrentUserID()
                              select Tasks).FirstOrDefault();
 
                if (result != null)
                {
                    DB.Tasks.DeleteOnSubmit(result);
                    DB.SubmitChanges();
                }
            }
            catch (Exception ex)
            {
                strError = ex.Message;
            }
 
            return strError;
        } 
        #endregion
 
        #region UpdateTask
        [WebMethod]
        public string UpdateTask(Task objTask)
        {
            string strError = "";
            DataClasses1DataContext DB = new DataClasses1DataContext();
 
            try
            {
                var result = (from Tasks in DB.Tasks
                              where Tasks.TaskID == objTask.TaskID
                              where Tasks.UserID == GetCurrentUserID()
                              select Tasks).FirstOrDefault();
 
                if (result != null)
                {
                    result.TaskDescription = objTask.TaskDescription;
                    result.TaskName = objTask.TaskName;
 
                    DB.SubmitChanges();
                }
            }
            catch (Exception ex)
            {
                strError = ex.Message;
            }
 
            return strError;
        } 
        #endregion
 
        #region InsertTask
        [WebMethod]
        public Task InsertTask(Task objTask)
        {
            DataClasses1DataContext DB = new DataClasses1DataContext();
 
            try
            {
                Task InsertTask = new Task();
 
                InsertTask.TaskDescription = objTask.TaskDescription;
                InsertTask.TaskName = objTask.TaskName;
                InsertTask.UserID = GetCurrentUserID();
 
                DB.Tasks.InsertOnSubmit(InsertTask);
                DB.SubmitChanges();
 
                // Set the TaskID 
                objTask.TaskID = InsertTask.TaskID;
            }
            catch (Exception ex)
            {
                // Log the error
                objTask.TaskID = -1;
                objTask.TaskDescription = ex.Message;
            }
 
            return objTask;
        } 
        #endregion
    }

Silverlight Application

Download and install RX Extensions from: http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx 

Add references to:

  • System.CoreEx
  • System.Observable
  • System.Reactive

Add a web reference to the web service.

Insert the following code for the Model:

    public class TasksModel
    {
        #region GetTasks
        public static IObservable<IEvent<GetTasksCompletedEventArgs>> GetTasks()
        {
            // Set up web service call
            WebServiceSoapClient WS = new WebServiceSoapClient();
 
            // Set the EndpointAddress
            WS.Endpoint.Address = new EndpointAddress(GetBaseAddress());
 
            IObservable<IEvent<GetTasksCompletedEventArgs>> observable =
              Observable.FromEvent<GetTasksCompletedEventArgs>(WS, "GetTasksCompleted");
            WS.GetTasksAsync();
 
            return observable;
        } 
        #endregion
 
        #region GetTask
        public static IObservable<IEvent<GetTaskCompletedEventArgs>> GetTask(int TaskID)
        {
            // Set up web service call
            WebServiceSoapClient WS = new WebServiceSoapClient();
 
            // Set the EndpointAddress
            WS.Endpoint.Address = new EndpointAddress(GetBaseAddress());
 
            IObservable<IEvent<GetTaskCompletedEventArgs>> observable =
              Observable.FromEvent<GetTaskCompletedEventArgs>(WS, "GetTaskCompleted");
            WS.GetTaskAsync(TaskID);
 
            return observable;
        }
        #endregion
 
        #region DeleteTask
        public static IObservable<IEvent<DeleteTaskCompletedEventArgs>> DeleteTask(int TaskID)
        {
            // Set up web service call
            WebServiceSoapClient WS = new WebServiceSoapClient();
 
            // Set the EndpointAddress
            WS.Endpoint.Address = new EndpointAddress(GetBaseAddress());
 
            IObservable<IEvent<DeleteTaskCompletedEventArgs>> observable =
              Observable.FromEvent<DeleteTaskCompletedEventArgs>(WS, "DeleteTaskCompleted");
            WS.DeleteTaskAsync(TaskID);
 
            return observable;
        }
        #endregion
 
        #region UpdateTask
        public static IObservable<IEvent<UpdateTaskCompletedEventArgs>> UpdateTask(Task objTask)
        {
            // Set up web service call
            WebServiceSoapClient WS = new WebServiceSoapClient();
 
            // Set the EndpointAddress
            WS.Endpoint.Address = new EndpointAddress(GetBaseAddress());
 
            IObservable<IEvent<UpdateTaskCompletedEventArgs>> observable =
              Observable.FromEvent<UpdateTaskCompletedEventArgs>(WS, "UpdateTaskCompleted");
            WS.UpdateTaskAsync(objTask);
 
            return observable;
        }
        #endregion
 
        #region InsertTask
        public static IObservable<IEvent<InsertTaskCompletedEventArgs>> InsertTask(Task objTask)
        {
            // Set up web service call
            WebServiceSoapClient WS = new WebServiceSoapClient();
 
            // Set the EndpointAddress
            WS.Endpoint.Address = new EndpointAddress(GetBaseAddress());
 
            IObservable<IEvent<InsertTaskCompletedEventArgs>> observable =
              Observable.FromEvent<InsertTaskCompletedEventArgs>(WS, "InsertTaskCompleted");
            WS.InsertTaskAsync(objTask);
 
            return observable;
        }
        #endregion
 
        // Utility
 
        #region GetBaseAddress
        private static Uri GetBaseAddress()
        {
            // Get the web address of the .xap that launched this application     
            string strBaseWebAddress = App.Current.Host.Source.AbsoluteUri;
            // Find the position of the ClientBin directory        
            int PositionOfClientBin =
                App.Current.Host.Source.AbsoluteUri.ToLower().IndexOf(@"/clientbin");
            // Strip off everything after the ClientBin directory         
            strBaseWebAddress = Strings.Left(strBaseWebAddress, PositionOfClientBin);
            // Create a URI
            Uri UriWebService = new Uri(String.Format(@"{0}/WebService.asmx", strBaseWebAddress));
            // Return the base address          
            return UriWebService;
        }
        #endregion
    }

Use the following code for the View Model:

    public class MainPageModel : INotifyPropertyChanged
    {
        public MainPageModel()
        {
            // Set the command property
            GetTasksCommand = new DelegateCommand(GetTasks, CanGetTasks);
            GetTaskCommand = new DelegateCommand(GetTask, CanGetTask);
            DeleteTaskCommand = new DelegateCommand(DeleteTask, CanDeleteTask);
            UpdateTaskCommand = new DelegateCommand(UpdateTask, CanUpdateTask);
            AddNewTaskCommand = new DelegateCommand(AddNewTask, CanAddNewTask);
 
            // The following line prevents Expression Blend
            // from showing an error when in design mode
            if (!DesignerProperties.IsInDesignTool)
            {
                // Get the Tasks for the current user
                GetTasks();
 
                // Set Visibility
                HasCurrentTask = Visibility.Collapsed;
                AddVisibility = Visibility.Visible;
                UpdateVisibility = Visibility.Collapsed;
                DeleteVisibility = Visibility.Collapsed;
            }
        }
 
        // Commands
 
        #region GetTasksCommand
        public ICommand GetTasksCommand { get; set; }
        public void GetTasks(object param)
        {
            GetTasks();
        }
 
        private bool CanGetTasks(object param)
        {
            return true;
        }
        #endregion
 
        #region GetTaskCommand
        public ICommand GetTaskCommand { get; set; }
        public void GetTask(object param)
        {
            // Get the Task that was passed as a parameter
            Task objTask = (Task)param;
 
            // Call GetTask to get and set
            // the CurrentTask property
            GetTask(objTask.TaskID);
        }
 
        private bool CanGetTask(object param)
        {
            // Only allow this ICommand to fire 
            // if a Task was passed as a parameter
            return ((param as Task) != null);
        }
        #endregion
 
        #region DeleteTaskCommand
        public ICommand DeleteTaskCommand { get; set; }
        public void DeleteTask(object param)
        {
            if (CurrentTask.TaskID == -1)
            {
                // This is a new Task
                SetToNewTask();
            }
            else
            {
                // This is an Existing Task
                DeleteTask(CurrentTask);
            }
        }
 
        private bool CanDeleteTask(object param)
        {
            // Do not allow if there is no Current Task
            return (CurrentTask != null);
        }
        #endregion
 
        #region UpdateTaskCommand
        public ICommand UpdateTaskCommand { get; set; }
        public void UpdateTask(object param)
        {
            if (CurrentTask.TaskID == -1)
            {
                // This is a new Task
                InsertTask(CurrentTask);
            }
            else
            {
                // This is an Update
                UpdateTask(CurrentTask);
            }
        }
 
        private bool CanUpdateTask(object param)
        {
            // Do not allow if there is no Current Task
            return (CurrentTask != null);
        }
        #endregion
 
        #region AddNewTaskCommand
        public ICommand AddNewTaskCommand { get; set; }
        public void AddNewTask(object param)
        {
            SetToNewTask();
        }
 
        private bool CanAddNewTask(object param)
        {
            return true;
        }
        #endregion
 
        // Operations
 
        #region GetTasks
        private void GetTasks()
        {
            // Clear the current Tasks
            colTasks.Clear();
            // Call the Model to get the collection of Tasks
            TasksModel.GetTasks().Subscribe(p =>
            {
                if (p.EventArgs.Error == null)
                {
                    // loop thru each item
                    foreach (var Task in p.EventArgs.Result)
                    {
                        // Add to the colTasks collection
                        colTasks.Add(Task);
                    }
 
                    // Count the records returned
                    if (colTasks.Count == 0)
                    {
                        // If there are no records, indicate that
                        Message = "No Records Found";
 
                        // Set HasCurrentTask
                        HasCurrentTask = Visibility.Collapsed;
 
                        // We have no Tasks so set HasTasks
                        HasTasks = Visibility.Collapsed;
                    }
                    else
                    {
                        // We have Tasks so set HasTasks
                        HasTasks = Visibility.Visible;
                    }
                }
            });
        }
        #endregion
 
        #region GetTask
        private void GetTask(int intTaskID)
        {
            // Call the Model to get the Task
            TasksModel.GetTask(intTaskID).Subscribe(p =>
            {
                if (p.EventArgs.Error == null)
                {
                    // Set the CurrentTask Property
                    CurrentTask = p.EventArgs.Result;
 
                    // Set Visibility
                    HasCurrentTask = Visibility.Visible;
                    AddVisibility = Visibility.Visible;
                    UpdateVisibility = Visibility.Visible;
                    DeleteVisibility = Visibility.Visible;
                }
            });
        }
        #endregion
 
        #region DeleteTask
        private void DeleteTask(Task objTask)
        {
            // Call the Model to delete the Task
            TasksModel.DeleteTask(objTask.TaskID).Subscribe(p =>
            {
                if (p.EventArgs.Error == null)
                {
                    // Set the Error Property
                    Message = p.EventArgs.Result;
 
                    // Set current Task to null
                    CurrentTask = null;
 
                    // Update the Tasks list
                    GetTasks();
 
                    // Set Visibility
                    HasCurrentTask = Visibility.Collapsed;
                    AddVisibility = Visibility.Visible;
                    UpdateVisibility = Visibility.Collapsed;
                    DeleteVisibility = Visibility.Collapsed;
                }
            });
        }
        #endregion
 
        #region UpdateTask
        private void UpdateTask(Task objTask)
        {
            // Call the Model to UpdateTask the Task
            TasksModel.UpdateTask(objTask).Subscribe(p =>
            {
                if (p.EventArgs.Error == null)
                {
                    // Set the Error Property
                    Message = p.EventArgs.Result;
 
                    // Update the Tasks list
                    GetTasks();
 
                    // Set Visibility
                    HasCurrentTask = Visibility.Visible;
                    AddVisibility = Visibility.Visible;
                    UpdateVisibility = Visibility.Visible;
                    DeleteVisibility = Visibility.Visible;
                }
            });
        }
        #endregion
 
        #region InsertTask
        private void InsertTask(Task objTask)
        {
            // Call the Model to Insert the Task
            TasksModel.InsertTask(objTask).Subscribe(p =>
            {
                if (p.EventArgs.Error == null)
                {
                    // Set the CurrentTask Property
                    CurrentTask = p.EventArgs.Result;
 
                    // Update the Tasks list
                    GetTasks();
 
                    // Set Visibility
                    HasCurrentTask = Visibility.Visible;
                    AddVisibility = Visibility.Visible;
                    UpdateVisibility = Visibility.Visible;
                    DeleteVisibility = Visibility.Visible;
                }
            });
        }
        #endregion
 
        #region SetToNewTask
        private void SetToNewTask()
        {
            // Create a empty Task
            // so form will be blank
            Task objTask = new Task();
 
            // Set TaskID = -1 so we know it's 
            // a new Task
            objTask.TaskID = -1;
 
            // Set the CurrentTask Property
            CurrentTask = objTask;
 
            // Set Visibility
            HasCurrentTask = Visibility.Visible;
            AddVisibility = Visibility.Collapsed;
            UpdateVisibility = Visibility.Visible;
            DeleteVisibility = Visibility.Collapsed;
        }
        #endregion
 
        // Properties
 
        #region CurrentTask
        private Task _CurrentTask = new Task();
        public Task CurrentTask
        {
            get { return _CurrentTask; }
            private set
            {
                if (CurrentTask == value)
                {
                    return;
                }
                _CurrentTask = value;
                this.NotifyPropertyChanged("CurrentTask");
            }
        }
        #endregion
 
        #region AddVisibility
        private Visibility _AddVisibility = Visibility.Visible;
        public Visibility AddVisibility
        {
            get { return _AddVisibility; }
            private set
            {
                if (AddVisibility == value)
                {
                    return;
                }
                _AddVisibility = value;
                this.NotifyPropertyChanged("AddVisibility");
            }
        }
        #endregion
 
        #region UpdateVisibility
        private Visibility _UpdateVisibility = Visibility.Visible;
        public Visibility UpdateVisibility
        {
            get { return _UpdateVisibility; }
            private set
            {
                if (UpdateVisibility == value)
                {
                    return;
                }
                _UpdateVisibility = value;
                this.NotifyPropertyChanged("UpdateVisibility");
            }
        }
        #endregion
 
        #region DeleteVisibility
        private Visibility _DeleteVisibility = Visibility.Visible;
        public Visibility DeleteVisibility
        {
            get { return _DeleteVisibility; }
            private set
            {
                if (DeleteVisibility == value)
                {
                    return;
                }
                _DeleteVisibility = value;
                this.NotifyPropertyChanged("DeleteVisibility");
            }
        }
        #endregion
 
        #region HasTasks
        private Visibility _HasTasks = Visibility.Collapsed;
        public Visibility HasTasks
        {
            get { return _HasTasks; }
            private set
            {
                if (HasTasks == value)
                {
                    return;
                }
                _HasTasks = value;
                this.NotifyPropertyChanged("HasTasks");
            }
        }
        #endregion
 
        #region HasCurrentTask
        private Visibility _HasCurrentTask = Visibility.Collapsed;
        public Visibility HasCurrentTask
        {
            get { return _HasCurrentTask; }
            private set
            {
                if (HasCurrentTask == value)
                {
                    return;
                }
                _HasCurrentTask = value;
                this.NotifyPropertyChanged("HasCurrentTask");
            }
        }
        #endregion
 
        #region Message
        private string _Message;
        public string Message
        {
            get { return _Message; }
            private set
            {
                if (Message == value)
                {
                    return;
                }
                _Message = value;
                this.NotifyPropertyChanged("Message");
            }
        }
        #endregion
 
        // Collections
 
        #region colTasks
        private ObservableCollection<Task> _colTasks
            = new ObservableCollection<Task>();
        public ObservableCollection<Task> colTasks
        {
            get { return _colTasks; }
            private set
            {
                if (colTasks == value)
                {
                    return;
                }
                _colTasks = value;
                this.NotifyPropertyChanged("colTasks");
            }
        }
        #endregion
 
        // Utility
 
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
 
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion
    }

Hook up the View to the View Model.

Live example: http://silverlight.adefwebserver.com/RIATasks/

Download Sample Code: WebApplication1.zip





Comments are closed.
Showing 7 Comments
Avatar  Matt 7 years ago

This looks like such a great tutorial that I could learn from but I am a vb programmer and can not translate your code to vb. Is there any way there is a post that shows all this in VB?

Avatar  Michael Washington 7 years ago

@ Stephen Patten - Thank you for the feedback. I just hope you are able to easily understand this and simply get moving on your own projects :) I hope you simply say, "Oh that's no big deal".

Avatar  Stephen Patten 7 years ago

Thank you for taking the time to explain this.

Avatar  Michael Washington 7 years ago

@Mo_Hassan - I appreciate the feedback, thanks!

Avatar  Mo_Hassan 7 years ago

Very helpful Mr. Washington thank you.

Avatar  Michael Washington 7 years ago

@Daniel - Thanks for the feedback. I hope this helps.

Avatar  Daniel 7 years ago

Great article.&lt;br&gt;Very easy to learn.