CSLA.Core.BindableBase

The CSLA.Core.BindableBase assembly contains two classes: BindableBase and BindableCollectionBase . These will be the bases from which our BusinessBase and BusinessCollectionBase classes inherit. Notice that BindableCollectionBase also inherits from System.Collections.CollectionBase , so that it's a base collection class, as shown in Figure 4-3.

image from book
Figure 4-3: Class diagram showing BindableCollectionBase and BindableBase

With our CSLA solution open in VS .NET, use File image from book Add Project image from book New Project to add a new Class Library project named CSLA.Core.BindableBase , as shown in Figure 4-4.

image from book
Figure 4-4: Adding the CSLA.Core.BindableBase project to the solution

Core.BindableBase

First, let's create the base class that will safely declare our IsDirtyChanged event, which will be raised when any property is changed. Rename Class1.cs to BindableBase.cs , and then change its code to the following:

 using System;  namespace CSLA.Core  {   /// <summary>  /// This base class declares the IsDirtyChanged event   /// to be NonSerialized so serialization will work.  /// </summary>  [Serializable()]   public abstract class BindableBase   {     [field: NonSerialized]     public event EventHandler IsDirtyChanged;     virtual protected void OnIsDirtyChanged()     {       if (IsDirtyChanged != null)         IsDirtyChanged(this, EventArgs.Empty);     }   }  } 

Notice here that we're declaring this class to be in the CSLA.Core namespace, as discussed in Chapter 2. We might as well change the default namespace that VS .NET will use for any further code files we add to the project. Right-click the project in Solution Explorer and choose Properties. Change the default namespace to CSLA.Core . Note that changing this won't affect any code we may already have in the project.

It's important that this class is marked as [Serializable()] . We want our business objects to be serializable, and that means that any classes they inherit from must also be marked as such. BindableBase will be the ultimate base class for all our business objects.

Also, the class is declared as abstract . This means that an instance of this class can't be created directly. Instead, it must be subclassed to create other classes from which we can create objects. In our case, these will be our business objects.

The key lines occur at the declaration of the event:

 [field: NonSerialized]     public event EventHandler IsDirtyChanged; 

By applying the attribute to the event, we're telling .NET that any delegate references that are used to raise the event to other objects shouldn't be serialized. This means that the serialization process won't try to serialize the objects that are receiving and handling our IsDirtyChanged event.

The thing about events and inheritance is that an event can only be raised by code in the class where it is declared. This is because the event member can only be accessed directly from the class in which it is defined. It can't be raised by code in classes that inherit from this class. This means that our business objects can't raise the IsDirtyChanged event directly, even though we'll want them to do just that. To solve this problem, we're following a standard .NET design pattern by creating a protected method that in turn raises the event:

 virtual protected void OnIsDirtyChanged()     {       if (IsDirtyChanged != null)         IsDirtyChanged(this, EventArgs.Empty);     } 

Any classes that inherit from our base class can call this method when they want to raise the event.

The result is that we now have a base class that allows our business objects to raise the IsDirtyChanged event, thereby supporting data binding.

Core.BindableCollectionBase

To support Windows Forms data binding fully, collection objects should implement the System.ComponentModel.IBindingList interface. This is a fairly complex interface that can be used to enable searching, sorting, and notification of changes to the collection, and to provide control over whether elements can be added, removed, or edited through data binding.

The reason we're discussing this here is that the notification of changes is handled through an event that's part of the IBindingList interface. To implement the interface, we must declare and raise a ListChanged event ”and, of course, that event must have the attribute that prevents it from being serialized.

Tip  

We won't implement the searching or sorting capabilities of this interface in this book. If these features are important to your business collections, you can enhance this code to provide that support for your framework. We'll hard-code the methods for these features of the interface to indicate that the features aren't supported.

Add a new class named BindableCollectionBase to the project. Since this will act as our base collection class, it needs to inherit from System.Collections.CollectionBase , and of course it needs to implement the IBindingList interface as well. This means that we'll declare the class with the following code:

 using System;  using System.Collections; using System.ComponentModel;  namespace CSLA.Core {   /// <summary>  /// This is a base class that exposes an implementation   /// of IBindableList that does nothing other than   /// create a nonserialized version of the listchanged   /// event.  /// </summary>  [Serializable]   public abstract class BindableCollectionBase : CollectionBase, IBindingList   {   }  } 

When building a framework, it's always wise to think ahead to what future developers might want to do with it. Wherever possible, we should build in flexibility so that business developers can override or alter the behavior of our framework in meaningful ways. For instance, we can allow business developers to have their actual business collection objects control whether data binding can add, remove, or edit items in the collection. While we won't support those concepts by default, we'll add code in our framework so that business developers can support the features if they desire .

To do this, we'll expose a set of protected variables that can optionally be altered by our business classes. Add the highlighted lines to the BindableCollectionBase class:

 [Serializable]   public abstract class BindableCollectionBase : CollectionBase, IBindingList   {  protected bool AllowNew = false;     protected bool AllowEdit = false;     protected bool AllowRemove = false;  

Since these values default to false , we're indicating that by default, these features aren't supported by business collections. Because they're protected in scope, a business developer can set their values to true in order to enable the features for a specific business collection object. This is ideal, because the business collection class will need to implement extra code to support the addition of new objects and may also want to do extra processing to properly support the editing or removal of child objects.

Next, we'll declare the ListChanged event to be NonSerialized :

  [field: NonSerialized]     public event ListChangedEventHandler ListChanged;  

As with the BindableBase class, this event declaration creates an event that is safe for use with serialization. Once again, we also need to provide a way for our business collections to raise this event, so we create a protected method:

  virtual protected void OnListChanged(ListChangedEventArgs e)     {       if (ListChanged != null)         ListChanged(this, e);     }  

This method can be called to raise the ListChanged event by any code that inherits from our base class. Much of the remaining code in the class, then, provides a very simple implementation of the IBindingList interface:

  void IBindingList.AddIndex(PropertyDescriptor property) { }     object IBindingList.AddNew()     {       if(AllowNew)         return OnAddNew();       else         throw new InvalidOperationException("Adding items not allowed");     }     void IBindingList.ApplySort(PropertyDescriptor property,                                 ListSortDirection direction) { }     int IBindingList.Find(PropertyDescriptor property, object key) { return 0; }     void IBindingList.RemoveIndex(PropertyDescriptor property) { }     void IBindingList.RemoveSort() { }     bool IBindingList.AllowEdit { get { return AllowEdit; } }     bool IBindingList.AllowNew { get { return AllowNew; } }     bool IBindingList.AllowRemove { get { return AllowRemove; } }     bool IBindingList.IsSorted { get { return false; } }     ListSortDirection IBindingList.SortDirection     { get { return ListSortDirection.Ascending; } }     PropertyDescriptor IBindingList.SortProperty { get { return null; } }     bool IBindingList.SupportsChangeNotification { get { return true; } }     bool IBindingList.SupportsSearching { get { return false; } }   bool IBindingList.SupportsSorting { get { return false; } }     virtual protected object OnAddNew() { return null; }  } } 

Since we're not supporting searching or sorting, this code provides no real functionality ”it merely returns simple values or performs no operation. Even so, an important element of the code here is the implementation of the OnAddNew() method:

 virtual protected object OnAddNew() { return null; } 

This function is called if data binding tries to add a new object to the collection. It's up to our business implementation to override this OnAddNew() method with a version that actually does return a new, valid object.

Because the AllowNew variable defaults to false , our default behavior is to not support adding new items to the collection. This is enforced in our implementation of IBindingList.AddNew .

Note  

If the business developer changes the AllowNew value to true in his business collection class, then he must provide an implementation of the OnAddNew() method.

Finally, the BindableCollectionBase class needs to override some methods from CollectionBase itself. When an item is inserted, removed, or changed, or the collection is cleared, a method is run that we can override. In each of these cases, we know that the content of the collection has changed, so we want to raise the ListChanged event:

 override protected void OnInsertComplete(int index, object value)  {       OnListChanged(         new ListChangedEventArgs(ListChangedType.ItemAdded, index));     }     override protected void OnClearComplete()     {       OnListChanged(         new ListChangedEventArgs(ListChangedType.Reset, 0));     }     override protected void OnRemoveComplete(int index, object value)     {       OnListChanged(         new ListChangedEventArgs(ListChangedType.ItemDeleted, index));     }   override protected void OnSetComplete(int index,                                           object oldValue, object newValue)     {       OnListChanged(         new ListChangedEventArgs(ListChangedType.ItemChanged, index));     }  } } 

Now, anytime the collection's contents change, our event will be raised automatically.



Expert C# Business Objects
Expert C# 2008 Business Objects
ISBN: 1430210192
EAN: 2147483647
Year: 2006
Pages: 111
Authors: Rockford Lhotka
BUY ON AMAZON

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net