Author Archives: jonnybekkum

Forms DataBinding – The magic sequence of BindUI/UnbindUI

If you are developing Windows Forms apps and using Csla you may get errors like “EditLevelMismatch in CopyState” or se that data values are overwritten when the dataobject is connected to the UI, diconnected from the UI or when the Form is disposed. And often/typically this happends when ComboBoxes are used on the form.

The reasion this happends is because you are making logical errors in your code with regards to DataBinding.

Csla 3.6 introduces the CslaActionExtender and additional helper classes to automate BindUI and UnbindUI but there are times when you really need to do this manually and this article will show you how to this in a proper way. The CslaActionExtender is covered in Rockys book “Expert C# 2008 Business Objects” so I will not cover here.

Whenever it is necessary to handle Bund/Unbind manually I always recommend to create the following methods in your form:

private void BindUI()
{
    // Bind ComboBox list datasources first 
    BindingHelper.RebindBindingSource(customerTypeNameValueListBindingSource,
                                        CustomerTypeNameValueList.GetNameValueList());

    // Bind dataobjects - starting with root object, child, grandchild and so on....
    BindingHelper.RebindBindingSource(testRootBindingSource, MyRoot);
}

private void UnbindUI(bool cancel)
{
    // Unbind in the opposite sequence of BindUI
    BindingHelper.UnbindBindingSource(testRootBindingSource, cancel, true);
    BindingHelper.UnbindBindingSource(customerTypeNameValueListBindingSource, false, false);
}

The correct sequence for BindUI is:

  1. First always bind the datasorces for lists used in ComboBoxes
  2. Next bind the root object
  3. If needed bind the child and grand child objects in that order

The correct sequence for Unbind is the reverse sequence of BindUI.

Why is the sequence so important? When databinding is active on one of the properties SelectedValue, SelectedItem or Text on a ComboBox the value vill be written to the underlying boound property when the prtoperty value is changed. This will happen if the list items does not exist or is removed whild databinding is active. So the key is to make sure that list items exists before the dataobject is connected to BindingSource (bound) and list items are removed only after the dataobject has been disconnected (unbound).

The last pitfall is if you (the developer) does not disconnect dataobjects from the UI when the form is closed. The sequence og disposing BindingSource will be undetermined and may or may not cause the same problems. To fix this you should always make sure to call UnbindUI in the FormClosed event.

    private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
    {
      UnbindUI(true);
    }

The sematics for Save operation is as follows:

  private void SaveMyRoot()
  {
    // assumes dataobject is valid for save, should thest for IsSavable

    UnbindUI(false);
    try
    {
      MyRoot = MyRoot.Save();
    }
    catch (DataPortalException ex)
    {
      // Handle exception the way you waant
    }
    finally
    {
      BindUI();
    }

If you download the code from CslaContrib you will find a small helper class in MyCsla.Windows.BindingHelper that helps you to do a correct Bind and Unbind:

public static class BindingHelper
{

	/// 
	/// Unbinds the binding source and the Data object. Use this Method to safely disconnect the data object from a BindingSource before saving data.
	/// 
	/// The source.
	/// if set to true then call CancelEdit else call EndEdit.
	/// if set to true this BindingSource contains the Root object. Set to false for nested BindingSources
	public static void UnbindBindingSource(BindingSource source, bool cancel, bool isRoot)
	{
		IEditableObject current = null;
		// position may be -1 if bindigsource is already unbound which results in Exception when trying to address current
		if ((source.DataSource != null) && (source.Position > -1)) {
			current = source.Current as IEditableObject;
		}

		// set Raise list changed to True
		source.RaiseListChangedEvents = false;
		// tell currency manager to suspend binding
		source.SuspendBinding();

		if (isRoot) source.DataSource = null;
		if (current == null) return;

		if (cancel)
		{
			current.CancelEdit();
		}
		else
		{
			current.EndEdit();
		}
	}

	/// 
	/// Rebinds the binding source.
	/// 
	/// The source.
	/// The data.
	public static void RebindBindingSource(BindingSource source, object data)
	{
		RebindBindingSource(source, data, false);
	}


	/// 
	/// Rebinds the binding source.
	/// 
	/// The source.
	/// The data.
	/// if set to true then metadata (ovject/list type) was changed.
	public static void RebindBindingSource(BindingSource source, object data, bool metadataChanged)
	{
		if (data != null)
		{
			source.DataSource = data;
		}

		// set Raise list changed to True
		source.RaiseListChangedEvents = true;
		// tell currency manager to resume binding 
		source.ResumeBinding();
		// Notify UI controls that the dataobject/list was reset - and if metadata was changed 
		source.ResetBindings(metadataChanged);
	}
}

Using custom FieldData in Csla 3.1.7 for Net 2.0

Jason Bock wrote a great article on his blog about Creating custom Fielddata classes in Csla that requires Csla 3.7.1 of newer for .Net 3.5 Sp1. If you havent already read his article please do to get a better understanding of what custom FieldData can bring to your Csla solution.

However most of my worktime is using the Csla 3.71. for .Net 2.0 so how could I use these custom fielddata in a .Net 2.0 solution and still be able to port/recompile my code with Csla 3.7.1 for .Net 3.5?

Our customers/users have also requested our apps to be more precise in determining if data has been changed or not, ie has the propertyValue actually changed from the originalvalue. The introduction of custom field data would give us this with minimal effort. I can also vision custom FieldData beein used for auditing purposes because they may track both original and new field value.

The business objects in Jasons sample uses RegisterProperty with lambdas that are actually Expressions. So I had to alter my intermediate classes in MyCsla to add new registerProperty overloads that take a string propertyName instead of a lambda expression.  Latest version of MyCsla for Csla 3.7.1 N2 and the entire CunstomFieldData sample project is available for download on the CslaContrib project on CodePlex.

  [Serializable]
  public class BusinessBase<T> : Csla.BusinessBase<T> where T : BusinessBase<T>
  {
    #region RegisterProperty
    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName));
    }

    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName));
    }

    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, RelationshipTypes relationship)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, relationship));
    }

    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, P defaultValue)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, defaultValue));
    }

    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, P defaultValue, RelationshipTypes relationship)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, defaultValue, relationship));
    }
    #endregion
  }

Next we need to alter BusinessCore base class in CustomFieldData project to inherit from MyCsla.BusinessBase  to get the new RegisterProperty overloads:

  [Serializable]
  public abstract class BusinessCore : MyCsla.BusinessBase where T: BusinessCore

And finally change the test business object to use the new RegisterProperty.

  [Serializable]
  public sealed class Person : BusinessCore
  {
    private static PropertyInfo ageProperty =
	                    RegisterProperty("Age");
    private static PropertyInfo firstNameProperty =
			    RegisterProperty("FirstName", "Friendly first name");
    private static PropertyInfo lastNameProperty =
			    RegisterProperty("LastName", "Friendly lastname", String.Empty);

And thats just about it. The custom FieldData works great!!

Download the latest version of MyCsla for Csla 3.7.1 for N2 with CustomFieldData project from CslaContrib

[Serializable]
public class BusinessBase<T> : Csla.BusinessBase<T> where T : BusinessBase<T>
{
#region RegisterProperty
protected static PropertyInfo<P> RegisterProperty<P>(string propertyName)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName));
}protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName));
}protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, RelationshipTypes relationship)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, relationship));
}

protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, P defaultValue)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, defaultValue));
}

protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, P defaultValue, RelationshipTypes relationship)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, defaultValue, relationship));
}
#endregion

protected override void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)
{
Debug.Print(“DataPortalInvoke object:{0}, operation:{1}”, e.ObjectType, e.Operation);
base.DataPortal_OnDataPortalInvoke(e);
}

protected override void DataPortal_OnDataPortalInvokeComplete(DataPortalEventArgs e)
{
Debug.Print(“DataPortalInvokeCompleted object:{0}, operation:{1}”, e.ObjectType, e.Operation);
base.DataPortal_OnDataPortalInvokeComplete(e);
}

protected override void DataPortal_OnDataPortalException(DataPortalEventArgs e, Exception ex)
{
Debug.Print(“DataPortalExeption object:{0}, operation:{1}, exception:{2}”, e.ObjectType, e.Operation, ex);
base.DataPortal_OnDataPortalException(e, ex);
}

}

CSLA 3.7.1 for .Net 2.0 is released

Csla 3.7.1 for .Net 2.0 SP1 was released on the 30. of September.  This release includes:

  • bugfixes to FilteredBindingList/SortedBindingList
  • updates to ApplicationContext – LogicalExecutionLocation
  • and the new PropertyInfoFactory that allows you to create your own custom field data

All the Business Objects you write using Csla 3.7.1 for N2 will also work and compile with the “full” Csla 3.7.1 for .Net 3.5 when you are ready to move to the latest version of .Net.

Unfortunately there are still many developers working with legacy systems or have to support their solutions on Windows2000 platform that limits their choice of .Net framwork to version 2.0. As our client is stuck with a similar condition I have “downgraded” Csla from .Net 3,5 to run on .Net 2.0 with SP1. My version for N2 also inludes LinqBridge from www.albahari.com that enables nearly all of C# 3.0 syntax inlunding Linq over IEnumerable.

If you are interested in using Csla make sure to download the samples and look at the code.