| 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!

| Comments

I got enough feedback and suggestions that I figured it would be better just to put the code up on CodePlex rather than package zips on my blog :-).  Here it is: FloatableWindow project.  The latest build I have is up there which incorporates some feedback that I’ve received.

UPDATE: If you like this idea VOTE FOR IT in the Silverlight Toolkit!

Basically the ShowDialog() API operates the same way that ChildWindow.Show() does today.  No changes there, popup is used.  But when you just want some simple MDI type windows, use Show() which will not use Popup but rather add the elements as children to your root visual.  Now the key here is defining that root.  Before you show the window you’d want to do something like this:

   1: FloatableWindow fw = new FloatableWindow();
   2: fw.Width = 200;
   3: fw.Height = 200;
   4: fw.Title = "Foobar";
   5: fw.ParentLayoutRoot = this.LayoutRoot;
   6: fw.VerticalOffset = 200;
   7: fw.HorizontalOffset = 100;
   8: fw.Show();

Notice line #5 where I specify a Panel element to add it to?  That would be required.  An ArgumentNullException is thrown if it is not provided.

Thanks for the great feedback and encouragement on this refactoring.  I hope that putting it on CodePlex provides a better home for evolution and tracking issues (I know there is an animation issue now with non-modal).

| Comments

UPDATE: FloatableWindow is now on CodePlex for easier community contributions and management of latest source and builds.  FloatableWindow CodePlex Project. If you like this idea VOTE FOR IT in the Silverlight Toolkit!

A while back I wrote a post where I refactored the Silverlight ChildWindow to create a non-modal window and I called it FloatableWindow (you can see why I’m not a good marketer…hey I could have called it Microsoft Silverlight ChildWindow Refactored Edition Express).  A few folks found it useful, for which I’m appreciative of the comments.

My buddy Karl Shifflett decided to use it in Glimpse as the diagnostic window – thanks Karl!  Karl even added some code to it to basically provide a start position (HorizontalOffset and VerticalOffset).  Another request Karl had was to add resizing capabilities to the window.

I’m pleased to say that I added both of these features to the source.  I modified Karl’s offset properties a bit to not only include them in the Show() override, but also as public properties that would be used if available.  For resizing, I added a ResizeMode property which matches the System.Window.ResizeMode enumeration.  I chose to use that enumeration for some consistency with WPF APIs, but frankly it really only supports CanResize and NoResize with the default being CanResize.  You can see an example of the resizing capabilities here in this animation:

As you can see (sorry for the horrible animated image but just wanted to make it simple), the resizing handle will appear in the lower right corner of the window.

UPDATE: Based on Laurent/yaip’s feedback I reverted back to my original design, here’s the mouse out (normal) and mouse over states for the handle:

 

  I chose to make it invisible unless someone moves their mouse into that position.  Is that the wrong UX do you think?  I know it isn’t entirely consistent with something like WinForms, but I wanted to be a little different. 

Additionally I wanted to use the SizeNWSE Cursor as a normal WPF/WinForm window would use, but unfortunately that’s not a valid Cursor for Silverlight right now.

I did try to make the resizing adorner a template part so you could make it something else (right now it is just a path).  I haven’t really tried messing with it that much though.

I think my math might be a bit off in some scenarios and I’m still frustrated with the Popup and ZIndex issue in Silverlight, but I know there is an open issue for Silverlight to look at this so I’m trying to stay calm about it.  Anyhow, some subtle adds to make it hopefully just a bit more useful for anyone who cares (or cares to learn from it).

You can download the source code here: FloatableWindow_1.3.zip

Hope this helps and I appreciate any comments regarding the update or if you see issues in your scenarios. 

| Comments

UPDATE: FloatableWindow is now on CodePlex for easier community contributions and management of latest source and builds.  FloatableWindow CodePlex Project.  If you like this idea VOTE FOR IT in the Silverlight Toolkit!

I’ve seen a few comments/requests incoming lately that people like the ChildWindow control in the Silverlight 3 SDK.  This is a great control that creates a modal dialog for you.  When you use it, it disables your root layout application and shows the dialog you provide:

ChildWindow example

This is great for those true modal needs.  It responds to normal windows DialogResult type responses if you have buttons, etc. – great for error dialogs, logins, etc.  The request I’ve been seeing is for the same functionality, but in a ‘normal’ ChildWindow.

But I’ve been seeing requests that it should act more like the normal Window object in .NET, which has an option for showing a window as a ‘dialog’ (modal) versus a normal one.  Well, since the source code is available under Ms-PL license at the Silverlight Toolkit project, I decided to play around and refactor a bit.  If you look at the base class implementation of Window, the API shows two methods: Show and ShowDialog.  This is what I wanted to emulate.  I changed the ChildWindow to FloatableWindow only because I couldn’t think of a better name and it represented a desired behavior.  In ChildWindow, the default behavior is a modal dialog and there are a few key areas that drive this behavior.   There is a template part called Overlay that is responsible for the faded-out background of your app when the ChildWindow is shown.  The other modal semantics are driven by looking at the RootLayout of the parent creating the ChildWindow and changing its properties (IsEnabled=False).  Basically I just spelunked these areas and did some changing.

I implemented a property IsModal (_modal for the private accessor) that would be set in my new show methods, which I refactored to Show, ShowDialog and ShowWindow (internal).  ShowWindow would accept a boolean whether it was to be a modal or not, setting the private accessor.  The calls checking for disabling RootLayout, UpdateOverlaySize, ChangeVisualState and some of the focus event handling (as in a non-modal you may have multiple and you want each to be able to have focus).  After doing this I could create non-modal windows easily:

   1: FloatableWindow fw = new FloatableWindow();
   2: fw.Title = "Testing FloatableWindow";
   3: fw.Height = 200;
   4: fw.Width = 200;
   5: fw.Content = "Created at " + DateTime.Now.ToLongTimeString();
   6:  
   7: fw.Show(); //for non-modal
   8: fw.ShowDialog(); //for modal dialog

And I could create multiple:

FloatableWindow

All the other functions of the ChildWindow are there so there wasn’t a lot of work to do.  I kept the template parts, etc. so it is customizable in tools like Expression Blend.  There are a few things that I still need to do:

  • Better enable a default start position for the FloatableWindow
  • On focus events, if there are multiple windows, make sure that the selected window gets brought to the front of the layout when being used (NOTE: the logic for this is working actually, but ZIndex doesn’t appear to be able to be set on Popup, which I’m talking with the team about)
  • Whatever other bugs I may have caused by my assumptions :-)

But it was an experiment at least and seems to work so far.  Here’s the code: FloatableWindow_1.1.zip.  The archive file includes the code I used as well as Visual Studio item templates (C# and Visual Basic) to mirror the same functionality of Add New Item where you can add a new ChildWindow to your project as a user control. 

FloatableWindow Item Templates

Let me know what you think.  Hope this helps!