| Comments

I was talking with a good friend the other day about some feedback about DataForm.  It’s great to get raw and honest feedback…that’s where you improve more than ‘it sucks’ type feedback.  One of the use cases he felt would be common with the Silverlight DataForm control (available in the Silverlight Toolkit) was the concept of confirming the delete action.  I agreed as well that confirming permanent delete actions is a common line-of-business application pattern.  It got me thinking about some things…

The Problem

DataForm is a great control.  I love it.  It will benefit many developers in the simplest applications quickly as well as some in the most complex applications with some additional configuration.  For the purposes of this demonstration, I’ll talk about the simplest scenario.  DataForm has build in controls for navigating a bound data set, as well as adding and deleting new items.  It is the deleting I’d like to concentrate on here. 

When DeleteItem is called on DataForm (via the built-in toolbar or via your own methods), it deletes the current item.  After all, that’s what you told it to do!  The problem is that the delete is fast and there is no easy “undo” method. 

The Helper

Luckily, DataForm provides a method interceptor for us: DeletingItem.  This event fires when DeleteItem is called and tells you: Hey, I’m about to delete the current item.  If you want to do anything about that, now is the time.  So there you have it…you can prevent the delete because there is a CancelEventArgs parameter passed in to cancel the remaining event.  So what would you do in DeletingItem?

Solution 1: Go modal

One thing you can do is leverage a modal dialog.  This would block the event until a modal dialog response is provided for you to investigate.  Here’s an example of what you might do in DeletingItem:

   1: private void PeopleBrowser_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
   2: {
   3:     if (MessageBox.Show("Are you sure you want to delete this item?\nThis cannot be undone", "Delete Item", MessageBoxButton.OKCancel) == MessageBoxResult.Cancel)
   4:     {
   5:         e.Cancel = true;
   6:     }
   7: }

So what’s the problem with this one?  Nothing really.  The dialog would show, giving your user a chance to react and block the delete call until an OK is received (it cancels the event if Cancel is clicked).  But let’s look what it generates from a user experience standpoint:

confirm delete with MessageBox

Hmm, maybe there is a problem.  First, it says OK or Cancel…not really a “Yes or No” response the user is really looking for.  Second, using MessageBox will focus the dialog in the center of your window and not center of parent (or calling control).  These two things make it less ideal in my opinion.  The major positive here is twofold: it works and it’s truly modal (thus blocking the call to delete).

Solution 2: Pimp your dialog, but also your code

Silverlight is all about improving the user experience right?  Changing things around and differentiating the RIA?  So let’s use that mantra to think what we could do here.  Silverlight 3 provides a new control, ChildWindow that you could use in this instance.  It provides a modal-like experience to the end user (blocking other UI components) and gives them a focused area to provide a response.

NOTE: I’ve refactored ChildWindow into something I call “FloatableWindow” for MDI or other type interfaces.  I’ve provided my code for you to use on the .  I’ve also added a work item on the Silverlight Toolkit so if you like the idea, please vote on it!

The challenge with ChildWindow is that while it exhibits all the UI experience of a modal dialog, behind the scenes it is asynchronous.  This means that if you put a Show() call to a ChildWindow in your code, that your next line of code will run as well.

NOTE: If you think this should be changed, consider voting on the Silverlight Toolkit project for this item: ChildWindow – make it modal.

We can, however, still be creative.  Let’s explore an idea here.  We know that we have the DeletingItem event we can still tap into, so we can trigger our implemented ChildWindow like this:

   1: private void PeopleBrowser_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
   2: {
   3:     ConfirmDialog cd = new ConfirmDialog();
   4:     cd.Show();
   5: }

The problem is that unless we cancel the event, the delete will still happen (and you can see it happen from behind the ChildWindow even…frustrating!  The ChildWindow.DialogResult is essentially worthless to us right now.  Let’s think creatively though.  What I did was create a member variable called _delete and set it to false.  This tells the DeletingItem event whether or not it really should delete.  It sounds dumb, I know, but work with me.

Now when we call DeletingItem, we check to see if we really should delete or not (really we check to see if we should cancel the event).  If we are still in “false” mode, then we shouldn’t delete but should show our confirmation window and cancel the DeletingItem event.  That’s great, but we still need to get the user response from the window!  We then need to tap into the ChildWindow.Closed event and get the DialogResult from there.  In the Closed event we can see if they confirmed the delete.  If they cancelled the delete, we do nothing further.  If they said “Yes” then we need to change our _delete variable and call DeleteItem on our DataForm again.  Now our DeletingItem handler knows we can successfully continue.

Sound fuzzy?  Sound like a hack?  Maybe so, but it works.  This gives us the opportunity to create a customized user experience for the confirmation.  Now I’m a crappy designer, but to make the point clear to differntiate from simple MessageBox, my ChildWindow has a flashing background and blurs the DataForm.  Yeah, it’s obnoxious, but that is the point for this demonstration!  Here’s the complete code for this solution:

   1: private bool _delete = false;
   2:  
   3: private void PeopleBrowser_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
   4: {
   5:     if (!_delete)
   6:     {
   7:         Person p = PeopleBrowser.CurrentItem as Person;
   8:         ConfirmDialog cd = new ConfirmDialog();
   9:         cd.Closed += new EventHandler(cd_Closed);
  10:         cd.Title = string.Format("Delete {0} {1}?", p.FirstName, p.LastName);
  11:         BlurEffect b = new BlurEffect();
  12:         b.Radius = 10;
  13:         PeopleBrowser.Effect = b;
  14:         cd.Show();
  15:         e.Cancel = true;
  16:     }
  17:     else
  18:     {
  19:         _delete = false;
  20:     }
  21: }
  22:  
  23: void cd_Closed(object sender, EventArgs e)
  24: {
  25:     PeopleBrowser.Effect = null;
  26:     ConfirmDialog cd = sender as ConfirmDialog;
  27:     if (cd.DialogResult == true)
  28:     {
  29:         _delete = true;
  30:         PeopleBrowser.DeleteItem();
  31:     }
  32: }

You can try this out yourself here (requires Siliverlight 3): Sample confirm delete with DataForm.  Go ahead, I’ll wait. 

Obnoxious isn’t it :-).  Of course using Expression Blend to customize your own is highly recommended!

Summary

While there is no true modal dialog in Silverlight other than MessageBox (which isn’t XAML-customizable), these are two options that provide you with the opportunity to confirm your delete action within DataForm.  Hopefully these are helpful to get you to think at least and if someone has better implementations, please share them!  You can download the complete code for this sample here: ConfirmDeleteDataForm.zip

Hope this helps!

Please enjoy some of these other recent posts...

Comments