I was playing around with the DataForm control offered by the Silverlight 4 Toolkit and I realized it’s missing an IsEditing property I could have used to bind to other interface elements (and disable them while I am editing something in the DataForm).

This property will be especially useful if you use the DataForm with the AutoCommit property set to false: in this case you cannot change the CurrentItem while you are in edit mode, because the control itself will throw an exception.

Adding this property isn’t too hard because the control offers a couple of events (BeginEdit/EndEdit) you can use as a starting point. So, in my first attempt, I derived a new DataForm control, added a new IsEditing DependencyPropery and overridden the OnBeginningEdit() and OnItemEditEnded() function to set the property to the correct values.

It worked, but I had a subtle problem: the OnBeginEdit() is fired the first time when you click on the form (and you can disable the rest of the interface, but if you have AutoCommit set to false and you do not want to edit nor change the item anymore you have no way to have the EndEdit fired, because to fire it you need to switch the focus to another control...but all your interface is now disabled...and you are stuck! You are forced to edit something to be able to cancel the operation).

Solving this problem wasn’t totally hard, all you need to do is to change the IsEditing property only when you are interacting with editing controls or when the underlying bound object gets changed; so I’ve wired my state changes to the OnKeyUp() of the DataForm and to the PropertyChanged() event exposed by the bound object.

The resulting class worked quite well: the IsEditing property gets changed every time you type something in an control or when you change a property of the bound objet without keyboard interaction (like using a Slider or with a DatePicker control).

Here’s the code for the ‘new’ DataForm.

public class CustomDataForm : DataForm
{
   #region "IsEditing"
   
   public bool IsEditing
   {
       get { return (bool)GetValue(IsEditingProperty); }
       set { SetValue(IsEditingProperty, value); }
   }

   // Using a DependencyProperty as the backing store for IsEditing.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty IsEditingProperty =
       DependencyProperty.Register("IsEditing", typeof(bool), typeof(CustomDataForm), new PropertyMetadata(false));

   // we cannot use the BeginEdit event, we may have no way to exit the editing state if we do not edit anything.
   // under special conditions (like having no explicit enable/disable edit mode for the DataForm)
   //protected override void OnBeginningEdit(CancelEventArgs e)
   //{
   //    base.OnBeginningEdit(e);
   //}

   // when we edit any control inside the DataForm we set the IsEditing at true
   protected override void OnKeyUp(KeyEventArgs e)
   {
       base.OnKeyUp(e);

       IsEditing = true;
   }        

   protected override void OnCurrentItemChanged(EventArgs e)
   {
       base.OnCurrentItemChanged(e);
       SubscribeToProperyChanged();
   }

   // for controls that do not use keyboard inputs we rely on property changed of the binded object
   private void SubscribeToProperyChanged()
   {
       INotifyPropertyChanged obj = CurrentItem as INotifyPropertyChanged;
       if (obj != null)
       {
           // remove and reattach the event in case we get back to the same object or we manage a list of objects
           obj.PropertyChanged -= obj_PropertyChanged;
           obj.PropertyChanged += obj_PropertyChanged;
       }
   }

   void obj_PropertyChanged(object sender, PropertyChangedEventArgs e)
   {
       if (!IsRiaEntityProperty(e.PropertyName))
           IsEditing = true;
   }

   protected override void OnItemEditEnded(DataFormEditEndedEventArgs e)
   {
       base.OnItemEditEnded(e);

       IsEditing = false;
   }

   /// <summary>
   /// list of properties exposed by the base RIA Entity Class
   /// </summary>
   static string[] pInfos;
       
   private static bool IsRiaEntityProperty(string property)
   {
       if (pInfos == null)
       {
           Type t = typeof(Entity);
           pInfos = t.GetProperties().Select(pi => pi.Name).ToArray();
       }
       return pInfos.Contains(property);
   }

   #endregion
}

 

A little tweak have to be done when dealing with RIA services generated proxy classes: in the PropertyChanged event handler we have to check if the property that was changed was one exposed by the RIA infrastructure and in that case we do not have to set the IsEditing property (otherwise we may have side effects that will keep our UI disabled).

Now you can bind it to the ‘IsEnabled’ property of any other control:

<Button Command="{Binding DeletePageCommand}" 
        CommandParameter="{Binding Path=SelectedPage}" 
		IsEnabled="{Binding ElementName=EditForm, Path=IsEditing, ValidatesOnNotifyDataErrors=False, Converter={StaticResource NotOperatorValueConverter}}">
    <StackPanel Orientation="Horizontal">
        	...
    </StackPanel>
</Button>

 

This can be a good workaround until the guys in that develop the toolkit add a similar property to the official control release.

Related Content