Click here to Skip to main content
13,764,506 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

11.4K views
110 downloads
8 bookmarked
Posted 5 May 2011
Licenced CPOL

Implement a Cancel Button while using Bindings, and Send Only an Object's Changes to a WCF Service

, 5 May 2011
Rate this:
Please Sign up or sign in to vote.
This example will demonstrate a cancel button, even when bindings have updated your object.

Introduction

Take the following scenario: A user of your application opens a customer for editing. They put in a new address. Now they realize they're in the wrong customer entirely and want to cancel their changes.

What does your application do? If you're like me, then you are on a mission to keep code behind to an absolute minimum. You have two way bindings setup for everything. You're proud of all the C#/VB code that you haven't written in your application. So, your model is already updated. Do you not have a cancel button? Blame the user for the bad experience and tell them not to do that? Or did you add a reload method to your cancel button instead, forcing the application to grab data from a web service or a database?

Another possible option might have been to follow MVVM completely and to separate the Model from the ViewModel even on the most basic classes. The ViewModel could then store copies of this data and include methods to move back to the object and to reload data from the object. The downside here is that you need to add properties twice, and always remember to include them in your methods. This is a lot of code duplication.

Here's another predicament: You use a web service for your data source. You'd like to keep the data sent across the Internet to a minimum. But your class has 100+ properties. Sure, only one might have changed, but how do you know which ones have changed? Do you serialize the entire class to send it? Do you create 100 variables called OrgAddress, OrgName, etc.?

I'd like to share a solution to both of these that has worked for me: A special base class.

Using the Code

Let's introduce our class:

public class ChangeSetClass : IDisposable, INotifyPropertyChanged 
It optionally implements two interfaces: IDisposable for using using() statements and InotifyPropertyChanged so that our simple classes will not need a separate ViewModel. I'm not saying we should eliminate the ViewModel. For MANY cases, I still use it. But when I have a class that only contains basic properties, I have gone this route.

Note also that even if you do implement INotifyPropertyChanged, you can still use a separate ViewModel with this. You just don't raise any notifications in your object, and instead do that in your actual ViewModel.

Now, let's introduce a property and a private variable to our class. ParentType will remember what type the originating object really is, and OrgList will maintain a set of values.

public Type ParentType;
private List<Object> OrgList = new List<object>();

Next, let's define a customer class for our example:

public class Customer : ChangeSetClass
{ 
     public int ID { get; set; }
     public string Address { get; set; }
     public string City { get; set; }
     public string State { get; set; }
     // There are more properties

     public Customer()
     {
        base.ParentType = typeof(Customer);
     }
} 

So, in our constructor, we set the ParentType in our ChangeSetClass class. Now the magic can begin. We introduce new methods to the ChangeSetClass:

public void InitializeValues()

This is called when the object is loaded from our source. It simply enumerates through the properties in the class and adds them to the OrgList.

public void ResetValues()

This is called when the user attempts to cancel something. The OrgList values are put back into the object.

public bool HasChanged()

This loops the properties and compares them to the OrgList letting us know if anything has changed.

All three methods here are similar in nature, so let's just take a look at the first one:

public void InitializeValues() 
{ 
     // Loop all properties and see if anything has changed.
     // First grab all properties into a list.
     PropertyInfo[] properties = ParentType.GetProperties();
     // Now loop all properties and compare to the original list. 
     OrgList.Clear();
     // Now just add this to the original array list
     for (int i = 0; i < properties.GetLength(0); i++)
     {
         // Read only properties are skipped
         if (properties[i].CanWrite)
         {
             // We only care about standard .NET objects like
             // Date/String/float/int/bool, etc.
             // If we have defined a new class, that should not be in the compare area.
             OrgList.Add(properties[i].GetValue(this, null));
         }
     }
 }

This method uses reflection to look up our object hierarchy. It will work for any object we throw at it. We could also account for nested classes or other data types. Or, if desired, we could enumerate any IEnumerable property. So if my class is an Invoice class, and I have a List<InvoiceLines> property inside it, the HasChanged() method could dig down into this list too. Better yet, why not make the same three methods with an optional parameter (bool bRecusive)?

Let's look at some benefits of this design:

  • All bindings work as expected.
  • If there is an intermediate ViewModel, it can write directly to the object while performing notification, and does not need to store separate variables, nor implement methods to move data into the model or out of the model.
  • Since updates are live to the object, the screen my modal customer edit window is partially covering is being updated as the user works.
  • All new classes based on ChangeSetClass will automatically inherit these properties and be able to rollback changes when needed. No special ViewModels are needed.
  • Any new property added to our class is instantly seen by the ChangeSetClass because of Reflection.

Now let's look at our second predicament from before. In web based applications, you would want to transmit the least amount of data possible. So, why not let the ChangeSetClass do this work for you?

public string ChangesXML { get { return GetChangesXML(); } }
private string GetChangesXML()
{         
   // Loop all properties and see if anything has changed.
   // First grab all properties into a list.
   PropertyInfo[] properties = ParentType.GetProperties();
   TextWriter tw = new StringWriter();
   using (XmlWriter writer = XmlWriter.Create(tw)) 
   {
      writer.WriteStartDocument();
      writer.WriteStartElement(ParentType.ToString());
      int iy = -1;
      bool bChanges = false;
      for (int i = 0; i < OrgList.Count; i++)
      {
         // I have public properties calling methods
         // like AddressFull, etc.  These should not be looked at.
         if (properties[i].CanWrite)
         {
            iy++;
            if (OrgList.Count > iy)
            {
               // If one property is NULL and the other one
               // is NOT NULL then we've had a change.
               if (((OrgList[iy] == null) && 
                    (properties[i].GetValue(this, null) != null)) ||
                  // Check both sides for this null check
                  ((OrgList[iy] != null) && (properties[i].GetValue
			(this, null) == null)) ||
                  // Also, if both are NOT null and the values
                  // are not equal, then we've had a change.
                  ((OrgList[iy] != null) && (properties[i].GetValue
			(this, null) != null) &&
                    (!(OrgList[iy].Equals(properties[i].GetValue(this, null))))))
               {
                   bChanges = true;
                   writer.WriteElementString(properties[i].Name, 
                      properties[i].GetValue(this, null).ToString());
               }
            }
         }
      } 
      if (!bChanges) return string.Empty;
      writer.WriteEndElement();
      writer.WriteEndDocument();
   }
   return tw.ToString();
}

This provides XML of only the properties that have changed. This is minimal data to send to a web service.

In conclusion, we have looked at a possible solution for solving the two way binding conundrum and allowing for changes to rollback, and we've also created a standardized format for updating our web service, should we be using one. I believe this standardization will help to produce future classes more rapidly and with better consistency.

History

  • 05-04-2011: Initial version

License

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

Share

About the Author

gardnerp
Software Developer
United States United States
I've been a developer since 2004. I work in C#, ASP, WPF, XAML, Silverlight, Windows Store, HTML/JavaScript/CSS, Java, Delphi, and SQL. I write for web, desktop, Android, Windows Mobile, Windows Store, and database servers.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
Generalgood but... Pin
roks nicolas5-May-11 23:30
professionalroks nicolas5-May-11 23:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.181113.4 | Last Updated 5 May 2011
Article Copyright 2011 by gardnerp
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid