Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Multi-touch development with WPF - A multi-touch RSS reader

, 1 Nov 2009
A multi-touch RSS reader built with Multi-Touch Vista.
MultiTouchRSS_Demo.zip
MultiTouchVista
AddIns
MultipleMice
MultipleMice.dll
TuioProvider
TUIO.dll
TuioProvider.dll
Framework
AdvanceMath.dll
DynamicUtilities.dll
Multitouch.Framework.dll
Multitouch.Framework.WPF.dll
Physics2DDotNet.dll
HIDLibrary.dll
ManagedWinapi.dll
Microsoft.Practices.Composite.dll
Microsoft.Practices.Composite.Presentation.dll
Microsoft.Practices.Composite.UnityExtensions.dll
Microsoft.Practices.ObjectBuilder2.dll
Microsoft.Practices.ServiceLocation.dll
Microsoft.Practices.Unity.dll
Multitouch.Configuration.dll
Multitouch.Configuration.Resources.dll
Multitouch.Configuration.Resources.ilk
Multitouch.Configuration.WPF.exe
Multitouch.Contracts.dll
Multitouch.Driver.Console.exe
Multitouch.Driver.Logic.dll
Multitouch.Driver.Service.exe
Multitouch.Framework.dll
Multitouch.Service.Console.exe
Multitouch.Service.exe
Multitouch.Service.Logic.dll
System.ComponentModel.Composition.dll
TouchReader
AdvanceMath.dll
DynamicUtilities.dll
Interop.Microsoft.Feeds.Interop.dll
Multitouch.Framework.dll
Multitouch.Framework.WPF.dll
MvvmFoundation.Wpf.dll
Physics2DDotNet.dll
Ricciolo.PaperBoy.Feeds.dll
TouchReader.exe
MultiTouchRSS_Source.zip
Behavior
Factory
ViewFactory.cd
HTMLConverter
MultiTouchVista
AddIns
MultipleMice
MultipleMice.dll
TuioProvider
TUIO.dll
TuioProvider.dll
Framework
AdvanceMath.dll
DynamicUtilities.dll
Multitouch.Framework.dll
Multitouch.Framework.WPF.dll
Physics2DDotNet.dll
HIDLibrary.dll
ManagedWinapi.dll
Microsoft.Practices.Composite.dll
Microsoft.Practices.Composite.Presentation.dll
Microsoft.Practices.Composite.UnityExtensions.dll
Microsoft.Practices.ObjectBuilder2.dll
Microsoft.Practices.ServiceLocation.dll
Microsoft.Practices.Unity.dll
Multitouch.Configuration.dll
Multitouch.Configuration.Resources.dll
Multitouch.Configuration.Resources.ilk
Multitouch.Configuration.WPF.exe
Multitouch.Contracts.dll
Multitouch.Driver.Console.exe
Multitouch.Driver.Logic.dll
Multitouch.Driver.Service.exe
Multitouch.Framework.dll
Multitouch.Service.Console.exe
Multitouch.Service.exe
Multitouch.Service.Logic.dll
System.ComponentModel.Composition.dll
MvvmFoundation
MvvmFoundation.Wpf.dll
PaperBoy
Interop.Microsoft.Feeds.Interop.dll
Ricciolo.PaperBoy.Feeds.dll
Properties
Settings.settings
Resources
Images
feed.png
folder.png
trash.png
View
ViewModel
ViewModel.cd
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Multitouch.Framework.WPF.Input;
using VirtualDreams.TouchReader.ViewModel;

namespace VirtualDreams.TouchReader.Behavior
{
    public class TrashBehavior
    {
        private static readonly Dictionary<DoubleAnimation, FrameworkElement> AnimationViews =
            new Dictionary<DoubleAnimation, FrameworkElement>();

        private static readonly TimeSpan DefaultAnimationDuration = TimeSpan.FromSeconds(0.5);

        private static readonly List<Trash> Trashes = new List<Trash>();

        #region SourcePanel

        /// <summary>
        /// SourcePanel Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty SourcePanelProperty =
            DependencyProperty.RegisterAttached("SourcePanel", typeof (Panel), typeof (TrashBehavior),
                                                new FrameworkPropertyMetadata(null,
                                                                              new PropertyChangedCallback(
                                                                                  OnSourcePanelChanged)));

        /// <summary>
        /// Gets the SourcePanel property.  This dependency property 
        /// indicates which panel uses this element as trash.
        /// </summary>
        public static Panel GetSourcePanel(DependencyObject d)
        {
            if (d == null)
                throw new ArgumentNullException("d");
            return (Panel) d.GetValue(SourcePanelProperty);
        }

        /// <summary>
        /// Sets the SourcePanel property.  This dependency property 
        /// indicates which panel uses this element as trash.
        /// </summary>
        public static void SetSourcePanel(DependencyObject d, Panel value)
        {
            if (d == null)
                throw new ArgumentNullException("d");
            d.SetValue(SourcePanelProperty, value);
        }

        /// <summary>
        /// Handles changes to the SourcePanel property.
        /// </summary>
        private static void OnSourcePanelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var trash = d as Trash;
            if (trash == null)
                throw new InvalidOperationException(
                    "The SourcePanel attached property can only be applied to Trash controls.");

            var oldPanel = e.OldValue as Panel;
            if (oldPanel != null)
                MultitouchScreen.RemovePreviewContactRemovedHandler(oldPanel, SourcePanel_PreviewContactRemoved);

            var panel = e.NewValue as Panel;
            if (panel != null)
            {
                if (!Trashes.Contains(trash)) Trashes.Add(trash);
                MultitouchScreen.AddPreviewContactRemovedHandler(panel, SourcePanel_PreviewContactRemoved);
            }
            else
            {
                if (Trashes.Contains(trash)) Trashes.Remove(trash);
            }
        }

        #endregion

        private static void SourcePanel_PreviewContactRemoved(object sender, ContactEventArgs e)
        {
            var panel = (Panel) sender;

            var element = e.Contact.Captured as FrameworkElement;
            if (element == null || element is Trash || IsRootFolder(element)) return; // root folder can't be removed

            foreach (Trash trash in Trashes)
            {
                Rect trashRect = GetBoundingBox(trash, panel);
                Rect elementTrashIntersection = Rect.Intersect(GetBoundingBox(element, panel), trashRect);
                var contactRect = new Rect(e.Contact.GetPosition(panel),
                                           new Size(e.Contact.BoundingRect.Width, e.Contact.BoundingRect.Height));
                Rect contactTrashIntersection = Rect.Intersect(contactRect, trashRect);
                if ((elementTrashIntersection.Width > 0 || elementTrashIntersection.Height > 0) &&
                    (contactTrashIntersection.Width > 0 || contactTrashIntersection.Height > 0) &&
                    panel.Children.Contains(element))
                {
                    RemoveView(panel, element, e.Contact.GetPosition(panel));
                    break;
                }
            }
        }

        private static bool IsRootFolder(FrameworkElement element)
        {
            var viewModel = element.DataContext as FolderViewModel;
            return (viewModel != null && viewModel.IsRoot);
        }

        private static Rect GetBoundingBox(FrameworkElement element, Visual visual)
        {
            GeneralTransform transform = element.TransformToVisual(visual);
            Point topLeft = transform.Transform(new Point(0, 0));
            Point bottomRight = transform.Transform(new Point(element.ActualWidth, element.ActualHeight));
            return new Rect(topLeft, bottomRight);
        }

        public static void RemoveView(Panel panel, FrameworkElement view, Point animationDestination)
        {
            RemoveView(panel, view, animationDestination, DefaultAnimationDuration);
        }

        public static void RemoveView(Panel panel, FrameworkElement view, Point animationDestination,
                                      Duration animationDuration)
        {
            if (!panel.Children.Contains(view)) return;

            var zeroAnimation = new DoubleAnimation(0, animationDuration) {AccelerationRatio = 1};

            Rect viewBoundingBox = GetBoundingBox(view, panel);

            var scale = new ScaleTransform(1, 1)
                            {
                                CenterX = view.ActualWidth/2 + (animationDestination.X - viewBoundingBox.Left)/2,
                                CenterY = view.ActualHeight/2 + (animationDestination.Y - viewBoundingBox.Top)/2
                            };

            var group = view.RenderTransform as TransformGroup;
            if (group != null)
                group.Children.Add(scale);
            else
                view.RenderTransform = new TransformGroup {Children = {view.RenderTransform, scale}};

            AnimationViews.Add(zeroAnimation, view);

            view.BeginAnimation(UIElement.OpacityProperty, zeroAnimation);
            scale.BeginAnimation(ScaleTransform.ScaleXProperty, zeroAnimation);
            scale.BeginAnimation(ScaleTransform.ScaleYProperty, zeroAnimation);

            zeroAnimation.CurrentStateInvalidated += AnimationCurrentStateInvalidated;
        }

        private static void AnimationCurrentStateInvalidated(object sender, EventArgs e)
        {
            var da = sender as DoubleAnimation;
            if (da != null)
            {
                FrameworkElement view = AnimationViews[da];
                var parent = view.Parent as Panel;
                if (parent != null) parent.Children.Remove(view);
                AnimationViews.Remove(da);
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

Roberto Sonnino
Virtual Dreams
Brazil Brazil
Hi! I'm Roberto. I'm a Brazilian Engineering student at the University of São Paulo and the Ecole Centrale de Lille (France).
 
I've participated in the Imagine Cup competition and went to the world finals every year from 2005 to 2009. I also won the 1st place award in 2006, in India, for the Interface Design invitational, in 2007 in Korea, for the Embedded Development invitational, and in 2009 in Egypt for the Windows Mobile Award.
 
Currently I keep a blog (in English and Portuguese) at http://virtualdreams.com.br/blog/ and a weekly webcast about WPF and Silverlight (in Portuguese) at http://www.xamlcast.net.
Follow on   Twitter

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 1 Nov 2009
Article Copyright 2009 by Roberto Sonnino
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid