Colin Eberhardt's Technology Adventures

A gesture-driven Windows Phone to-do application Part Two – drag re-ordering

June 27th, 2012

A couple of weeks ago I blogged about a todo list application which uses gestures to achieve its basic functions, a left swipe deletes an item, while a right-swipe marks it as complete. In this blog post I am adding re-ordering which is initiated via a tap-and-hold gesture and performed via a drag.

As mentioned previously, this application is heavily inspired by the iPhone application Clear, who I give full credit for coming up with such an innovative user-interface design. This blog post is for fun an education, you are strictly prohibited from using this as the basis of a ‘Clear’ clone for WP7.

The video below shows the application so far, with the new re-order functionality:

Initiating a Drag

In the previous blog post we saw how the Toolkit GestureListener can be used to convert the low-level manipulation events into high level gestures. In order to initiate an item drag we could use the Hold event that the GestureListener exposes. The Hold event is fired when the user taps and holds the location of their finger for a few seconds. When I published my previous blogpost one of my readers Simon (Darkside) Jackson kindly pointed out that some of the gesture events are now supported by FrameworkElement directly, Hold is one such event. For this reason I’ll use FrameworkElement.Hold, the equivalent event on GestureListener should be considered deprecated.

The easiest way to allow the user to ‘pick up’ and drag the item is to clone it using a WriteableBitmap, hiding the original. This technique allows us to place the item at a higher Z-index than the list which it comes from, so that it will always be top-most as it is moved up and down the list.

We’ll add the element that is used to render the dragged item to the XAML:

<Grid>
  <ItemsControl ItemsSource="{Binding}" x:Name="todoList">
    ... markup from previous blog post
  </ItemsControl>
 
  <Grid x:Name="dragImageContainer"
        VerticalAlignment="Top"
        Visibility="Collapsed">
    <!-- the image that displays the dragged item -->
    <Image x:Name="dragImage"
          VerticalAlignment="Top">
    </Image>
 
    <!-- lower drop shadow -->
    <Rectangle Height="10"
                VerticalAlignment="Bottom">
      <Rectangle.Fill>
        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
          <GradientStop Color="#AA000000"/>
          <GradientStop Color="#00000000" Offset="1"/>
        </LinearGradientBrush>
      </Rectangle.Fill>
      <Rectangle.RenderTransform>
        <TranslateTransform Y="10"/>
      </Rectangle.RenderTransform>
    </Rectangle>
 
    <!-- upper drop shadow -->
    <Rectangle Height="10"
               VerticalAlignment="Top">
      <Rectangle.Fill>
        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
          <GradientStop Color="#00000000"/>
          <GradientStop Color="#AA000000" Offset="1"/>
        </LinearGradientBrush>
      </Rectangle.Fill>
      <Rectangle.RenderTransform>
        <TranslateTransform Y="-10"/>
      </Rectangle.RenderTransform>
    </Rectangle>
  </Grid>
</Grid>

The markup contains an image, and a couple of Rectangle elements, each of which is filled with a subtle opacity gradient and offset in order to give the impression of a drop shadow. The Source of the image is set in the Hold event handler as shown:

private void GestureListener_Hold(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
{
  _dragReOrder = true;
 
  // copy the dragged item to our 'dragImage' 
  FrameworkElement draggedItem = sender as FrameworkElement;
  var bitmap = new WriteableBitmap(draggedItem, null);
  dragImage.Source = bitmap;
  dragImageContainer.Visibility = Visibility.Visible;
  dragImageContainer.Opacity = 1.0;\
  dragImageContainer.SetVerticalOffset(draggedItem.GetRelativePosition(todoList).Y);
 
  // hide the real item
  draggedItem.Opacity = 0.0;
 
  // fade out the list
  todoList.Animate(1.0, 0.7, FrameworkElement.OpacityProperty, 300, 0);
 
  _initialDragIndex = _todoItems.IndexOf(((ToDoItem)draggedItem.DataContext));
}

The code above also hides the real object and offsets our ‘fake’ vertically so that it occupies the same position as the original. The list is also faded slightly to give a visual indication that the dragged item is now above the rest of the list:

Dragging the item

In order to allow the user to support dragging of the item we also need to handle ManipulationDelta on the Border element, as shown below:

<ItemsControl.ItemTemplate>
  <DataTemplate>
    <Border Background="{Binding Path=Color, Converter={StaticResource ColorToBrushConverter}}"
            ManipulationDelta="Border_ManipulationDelta"
            ManipulationCompleted="Border_ManipulationCompleted"
            Hold="Border_Hold"
            Canvas.ZIndex="0">
      <!-- gestures that were added in the last blog post to support delete / complete -->
      <toolkit:GestureService.GestureListener>
        <toolkit:GestureListener
                  DragStarted="GestureListener_DragStarted"
                  DragDelta="GestureListener_DragDelta"
                  DragCompleted="GestureListener_DragCompleted"
                  GestureCompleted="GestureListener_GestureCompleted"
                  Flick="GestureListener_Flick"/>
      </toolkit:GestureService.GestureListener>
 
      <Grid>
        <!-- the todo item XAML from the previous blog post -->        
      </Grid>
    </Border>
  </DataTemplate>
</ItemsControl.ItemTemplate>

So why am I handling these events on the Border rather than re-using the DragDelta / GestureComplete on the GestureListener? There are a couple of reasons:

  1. The GestureListener ‘drag’ has a tolerance (as discussed in the previous blog post). The user has to move their finger beyond a certain distance before a drag is started. In the current context we want a drag to occur as soon as the Hold event fires.
  2. When handling the element ‘drag’ we need to suppress the event by setting e.Handled=true, otherwise a drag will bubble up to the ScrollViewer that hosts our elements causing it to scroll. I found (through trial and error) that the GestureListener.DragDelta.Handled property doesn’t appear to have any effect.

The handler for the drag event is pretty simple, moving the copy of our item by the required distance:

private void Border_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
  Debug.WriteLine("ManipulationDelta");
 
  if (!_dragReOrder)
    return;
 
  // set the event to handled in order to avoid scrolling the ScrollViewer
  e.Handled = true;
 
  // move our 'drag image'.
  dragImageContainer.SetVerticalOffset(dragImageContainer.GetVerticalOffset().Value + e.DeltaManipulation.Translation.Y);
 
  ShuffleItemsOnDrag();
}

Offsetting the items underneath

The ShuffleItemsOnDrag method is where the fun starts, we’ll get to that shortly. First we’ll take a look at a simple utility method that is used to determine the index that the item being dragged would occupy if it were dropped at the present location. This is achieved by a simple measurement:

// Determines the index that the dragged item would occupy when dropped
private int GetDragIndex()
{
  double dragLocation = dragImageContainer.GetRelativePosition(todoList).Y +
                          VerticalScrollViewer.VerticalOffset +
                          dragImage.ActualHeight / 2;
  int dragIndex = (int)(dragLocation / dragImage.ActualHeight);
  return dragIndex;
}
 
private ScrollViewer _scrollViewer;
 
 
// gets the scrollviewer from the ItemsControl template
private ScrollViewer VerticalScrollViewer
{
  get
  {
    if (_scrollViewer == null)
    {
      _scrollViewer = todoList.Descendants<ScrollViewer>()
                              .Cast<ScrollViewer>()
                              .Single();
    }
    return _scrollViewer;
  }
}

The above code needs to take the current scroll location into consideration, which is why the ScrollViewer property above uses Linq-to-VisualTree to find the ScrollViewer that the ItemsControl generates to hosts our elements.

ShuffleItemsOnDrag is where the fun begins, we want to create an effect where the dragged item ‘pushes’ the other items out of the way as it hovers over them, giving the impression that the list is re-ordering as we drag.

The method below iterates over all of the items in the list to determine whether they need to be offset. An item needs to be offset if it is between the current dragged item index and the items original location.

private void ShuffleItemsOnDrag()
{
  // find its current index
  int dragIndex = GetDragIndex();
 
  // iterate over the items in the list and offset as required
  double offset = dragImage.ActualHeight;
  for (int i = 0; i < _todoItems.Count; i++)
  {
    FrameworkElement item = todoList.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
 
    // determine which direction to offset this item by
    if (i <= dragIndex && i > _initialDragIndex)
    {
      OffsetItem(-offset, item);
    }
    else if (i >= dragIndex && i < _initialDragIndex)
    {
      OffsetItem(offset, item);
    }
    else
    {
      OffsetItem(0, item);
    }
  }
}

The OffsetItem method performs the actual offset by animating the Y position of each item. The target location is stored in the elements Tag property so that we don’t repeatedly fire the same animation on an element.

private void OffsetItem(double offset, FrameworkElement item)
{
  double targetLocation = item.Tag != null ? (double)item.Tag : 0;
  if (targetLocation != offset)
  {
    var trans = item.GetVerticalOffset().Transform;
    trans.Animate(null, offset, TranslateTransform.YProperty, 500, 0);
    item.Tag = offset;
    _moveSound.Play();
  }
}

Completing the drag

When the user stops dragging the item, the ManipulationCompleted event is fired. Here we perform a number of tasks:

  1. Fade the list back to full opacity
  2. Animate the dragged item so that it ‘snaps’ into location
  3. When the above is complete, we need to re-order the underlying collection of model items, then re-populate the ObservableCollection exposed to the view. This causes all the items to be re-rendered, removing all of the TranslateTransforms that have been applied.
  4. Finally, remove the image which is our copy of the dragged item.

This sounds like a lot of work, but our Animate utility method makes it quite simple:

private void Border_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
  if (!_dragReOrder)
    return;
 
  _dragReOrder = false;
  _autoScrollTimer.Stop();
 
  int dragIndex = GetDragIndex();
 
  // fade in the list
  todoList.Animate(null, 1.0, FrameworkElement.OpacityProperty, 200, 0);
 
  // animated the dragged item into location
  double targetLocation = dragIndex * dragImage.ActualHeight - VerticalScrollViewer.VerticalOffset;
  var trans = dragImageContainer.GetVerticalOffset().Transform;
  trans.Animate(null, targetLocation, TranslateTransform.YProperty, 200, 0, null,
    () =>
    {
      // clone the list and move the dragged item
      var items = _todoItems.ToList();
      var draggedItem = items[_initialDragIndex];
      items.Remove(draggedItem);
      items.Insert(dragIndex, draggedItem);
 
      // re-populate our ObservableCollection
      _todoItems.Clear();
      _todoItems.AddRange(items);
      UpdateToDoColors();
 
      // fade out the dragged image and collapse on completion
      dragImageContainer.Animate(null, 0.0, FrameworkElement.OpacityProperty, 1000, 0, null, ()
        => dragImageContainer.Visibility = Visibility.Collapsed);
    });
 
}

Scrolling the list

The current implementation only allows the user to drag the item within the bounds of the current screen. What if the list is larger than the screen and the users want to drag right from the bottom to the top?

A common solution to this problem is to auto-scroll the list if the item is dragged near to the top. The following method is invoked periodically by a timer to see whether the item has been dragged within the top or bottom ‘scroll zones’. The velocity of the scroll is proportional to just how far within these zones the item has been dragged. Scrolling is simply a matter of setting the scroll location on the ScrollViewer we located earlier:

// checks the current location of the item being dragged, and scrolls if it is
// close to the top or the bottom
private void AutoScrollList()
{
  // where is the dragged item relative to the list bounds?
  double draglocation = dragImage.GetRelativePosition(todoList).Y + dragImage.ActualHeight / 2;
 
  if (draglocation < AutoScrollHitRegionSize)
  {
    // if close to the top, scroll up
    double velocity = (AutoScrollHitRegionSize - draglocation);
    VerticalScrollViewer.ScrollToVerticalOffset(VerticalScrollViewer.VerticalOffset - velocity);
  }
  else if (draglocation > todoList.ActualHeight - AutoScrollHitRegionSize)
  {
    // if close to the bottom, scroll down
    double velocity = (AutoScrollHitRegionSize - (todoList.ActualHeight - draglocation));
    VerticalScrollViewer.ScrollToVerticalOffset(VerticalScrollViewer.VerticalOffset + velocity);
  }
}

You can see the scroll zones illustrated below:

And finally, we are all done! With our todo application you can use flick and drag gestures to mark items as complete or delete them, and now use hold and drag to re-order. I think it’s about time I made it so that you can add or edit items. We’ll get to that next time!

You can download the sourcecode here: ClearStyle.zip

Regards, Colin E.

A Developer Perspective on Windows Phone 8 – An Update

June 22nd, 2012

My blog post from yesterday “A Developer Perspective on Windows Phone 8″ sparked a bit of furious debate on Twitter. I think it is fair to say that I was not the only one who was quite concerned about the impact of aligning the developer experience of Windows Phone 8 with Windows 8. To quote Shaw Wildermuth “As a developer who’s ported Windows Phone 7 to Windows 8 (WinRT), I can tell you it isn’t a trivial task”.

During the debate there were one or two that pointed out that the assumptions I, and many others, were making about WP8 being aligned to WinRT were incorrect. However, I think they were under NDA so could not say much more.

Shortly afterwards Cliff Simpkins, a Product Manager for WP7 at Microsoft, had this to say:

I also received a very nice email from Claus Jørgensen who said of migrating from WP7 to WP8 “Migration of your codebase will involve zero issues. The major hurdle will be to change old controls into new, better performing, controls. ”

I am much happier about this all than I was yesterday. Whilst I am not entirely convinced, finding it hard to reconcile the information that moving code from Win8 to WP8 will be easy, whilst also moving code from WP7 to WP8 will involve zero issues, it does look like the process of transitioning from WP7 development to WP8 will not be too painful.

My point still stands that the developer community doesn’t appear to be embracing WinRT in the same way it did WPF and Silverlight, however, time will tell. The recently announced Microsoft Surface does look like a pretty cool device to develop for, and might kick some life into the developer community.

Also, I do feel that Microsoft needs to provide developers with a much clearer picture of what the future holds for them. I don’t doubt that the skills we have fostered from WPF, to Silverlight, through to WinRT will serve us well for WP8 development. But skills are only one part of the story, much more important to me is our assets, the code that we have written. For my part, I have written around 80 blog posts (that’s around 200,000 words!) on XAML technologies, and this was all just for fun! For clients, we have developed so much more. Any migration path has to consider this.

Anyhow, I’m going to stop worrying about WP8 and get back to blogging about what I enjoy most. Writing code. And yes, I am still developing for WP7.

Regards, Colin E.

A Developer Perspective on Windows Phone 8

June 21st, 2012

This blog post takes a look at what Windows Phone 8 (WP8) means for Windows Phone 7 (WP7) developers and the Microsoft development platform in general.

This has been a big week for Microsoft; Monday saw them unveil the new Surface tablet that runs Windows 8. This was followed up yesterday with Window Phone 8 being revealed to the crowds at the Windows Phone Summit (You can see a video of the keynote here).

From a consumer perspective, unfortunately WP7 devices will not be upgraded to WP8. However, they will be upgraded to WP7.8, which will give them the redesigned start screen. There have been very few details about what else WP7.8 will include, but it isn’t likely to include that much.

The image above shows the new re-designed WP8 start screen. I’ll talk a little bit later about some of the interesting new features of WP8, but for now let’s cut to the chase, what does WP8 mean for us developers?

NOTE: Some of these statements are based on speculation / educated guesses. I will indicate when that is the case.

UPDATE – It looks like some of these assumptions are wrong, or at least might be wrong. I have posted a brief updated on this blog post. As noted above, some of the statements noted below are based on assumptions. I have left them here because I still am not entirely sure about what is or is not correct, but in the interest of providing a balanced argument i would urge that you read my update after this blog post.

WP8 devices will run all the 100,000 apps that are currently in the marketplace. The marketplace will automatically re-compile WP7 apps for WP8, meaning that your time investment in your WP7 app is safe. It also gives future users of WP8 a well-populated app store from day one.

WP8 has the same runtime architecture as Win8. Porting code between Win8 and WP8 should be very easy – apart from screen size concerns. However, Microsoft has so far only explicitly mentioned that C++ / DirectX code will work almost seamlessly between Win8 and WP8, however, I think it is most likely that the same will be true for XAML-based applications.

WP8 will not support Silverlight directly, rather, WP7 apps will be recompiled by the marketplace to provide a form of emulation. This is something of an educated guess. The fact that WP7 apps are recompiled via the marketplace in order to run on WP8 indicates a lack of Silverlight runtime on WP8. Also, this is the same story with Win8, WinRT/XAML is similar to Silverlight/XAML, yet subtly different.

Migration of you codebase from WP7 to WP8 will involve the same set of issues as migration from Silverlight to WinRT. Again, based on the above assumptions. Whilst on the surface Win8 XAML / WinRT looks and feels pretty similar to Silverlight or WPF, migration is not a trivial task. See Morten’s series on migrating from Silverlight to WinRT, or my own article on writing cross platform WinRT / Silverlight apps.

This is NOT the same story as iOS or Android. I’ve seen a few people on Twitter try to make the point that with the recent announcement of iOS6 that iOS developers will be experiencing the same process. I’m afraid this is not the case. With iOS you can support older versions of the OS within the same codebase via simple ‘if’ statements, whereas in Android you can support libraries to bring newer UI features to old devices. In contrast, the differences between WP7 and WP8 are fundamental and architectural.

So why such a radical change? For a good overview I’d recommend reading Sorry, But Microsoft Screwing Windows Phone Fans Is the Right Thing To Do”, in a nutshell WP7 is based on Windows CE technology, which significantly limits the hardware capabilities. Whereas the WP8 architecture provides support for up to 64 processors, a much higher screen resolution, NFC (think contact-less payment).

I’ve been using WP7 for about a year now, and I really think it is a great phone. I also have a iPhone and can honestly say that WP7 is a better phone for business users. The support for multiple mail accounts, a much better email client, live tiles, a very good calendar app make it a great device to use on a day-to-day basis. I don’t doubt that WP8 will be even better, especially with true multi-tasking, the company hub (with device management), the improved start screen and more.

However, I think Microsoft developers are feeling a bit burnt right now. Let’s not forget that WP7 was a reboot, dropping Windows Mobile 6.5. To reboot once again in such a short space of time is not good news. And as for Silverlight being dropped in Windows 8, together with other plugins, only for MS to do an about face and announce that the plugin-free IE10 browser would support Flash – that’s not good news either! At the time the message from MS evangelists was that Silverlight still lives on in WP7, but there is no the denying in Silverlight.

Ex-MVP Josh Smith wrote a few months ago “Does anyone actually care about WinRT?”. I’ve observed something similar myself (see “WinRT – where’s the buzz?”).

Microsoft is going to have to work hard to win the support of the development community that were right behind them on Silverlight and WPF (I was part of the WPF Disciples group, that was re-branded as XAML Disciples when Windwos 8 was announced, but has since become rather quiet). So many people I know really like the look of WP8, Windows 8 and Surface, but not many seem to be chomping at the bit to develop for them.

Regards, Colin E.

Microsoft Surface vs. Apple iPad – a Visual Comparison

June 20th, 2012

Yesterday Microsoft held a surprise press conference in LA when they unveiled the Microsoft Surface Tablet, a product that they have successfully managed to keep under-wraps for a couple of years (I created on online poll, and of the 74 respondents, only 25 guessed correctly).

The new tablet comes in two flavours, both of which run Windows 8. The cheaper model, Surface RT, has an ARM processor and will only run newer Metro-style Windows 8 applications (Win8 has a rather strange split personality, it is almost two separate OSs in one), while the more expensive model, Surface Pro, is a bit bulkier and includes an Intel processor that will allow it to run both Metro and traditional desktop apps. Most significantly, the Surface Pro model will run all of your existing Windows software (Word, Excel, Visual Studio, PhotoShop, …)

So why has Microsoft made this bold move? It doesn’t have much of a reputation as a hardware manufacturer!

As an owner of a Windows Phone 7 and an iPhone I think I can answer this one. Windows Phone is a really great phone OS, for the tasks I use it for (mostly email, internet and calendar), it does a great job. I have multiple mail accounts and can pin each to the start screen, or even pin folders. The live tiles and lock screen information make it very easy for me to see upcoming calendar events and mail. For business use, Windows Phone has a few great productivity features that the iPhone cannot match. I was hoping that iOS6 might re-invent the home screen to provide something more like Android’s Widget or Windows Phone’s Live Tiles; so the news that Siri could now tell me baseball scores was a bit of a disappointment!

But there’s one big problem with Windows Phone … the phone hardware itself. I have a HTC Trophy, and the overall build quality is poor. I have also used the Samsung Omnia, Lumia 710 and various other models, and none of them have impressed me. In contrast, the iPhone is practically a work of art! When you own one you can see why Apple products instil a certain sense of pride in their owners. It’s not just the cosmetics, the phone is incredibly robust and the screen and back cover are both highly scratch resistant.

With Windows 8 going head to head with the iPad, which shares the same aesthetics and build quality of the iPhone, Microsoft must have realised that quality hardware is critical to the success of Windows 8 (at least as a tablet OS).

Reports from journalists who have had a chance to have a brief play with Surface seem to indicate that it has the same top-end feel that an iPad has. It also has another trick up its sleeve – the keyboard. The Surface has a magnetically attached fold out cover, much like the iPad, however, the Windows Surface cover also doubles as a keyboard. To my mind this further underlines the Surface being pitched as a productivity tool, especially the Pro model.

I’m still in two minds about Windows 8, my feeling is that the developer community have not really embraced this OS, due to the ever moving goal-posts (from WPF to Silverlight, to Silverlight for Windows Phone, to WinRT, all similar yet quite different). This could mean a real lack of quality software when Surface hits the shops. Also, we have yet to hear about the pricing and distribution (there are hints that it might be US only at first). I am not ready to place my bets yet, but I will say this:

With Surface, Microsoft has given Windows 8 the best possible chance of success.

Regards, Colin E.