Microsoft Surface示例之PhotoPaint
PhotoPaint是微软为Microsoft Surface给出的一个经典示例,此示例展示可在一张图片上绘图,就像Windows系统自带画图软件铅笔的效果,下面先看一下效果:
下面是代码部分:
Xaml Code:
<s:SurfaceWindow x:Class="PhotoPaint.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="http://schemas.microsoft.com/surface/2008" xmlns:pp="clr-namespace:PhotoPaint" Title="PhotoPaint"> <s:SurfaceWindow.Resources> <Grid x:Key="EditButtonImage" Width="38" Height="38" VerticalAlignment="Center" Background="Transparent" > <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Path x:Name="Pencil1" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Data="F1 M 5.82884,16.4138L 0.172017,17.828L 1.58624,12.1711L 13.5853,0.171997L 17.828,4.41467L 5.82884,16.4138 Z M 2.50753,13.0924L 1.98912,15.1661L 2.83398,16.0109L 4.90755,15.4925L 2.50753,13.0924 Z " Margin="0" Grid.Column="1" Grid.Row="1"/> </Grid> <Grid x:Key="EraseButtonImage" Background="Transparent" VerticalAlignment="Center" Height="38" Width="38"> <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Path x:Name="Path" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Data="F1 M 0.500891,21.1431L 1.87231,18.6184L 10.1914,25.9037C 10.8905,26.5158 11.9534,26.4453 12.5656,25.7463L 28.8125,7.19364L 28.8125,12.4007L 12.9586,31.4896C 11.7713,32.9192 9.64985,33.1157 8.22021,31.9283L 0.946734,25.2718C -0.482872,24.0844 0.000349879,22.4327 0.500891,21.1431 Z M 19.1382,1.0865C 20.3256,-0.343134 21.389,-0.311098 22.8186,0.876265L 28.204,5.33469L 12.4081,23.3724C 11.7959,24.0714 10.7329,24.1419 10.0339,23.5297L 2.86836,17.2547L 19.1382,1.0865 Z " Margin="0" Grid.Row="1" Grid.Column="1"/> </Grid> </s:SurfaceWindow.Resources> <!-- Window Content --> <Grid> <s:ScatterView> <!-- Drawing Pad - InkCanvas with white background and small border --> <s:ScatterViewItem Height="317" Width="423" MinWidth="293" MaxWidth="846" Background="{DynamicResource {x:Static s:SurfaceColors.LibraryControlManipulationAreaBackgroundBrushKey}}"> <Grid> <!-- Row and column definitions to position content and buttons --> <Grid.RowDefinitions> <RowDefinition Height="7" /> <RowDefinition /> <RowDefinition Height="52" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="7" /> <ColumnDefinition /> <ColumnDefinition Width="44" /> <ColumnDefinition Width="44" /> <ColumnDefinition Width="44" /> <ColumnDefinition /> <ColumnDefinition Width="7" /> </Grid.ColumnDefinitions> <!-- The Ink Canvas --> <Viewbox Grid.Column="1" Grid.ColumnSpan="5" Grid.Row="1" Grid.RowSpan="1" StretchDirection="Both" Stretch="Fill"> <s:SurfaceInkCanvas x:Name="DrawingPadCanvas" StrokeCollected="OnStrokeCollected" Height="263" Width="414" Background="{DynamicResource {x:Static s:SurfaceColors.LibraryControlScrollAreaBackgroundBrushKey}}" /> </Viewbox> <!-- Edit Mode --> <s:SurfaceButton Name="EditModeButton" Grid.Row="2" Grid.Column="2" Padding="0" Margin="3.5,7,3.5,7" Click="EditModeClicked"> <Grid x:Name="DrawButtonImage" Width="38" Height="38" VerticalAlignment="Center" Background="Transparent" > <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Path x:Name="Pencil2" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Data="F1 M 5.82884,16.4138L 0.172017,17.828L 1.58624,12.1711L 13.5853,0.171997L 17.828,4.41467L 5.82884,16.4138 Z M 2.50753,13.0924L 1.98912,15.1661L 2.83398,16.0109L 4.90755,15.4925L 2.50753,13.0924 Z " Margin="0" Grid.Column="1" Grid.Row="1"/> </Grid> </s:SurfaceButton> <!-- Undo Button --> <s:SurfaceButton Grid.Row="2" Grid.Column="4" Padding="0" Margin="3.5,7,3.5,7" Click="UndoClicked"> <Grid x:Name="Undo" Width="38" Height="38" Background="Transparent" VerticalAlignment="Center" > <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Path x:Name="Undo1" Width="20" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Data="F1 M 0.747703,0.413038L 3.56185,3.22719C 5.62339,0.955633 8.52644,-0.170475 11.3426,0.524384C 15.7386,1.60904 18.258,6.72012 16.97,11.9404C 16.3949,14.2711 15.1581,16.2295 13.5738,17.587L 12.1257,16.1389C 13.4472,15.0512 14.4955,13.4139 14.9802,11.4494C 15.9971,7.32806 14.1487,3.32765 10.8517,2.51414C 8.75505,1.99682 6.57753,2.89283 5.00835,4.67369L 7.92133,7.58666L 0.747703,7.58678L 0.747703,0.413038 Z " HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center" Grid.Column="1" Grid.Row="1"/> </Grid> </s:SurfaceButton> <!-- Current Color Button --> <Ellipse Name="CurrentColor" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="2" Grid.Column="3" Margin="3.5,7,3.5,7" Fill="Orange" MouseDown="OnCurrentColorMouseDown" MouseUp="OnMouseUp" TouchDown="OnCurrentColorTouchDown" LostMouseCapture="OnCurrentColorLostMouseCapture" LostTouchCapture="OnCurrentColorLostTouchCapture" s:TouchExtensions.TapGesture="OnCurrentColorTap"/> <!-- Color Wheel --> <Image Name="ColorWheel" Source="Resources\ColorWheel.png" RenderTransformOrigin="0.5, 0.5" Grid.Row="2" Grid.Column="3" Visibility="Hidden" MouseDown="OnColorWheelMouseDown" MouseUp="OnMouseUp" TouchDown="OnColorWheelTouchDown" LostMouseCapture="OnColorWheelLostMouseCapture" LostTouchCapture="OnColorWheelLostTouchCapture"> <Image.RenderTransform> <!-- Transform it so it extends beyond the borders of the grid --> <ScaleTransform ScaleX="4" ScaleY="4" /> </Image.RenderTransform> </Image> </Grid> </s:ScatterViewItem> <!-- Photo Pad - Transparent InkCanvas with transparent background overlaid on an image --> <s:ScatterViewItem x:Name="PhotoPadSVI" Height="320" Width="427" MinWidth="296"> <Grid> <!-- Row and column definitions to position content and buttons --> <Grid.RowDefinitions> <RowDefinition Height="7" /> <RowDefinition /> <RowDefinition Height="52" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="44" /> <ColumnDefinition /> <ColumnDefinition Width="44" /> <ColumnDefinition Width="44" /> <ColumnDefinition /> <ColumnDefinition Width="44" /> </Grid.ColumnDefinitions> <!-- The Ink Canvas --> <Grid Grid.Column="0" Grid.ColumnSpan="7" Grid.Row="0" Grid.RowSpan="5"> <Image Name="Photo" /> <Viewbox StretchDirection="Both" Stretch="Fill"> <s:SurfaceInkCanvas x:Name="PostcardCanvas" IsHitTestVisible="False" Height="320" Width="426" Background="Transparent" UsesTouchShape="False" /> </Viewbox> </Grid> <!-- Clear --> <s:SurfaceButton Grid.Row="2" Grid.Column="2" Padding="0" Margin="3.5,7,3.5,7" Click="ClearClicked"> <Grid x:Name="X" Width="38" Height="38" Background="Transparent" VerticalAlignment="Center" > <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Path x:Name="X2" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Data="F1 M 0.161144,1.57529L 1.57536,0.161083L 9.00003,7.58576L 16.4247,0.161139L 17.8389,1.57535L 10.4142,8.99997L 17.8388,16.4245L 16.4246,17.8387L 9.00004,10.4142L 1.57542,17.8388L 0.161202,16.4246L 7.58582,8.99997L 0.161144,1.57529 Z " Margin="0" Grid.Column="1" Grid.Row="1" /> </Grid> </s:SurfaceButton> <!-- Move/Draw Mode --> <s:SurfaceButton Grid.Row="2" Grid.Column="3" Padding="0" Margin="3.5,7,3.5,7" Click="InkCanvasOnOffChanged"> <Grid x:Name="MoveButtonImage" Width="38" Height="38" Background="Transparent" VerticalAlignment="Center"> <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Path x:Name="Resize1" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Data="F1 M 8.99998,-8.2016e-005L 12,2.99996L 9.99998,2.99996L 9.99998,7.99992L 15,7.99996L 15,6L 18,9L 15,12L 15,9.99996L 9.99998,9.99992L 9.99998,15.0001L 12,15.0001L 8.99996,18.0001L 5.99996,15.0001L 7.99998,15.0001L 7.99998,9.99992L 2.99999,9.99996L 2.99998,12L -1.90735e-005,8.99996L 3.00002,5.99996L 3.00001,7.99996L 7.99998,7.99992L 7.99998,2.99992L 5.99998,2.99992L 8.99998,-8.2016e-005 Z " HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1" Grid.Row="1"/> </Grid> </s:SurfaceButton> </Grid> </s:ScatterViewItem> <!-- Movie Pad - Transparent InkCanvas with transparent background overlaid on a movie --> <s:ScatterViewItem Height="320" Width="426" MinWidth="296" MaxWidth="853" Background="{DynamicResource {x:Static s:SurfaceColors.LibraryControlManipulationAreaBackgroundBrushKey}}"> <Grid> <!-- Row and column definitions to position content and buttons --> <Grid.RowDefinitions> <RowDefinition Height="29" /> <RowDefinition /> <RowDefinition Height="52" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="44" /> <ColumnDefinition /> <ColumnDefinition Width="44" /> <ColumnDefinition Width="44" /> <ColumnDefinition Width="44" /> <ColumnDefinition /> <ColumnDefinition Width="44" /> </Grid.ColumnDefinitions> <!-- The Ink Canvas --> <Grid Grid.Column="0" Grid.ColumnSpan="7" Grid.Row="1" Grid.RowSpan="1"> <MediaElement x:Name="Movie" LoadedBehavior="Manual" UnloadedBehavior="Manual" MediaEnded="OnMediaEnded" /> <Viewbox StretchDirection="Both" Stretch="Fill"> <!--Events on--> <s:SurfaceInkCanvas x:Name="MovieCanvas" Height="240" Width="427" Background="Transparent" IsHitTestVisible="false" UsesTouchShape="False" PreviewMouseLeftButtonDown="OnMovieCanvasPreviewLeftMouseDown" PreviewMouseMove="OnMovieCanvasPreviewMouseMove" PreviewMouseLeftButtonUp="OnMovieCanvasPreviewLeftMouseUp" PreviewTouchDown="OnMovieCanvasPreviewTouchDown" PreviewTouchMove="OnPreviewTouchMoveOrLostCapture" LostTouchCapture="OnPreviewTouchMoveOrLostCapture"> </s:SurfaceInkCanvas> </Viewbox> </Grid> <!-- Record --> <s:SurfaceButton x:Name="RecordButton" Grid.Row="2" Grid.Column="2" Padding="0" Margin="3.5,7,3.5,7" VerticalAlignment="Center" Click="OnRecordButtonPressed"> <Grid x:Name="AnotateVideoImage" Width="38" Height="38" VerticalAlignment="Center" Background="Transparent" > <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Path x:Name="Pencil" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Data="F1 M 5.82884,16.4138L 0.172017,17.828L 1.58624,12.1711L 13.5853,0.171997L 17.828,4.41467L 5.82884,16.4138 Z M 2.50753,13.0924L 1.98912,15.1661L 2.83398,16.0109L 4.90755,15.4925L 2.50753,13.0924 Z " Margin="0" Grid.Column="1" Grid.Row="1"/> </Grid> </s:SurfaceButton> <!-- Pause --> <s:SurfaceButton x:Name="PauseButton" Grid.Row="2" Grid.Column="3" Padding="0" Margin="3.5,7,3.5,7" VerticalAlignment="Center" Click="OnPauseButtonPressed"> <Grid x:Name="Pause" Height="38" Width="38" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Grid HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center" Grid.Column="1" Grid.Row="1"> <Rectangle x:Name="Rectangle_Copy2" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Margin="9,0,0,0" Height="19" HorizontalAlignment="Left" Width="5" /> <Rectangle x:Name="Rectangle_Copy3" Width="5" Stretch="Fill" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" HorizontalAlignment="Left" Margin="0" Height="19" /> </Grid> </Grid> </s:SurfaceButton> <!-- Play --> <s:SurfaceButton x:Name="PlayButton" Grid.Row="2" Grid.Column="4" Padding="0" Margin="3.5,7,3.5,7" VerticalAlignment="Center" Click="OnPlayButtonPressed"> <Grid x:Name="Play" Background="Transparent" Width="38" Height="38"> <Grid.RowDefinitions> <RowDefinition Height="0.25*"/> <RowDefinition/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Path x:Name="Play1" Fill="{DynamicResource {x:Static s:SurfaceColors.ButtonForegroundBrushKey}}" Stretch="Fill" Margin="0" VerticalAlignment="Center" Height="19" Data="M0,0 L0,18 9,9 z" Grid.Column="1" HorizontalAlignment="Center" Width="10" Grid.Row="1"/> </Grid> </s:SurfaceButton> </Grid> </s:ScatterViewItem> </s:ScatterView> </Grid> </s:SurfaceWindow>
C# Code:
using System; using System.Collections.Generic; using System.IO; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Windows.Threading; using Microsoft.Surface; using Microsoft.Surface.Presentation; using Microsoft.Surface.Presentation.Controls; using Microsoft.Surface.Presentation.Input; namespace PhotoPaint { /// <summary> /// Interaction logic for Window1.xaml. /// </summary> public partial class Window1 : SurfaceWindow { /// <summary> /// The current editing mode for the DrawingPadCanvas. /// When set, it updates the UI accordingly. /// </summary> private SurfaceInkEditingMode CurrentEditingMode { get { return DrawingPadCanvas.EditingMode; } set { if (value == SurfaceInkEditingMode.Ink) { DrawingPadCanvas.EditingMode = SurfaceInkEditingMode.Ink; // Update button image to show that we are now in ink mode. EditModeButton.Content = DrawButtonImage; } else { DrawingPadCanvas.EditingMode = SurfaceInkEditingMode.EraseByPoint; // Update button image to show that we are now in erase mode. EditModeButton.Content = FindResource("EraseButtonImage"); ; } } } /// <summary> /// The most recent stroke (used for undo operations). /// </summary> private Stroke mostRecentStroke; /// <summary> /// A mapping of each stroke to list of point timings in milliseconds. /// </summary> private readonly Dictionary<Stroke, List<long>> recordedStrokes = new Dictionary<Stroke, List<long>>(); /// <summary> /// A copy of the recordedStrokes used for playback. /// Strokes are removed from this Dictionary as they are played back. /// </summary> private Dictionary<Stroke, List<long>> recordedStrokesCopy; /// <summary> /// Keeps track of strokes as they are played back. /// Maps the new stroke (copy) being constructed during playback to the original stroke. /// </summary> private readonly Dictionary<Stroke, Stroke> newStrokes = new Dictionary<Stroke, Stroke>(); /// <summary> /// Whether or not new strokes are being recorded. /// </summary> private bool isRecording; /// <summary> /// Whether or not strokes are being played back. /// </summary> private bool isPlaying; /// <summary> /// Whether or not the playback or recording is paused. /// </summary> private bool isPaused; /// <summary> /// True when end of media has been reached. /// </summary> private bool mediaEnded; /// <summary> /// Interval timer used to drive recorded stroke playback. /// </summary> private DispatcherTimer timer; /// <summary> /// Stopwatch used to time strokes for playback when no video is present. /// </summary> private Stopwatch stopwatch; /// <summary> /// Duration of playback loop (in seconds) when no video is present. /// </summary> private const int duration = 12; /// <summary> /// Tracks a stroke being created by the mouse. /// </summary> private Stroke currentMouseStroke; /// <summary> /// Key used to track strokes in a touch device's user data. /// </summary> private readonly object strokeKey = new object(); /// <summary> /// Brushes used for movie buttons /// </summary> private static SolidColorBrush buttonHighlightBrush = new SolidColorBrush (Color.FromArgb(0xCC, 0x99, 0x99, 0x99)); private static SolidColorBrush buttonBackgroundBrush = new SolidColorBrush (Color.FromArgb(0xCC, 0xCC, 0xCC, 0xCC)); /// <summary> /// True when the current color is tapped to open the color wheel /// </summary> private bool tappedOpen; /// <summary> /// Gets or sets the value of isPaused. /// </summary> internal bool IsPaused { get { return isPaused; } set { isPaused = value; if (isPaused) { PauseButton.Background = buttonHighlightBrush; } else { PauseButton.Background = buttonBackgroundBrush; } } } #region Initalization /// <summary> /// Default constructor. /// </summary> public Window1() { InitializeComponent(); // Add handlers for window availability events AddWindowAvailabilityHandlers(); // listen for changes to the primary InteractiveSurfaceDevice. InteractiveSurface.PrimarySurfaceDevice.Changed += OnPrimarySurfaceDeviceChanged; } /// <summary> /// Occurs when the window is about to close. /// </summary> /// <param name="e"></param> protected override void OnClosed(EventArgs e) { base.OnClosed(e); // Remove handlers for window availability events RemoveWindowAvailabilityHandlers(); // Stop listening for InteractiveSurfaceDevice changes when the window closes. InteractiveSurface.PrimarySurfaceDevice.Changed -= OnPrimarySurfaceDeviceChanged; } /// <summary> /// Update the size of photo /// </summary> private void OnPrimarySurfaceDeviceChanged(object sender, DeviceChangedEventArgs e) { UpdatePhotoSize(); } /// <summary> /// Calculate the maximum size of photo based on logical dpi /// </summary> private void UpdatePhotoSize() { PhotoPadSVI.MaxWidth = Photo.Source.Width / (InteractiveSurface.PrimarySurfaceDevice.LogicalDpiX / 96); PhotoPadSVI.MaxHeight = Photo.Source.Height / (InteractiveSurface.PrimarySurfaceDevice.LogicalDpiX / 96); } /// <summary> /// Called when the application is finished initalizing /// </summary> /// <param name="e"></param> protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); // Load images and video from the public folders. // These are default OS folders that will always be in these locations // unless the user has deliberately moved them. string publicFoldersPath = Environment.GetEnvironmentVariable("public"); SetMoviePadContent(publicFoldersPath + @"\Videos\Sample Videos"); SetPhotoPadContent(publicFoldersPath + @"\Pictures\Sample Pictures"); } /// <summary> /// Sets the content for the MovieCanvas. /// </summary> /// <param name="publicVideosPath">Path to public videos.</param> private void SetMoviePadContent(string publicVideosPath) { if (Directory.Exists(publicVideosPath)) { // Use this video if it exists. string targetVideoPath = publicVideosPath + @"\Wildlife.wmv"; if (File.Exists(targetVideoPath)) { // Target movie exists, use it. Movie.Source = new Uri(targetVideoPath); } else { // Target movie does not exist, use the first WMV found in the directory. string[] files = Directory.GetFiles(publicVideosPath, "*.wmv"); if (files.Length > 0) { Movie.Source = new Uri(files[0]); } } } if (Movie.Source == null) { // If there aren't any movies at all, use a blank canvas. MovieCanvas.Background = Brushes.White; stopwatch = new Stopwatch(); } timer = new DispatcherTimer(); timer.Tick += OnTick; // Play the movie on startup. PlayMovie(); // Initial button state. isPlaying = true; PlayButton.Background = buttonHighlightBrush; isRecording = false; RecordButton.Background = buttonBackgroundBrush; IsPaused = false; EnableButtons(true); } /// <summary> /// Sets the content for the PhotoCanvas. /// </summary> /// <param name="publicPhotosPath">Path to public photos.</param> private void SetPhotoPadContent(string publicPhotosPath) { if (Directory.Exists(publicPhotosPath)) { // Use this images if it exists. string targetPhotoPath = publicPhotosPath + @"\Chrysanthemum.jpg"; if (File.Exists(targetPhotoPath)) { // Target image exists, use it Photo.Source = new BitmapImage(new Uri(targetPhotoPath)); } else { // Target image does not exist, use the first JPG found in the directory. string[] files = Directory.GetFiles(publicPhotosPath, "*.jpg"); if (files.Length > 0) { Photo.Source = new BitmapImage(new Uri(files[0])); } } } if (Photo.Source == null) { // If there aren't any images at all, use a blank canvas. PostcardCanvas.Background = Brushes.White; } else { UpdatePhotoSize(); } } #endregion Initalization #region InkCanvasEvents /// <summary> /// Toggles the edit mode of a SurfaceInkCanvas between EraseByPoint and Ink. /// </summary> /// <param name="sender">The object that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void EditModeClicked(object sender, RoutedEventArgs args) { SurfaceButton button = (SurfaceButton)sender; if (CurrentEditingMode == SurfaceInkEditingMode.Ink) { CurrentEditingMode = SurfaceInkEditingMode.EraseByPoint; } else { CurrentEditingMode = SurfaceInkEditingMode.Ink; } } #endregion InkCanvasEvents #region Drawing Pad Specific Code /// <summary> /// Handles the OnStrokeCollected event for SurfaceInkCanvas. /// </summary> /// <param name="sender">The SurfaceInkCanvas that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnStrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs args) { mostRecentStroke = args.Stroke; } /// <summary> /// Handles the click event for the undo button. /// </summary> /// <param name="sender">The button that raised the event.</param> /// <param name="e">The arguments for the event.</param> void UndoClicked(object sender, RoutedEventArgs e) { if (mostRecentStroke != null) { DrawingPadCanvas.Strokes.Remove(mostRecentStroke); mostRecentStroke = null; } } /// <summary> /// Handles the TouchDown event for the ColorWheel. /// </summary> /// <param name="sender">The color wheel.</param> /// <param name="args">The arguments for the event.</param> private void OnColorWheelTouchDown(object sender, TouchEventArgs args) { HandleInputDown(sender, args, false); } /// <summary> /// Handles the MouseDown event for the ColorWheel. /// </summary> /// <param name="sender">The color wheel.</param> /// <param name="args">The arguments for the event.</param> private void OnColorWheelMouseDown(object sender, MouseButtonEventArgs args) { HandleInputDown(sender, args, false); } /// <summary> /// Handles the LostTouchCapture event for the ColorWheel. /// </summary> /// <param name="sender">The color wheel.</param> /// <param name="args">The arguments for the event.</param> private void OnColorWheelLostTouchCapture(object sender, TouchEventArgs args) { HandleColorWheelLostCapture(sender, args); } /// <summary> /// Handles the LostMouseCapture event for the ColorWheel. /// </summary> /// <param name="sender">The color wheel.</param> /// <param name="args">The arguments for the event.</param> private void OnColorWheelLostMouseCapture(object sender, MouseEventArgs args) { HandleColorWheelLostCapture(sender, args); } private void HandleColorWheelLostCapture(object sender, InputEventArgs args) { UIElement element = sender as UIElement; // If there are input devices captured to the color wheel, // choose a color based on the position of the last touch device if (!element.GetAreAnyInputDevicesCaptured()) { // Select a color if hit, but keep the color wheel open if not if (ChooseColor(args, true)) { // If an actual color was chosen, release any capture on the current // color indicator as the color wheel was already dismissed CurrentColor.ReleaseAllCaptures(); } } } /// <summary> /// Handles the Tap gesture event for the current color indicator. /// </summary> /// <param name="sender">The current color indicator.</param> /// <param name="e">The arguments for the event.</param> private void OnCurrentColorTap(object sender, TouchEventArgs e) { // This event will be followed by a LostTouchCapture event. // Flag that it was tapped so that method can ensure the // color wheel stays open. tappedOpen = true; } /// <summary> /// Handles the LostTouchCapture event for the current color indicator. /// </summary> /// <param name="sender">The current color indicator.</param> /// <param name="args">The arguments for the event.</param> private void OnCurrentColorLostTouchCapture(object sender, TouchEventArgs args) { // Select a color if hit, based on the position of the only touch device // that just lost capture on the CurrentColor indicator and always dismiss // the color wheel ChooseColor(args, tappedOpen); tappedOpen = false; } /// <summary> /// Handles the LostMouseCapture event for the current color indicator. /// </summary> /// <param name="sender">The current color indicator.</param> /// <param name="args">The arguments for the event.</param> private void OnCurrentColorLostMouseCapture(object sender, MouseEventArgs args) { // Select a color if hit, based on the position of the only touch device // that just lost capture on the CurrentColor indicator and always dismiss // the color wheel ChooseColor(args, false); } /// <summary> /// Handles the MouseUp event for the current color indicator or color wheel. /// </summary> /// <param name="sender">The current color indicator or color wheel.</param> /// <param name="args">The arguments for the event.</param> private void OnMouseUp(object sender, MouseButtonEventArgs args) { // If the mouse was already captured to the sender, release it IInputElement element = sender as IInputElement; if (args.Device.GetCaptured() == element) { element.ReleaseMouseCapture(); } } /// <summary> /// Select a color based on the postion of args.TouchDevice if hit. /// </summary> /// <param name="args">The arguments for the input event.</param> /// <param name="closeOnlyOnHit">Indicates if the ColorWheel should /// be kept open when an actual color is chosen.</param> /// <returns> true if a color was actually chosen.</returns> private bool ChooseColor(InputEventArgs args, bool closeOnlyOnHit) { // If the color wheel is not visible, bail out if (ColorWheel.Visibility == Visibility.Hidden) { return false; } // Set the color on the CurrentColor indicator and on the SurfaceInkCanvas Color color = GetPixelColor(args.Device); // Black means the user touched the transparent part of the wheel. In that // case, leave the color set to its current value bool hit = color != Colors.Black; // close the colorwheel if caller always requests or only if a // color is actually chosen. bool close = !closeOnlyOnHit || hit; if (hit) { DrawingPadCanvas.DefaultDrawingAttributes.Color = color; CurrentColor.Fill = new SolidColorBrush(color); } if (close) { CurrentEditingMode = SurfaceInkEditingMode.Ink; // Replace the color wheel with the current color button ColorWheel.Visibility = Visibility.Hidden; } args.Handled = true; return hit; } /// <summary> /// Handles the TouchtDownEvent for the current color indicator. /// </summary> /// <param name="sender">The current color indicator that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnCurrentColorTouchDown(object sender, TouchEventArgs args) { HandleInputDown(sender, args, true); } /// <summary> /// Handles the MouseDownEvent for the current color indicator. /// </summary> /// <param name="sender">The current color indicator that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnCurrentColorMouseDown(object sender, MouseButtonEventArgs args) { HandleInputDown(sender, args, true); } private void HandleInputDown(object sender, InputEventArgs args, bool makeVisible) { // Capture the touch device and handle the event IInputElement element = sender as IInputElement; if (element != null && args.Device.Capture(element)) { args.Handled = true; } if (makeVisible) { // Overlay the current color button with the color wheel ColorWheel.Visibility = Visibility.Visible; } } /// <summary> /// Gets the color of a specific pixel. /// </summary> /// <param name="pt">The point from which to get a color.</param> /// <returns>The color of the point.</returns> private System.Windows.Media.Color GetPixelColor(InputDevice inputDevice) { // Translate the input point to bitmap coordinates double transformFactor = ColorWheel.Source.Width / ColorWheel.ActualWidth; Point inputPoint = inputDevice.GetPosition(ColorWheel); Point bitmapPoint = new Point(inputPoint.X * transformFactor, inputPoint.Y * transformFactor); // The point is outside the color wheel. Return black. if (bitmapPoint.X < 0 || bitmapPoint.X >= ColorWheel.Source.Width || bitmapPoint.Y < 0 || bitmapPoint.Y >= ColorWheel.Source.Height) { return Colors.Black; } // The point is inside the color wheel. Find the color at the point. CroppedBitmap cb = new CroppedBitmap(ColorWheel.Source as BitmapSource, new Int32Rect((int)bitmapPoint.X, (int)bitmapPoint.Y, 1, 1)); byte[] pixels = new byte[4]; cb.CopyPixels(pixels, 4, 0); return Color.FromRgb(pixels[2], pixels[1], pixels[0]); } #endregion Drawing Pad Specific Code #region Photo Pad Specific Code /// <summary> /// Handles the click event for the clear button. /// </summary> /// <param name="sender">The button that reaised the event.</param> /// <param name="e">The arguments for the event.</param> void ClearClicked(object sender, RoutedEventArgs e) { PostcardCanvas.Strokes.Clear(); } /// <summary> /// Handles the click event for the Move/Draw mode button on the PhotoPad. /// </summary> /// <param name="sender">The button that raised the event.</param> /// <param name="e">The arguments for the event.</param> void InkCanvasOnOffChanged(object sender, RoutedEventArgs e) { SurfaceButton button = (SurfaceButton) sender; if (PostcardCanvas.IsHitTestVisible) { // Prevent the SurfaceinkCanvas from processing input PostcardCanvas.IsHitTestVisible = false; // Load the new button image button.Content = MoveButtonImage; } else { // Let the SurfaceinkCanvas start processing input PostcardCanvas.IsHitTestVisible = true; // Load the new button image button.Content = FindResource("EditButtonImage"); } } #endregion Photo Pad Specific Code #region Movie Pad Specific Code #region Input Event Handlers // Remarks: // // We use the preview events because the normal events are consumed by // the MovieCanvas. // // These events are used to create a stroke that looks like the stroke // being added to the MovieCanvas. The actual stroke is not available // until it is completed. We use these events to record the stroke and // timestamp data so we can manully recreate the stroke during playback. /// <summary> /// Handles the PreviewTouchDown event for the MovieCanvas. /// Begins recording a new stroke. /// </summary> /// <param name="sender">The SurfaceInkCanvas that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnMovieCanvasPreviewTouchDown(object sender, TouchEventArgs args) { // If isRecord is set to true, record the stroke for playback later. if (isRecording && MovieCanvas.CaptureTouch(args.TouchDevice)) { SurfaceInkCanvas canvas = (SurfaceInkCanvas)sender; System.Windows.Point point = args.TouchDevice.GetPosition(canvas); StylusPointCollection points = new StylusPointCollection(); points.Add(new StylusPoint(point.X, point.Y)); // Cannot create a stroke without at least one point in the StylusPointCollection Stroke stroke = new Stroke(points); stroke.DrawingAttributes = canvas.DefaultDrawingAttributes.Clone(); if (MovieCanvas.UsesTouchShape) { stroke.DrawingAttributes.StylusTip = StylusTip.Ellipse; Ellipse touchEllipse = args.TouchDevice.GetEllipse(MovieCanvas); stroke.DrawingAttributes.Height = touchEllipse.Height; stroke.DrawingAttributes.Width = touchEllipse.Width; Matrix rotate = Matrix.Identity; rotate.Rotate(args.TouchDevice.GetOrientation(MovieCanvas)); stroke.DrawingAttributes.StylusTipTransform = rotate; } recordedStrokes.Add(stroke, new List<long>()); recordedStrokes[stroke].Add(CurrentPosition()); // Associate the new stroke with this touch device args.TouchDevice.SetUserData(strokeKey, stroke); } } /// <summary> /// Handles PreviewTouchMove and LostTouchCapture events for the MovieCanvas. /// Adds a point to the stroke associated with the touch device. /// </summary> /// <param name="sender">The SurfaceInkCanvas that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnPreviewTouchMoveOrLostCapture(object sender, TouchEventArgs args) { if (isRecording) { // Retrieve the stroke assocatiated with this touch device Stroke stroke = args.TouchDevice.GetUserData(strokeKey) as Stroke; if (stroke != null) { SurfaceInkCanvas canvas = (SurfaceInkCanvas) sender; System.Windows.Point position = args.TouchDevice.GetPosition(canvas); AddPointToStroke(stroke, position.X, position.Y); } } } /// <summary> /// Handles the PreviewLeftMouseDown event for the MovieCanvas. /// Begins a new currentMouseStroke. /// </summary> /// <param name="sender">The SurfaceInkCanvas that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnMovieCanvasPreviewLeftMouseDown(object sender, MouseButtonEventArgs args) { if (isRecording) { SurfaceInkCanvas canvas = (SurfaceInkCanvas)sender; System.Windows.Point point = args.MouseDevice.GetPosition(MovieCanvas); StylusPointCollection points = new StylusPointCollection(); points.Add(new StylusPoint(point.X, point.Y)); currentMouseStroke = new Stroke(points); currentMouseStroke.DrawingAttributes = canvas.DefaultDrawingAttributes.Clone(); recordedStrokes.Add(currentMouseStroke, new List<long>()); recordedStrokes[currentMouseStroke].Add(CurrentPosition()); } } /// <summary> /// Handles the PreviewMouseMove event for the MovieCanvas. /// Adds point to currentMouseStroke and record timing data. /// </summary> /// <param name="sender">The SurfaceInkCanvas that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnMovieCanvasPreviewMouseMove(object sender, MouseEventArgs args) { if (isRecording && currentMouseStroke != null) { System.Windows.Point position = args.MouseDevice.GetPosition(MovieCanvas); AddPointToStroke(currentMouseStroke, position.X, position.Y); } } /// <summary> /// Handles the PreviewLeftMouseUp event for the MovieCanvas. /// Ends the currentMouseStroke. /// </summary> /// <param name="sender">The SurfaceInkCanvas that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnMovieCanvasPreviewLeftMouseUp(object sender, MouseButtonEventArgs args) { if (isRecording && currentMouseStroke != null) { System.Windows.Point position = args.MouseDevice.GetPosition(MovieCanvas); AddPointToStroke(currentMouseStroke, position.X, position.Y); } currentMouseStroke = null; } /// <summary> /// Adds a point to a Stroke and records the time the point was added. /// </summary> /// <param name="stroke">The stroke to modify</param> /// <param name="positionX">The X coordinate of the point to add</param> /// <param name="positionY">the Y coordinate of the point to add</param> private void AddPointToStroke(Stroke stroke, double positionX, double positionY) { stroke.StylusPoints.Add(new StylusPoint(positionX, positionY)); if (recordedStrokes.ContainsKey(stroke)) { recordedStrokes[stroke].Add(CurrentPosition()); } } #endregion #region Button Event Handlers /// <summary> /// Handles the click event for the record button. /// Resumes the current recording, or starts a new recording from the /// beginning of our media. /// </summary> /// <param name="sender">The button that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnRecordButtonPressed(object sender, RoutedEventArgs args) { if (IsPaused && isRecording) { // Just Keep doing what we were doing. PlayMovie(); return; } // Stop stroke playback isPlaying = false; PlayButton.Background = buttonBackgroundBrush; // Begin new recording MovieCanvas.Strokes.Clear(); recordedStrokes.Clear(); isRecording = true; RecordButton.Background = buttonHighlightBrush; // Allow user to draw on the SurfaceInkCanvas. MovieCanvas.IsHitTestVisible = true; RestartMovie(); } /// <summary> /// Handles the click event for the play button. /// Resumes the current playback, or start a new playback from the /// beginning of our media. /// </summary> /// <param name="sender">The button that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnPlayButtonPressed(object sender, RoutedEventArgs args) { if (IsPaused && isPlaying) { // Just Keep doing what we were doing. PlayMovie(); return; } // Stop recording isRecording = false; RecordButton.Background = buttonBackgroundBrush; MovieCanvas.IsHitTestVisible = false; ResetPlayback(); isPlaying = true; PlayButton.Background = buttonHighlightBrush; RestartMovie(); } /// <summary> /// Handles the click event for the pause button. /// Resumes the current recording or playback. If at end of media, /// then start a new playback. /// </summary> /// <param name="sender">The button that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnPauseButtonPressed(object sender, RoutedEventArgs args) { if (IsPaused) { if (mediaEnded) { // If at the end media, treat hitting pause like hitting play. OnPlayButtonPressed(sender, args); } else { PlayMovie(); } } else { PauseMovie(); } } #endregion /// <summary> /// Handles the MediaEnded event for the MovieCanvas video. /// </summary> /// <param name="sender">The MediaElement that raised the event.</param> /// <param name="args">The arguments for the event.</param> private void OnMediaEnded(object sender, RoutedEventArgs args) { mediaEnded = true; Reset(); } /// <summary> /// Resets all buttons on MovieCanvas to initial state. /// </summary> private void Reset() { MovieCanvas.IsHitTestVisible = false; PauseMovie(); PauseButton.Opacity = 1.0; isPlaying = false; PlayButton.Background = buttonBackgroundBrush; PlayButton.Opacity = 1.0; isRecording = false; RecordButton.Background = buttonBackgroundBrush; RecordButton.Opacity = 1.0; EnableButtons(true); } /// <summary> /// Handles the Tick event from the DispatcherTimer. /// On each tick determines what strokes need to be added to the canvas. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnTick(object sender, EventArgs e) { timer.Stop(); try { if (IsPaused) { return; } // If there is no media, determine if the record/playback loop should end. if (stopwatch != null && stopwatch.Elapsed.Seconds >= duration) { OnMediaEnded(null, null); return; } if (recordedStrokesCopy == null || recordedStrokesCopy.Count < 1) { // There are no strokes to play back. return; } // Determine what strokes/points needed to be added to the MovieCanvas. if (isPlaying && !IsPaused) { // Get the current time in the playback loop. long elapsed = CurrentPosition(); foreach (Stroke copy in newStrokes.Keys) { Stroke original = newStrokes[copy]; if (!recordedStrokesCopy.ContainsKey(original)) { // The complete stroke has already been added to the canvas. continue; } List<long> dueTime = recordedStrokesCopy[original]; // See if the stroke is already added. if (MovieCanvas.Strokes.Contains(copy)) { // Stroke is already added. Add a new point to the stroke. int added = copy.StylusPoints.Count; if (added < original.StylusPoints.Count) { // There are more points to add. if (dueTime.Count > added && elapsed >= dueTime[added]) { // It's time to add this point. StylusPoint point = original.StylusPoints[copy.StylusPoints.Count]; copy.StylusPoints.Add(point); if (copy.StylusPoints.Count == original.StylusPoints.Count) { // We've added all the points for this stroke. recordedStrokesCopy.Remove(original); } } } } else { // Stroke has not been added yet. See if it's time to add it. // We already added the first point when we created the strokes. if (dueTime.Count > 0 && elapsed >= dueTime[0]) { MovieCanvas.Strokes.Add(copy); } } } } } finally { timer.Start(); } } /// <summary> /// Resets MoviePad state to begin stroke playback. /// </summary> private void ResetPlayback() { PauseMovie(); ResetPosition();; MovieCanvas.Strokes.Clear(); // Make a copy of the recorded strokes dictionary so its contents can be modified // without changing the original dictionary recordedStrokesCopy = new Dictionary<Stroke, List<long>>(recordedStrokes); // Make a dictionary of all the strokes the user recorded mapped to copies of those strokes. // // These copies will: // - Only have one point in their strokes collection // - Have the same DrawingAttributes as their mapped stroke // - Be added to the SurfaceInkCanvaas instead of the original stroke // - Have additional points added as the movie plays newStrokes.Clear(); // Copy the recorded strokes into a new distionary, but only copy the first point // of each stroke so the stroke can grow as the movie plays. foreach (Stroke original in recordedStrokesCopy.Keys) { StylusPointCollection points = new StylusPointCollection(); points.Add(original.StylusPoints[0]); Stroke copy = new Stroke(points); copy.DrawingAttributes = original.DrawingAttributes; newStrokes.Add(copy, original); } } /// <summary> /// Returns the current position in the playback loop. /// </summary> /// <returns></returns> private long CurrentPosition() { if (stopwatch != null) { return stopwatch.ElapsedMilliseconds; } else { return (long) Movie.Position.TotalMilliseconds; } } /// <summary> /// Resets the current position to the start of the playback loop. /// </summary> private void ResetPosition() { if (stopwatch != null) { stopwatch.Reset(); } else { Movie.Position = TimeSpan.Zero; } } /// <summary> /// Starts the playback from the beginning. /// </summary> private void RestartMovie() { ResetPosition(); PlayMovie(); mediaEnded = false; } /// <summary> /// Resumes the playback from current position. /// </summary> /// <returns></returns> private void PlayMovie() { timer.Start(); if (stopwatch != null) { stopwatch.Start(); } else { Movie.Play(); } IsPaused = false; } /// <summary> /// Pauses the media playback. /// </summary> private void PauseMovie() { if (IsPaused) return; timer.Stop(); if (stopwatch != null) { stopwatch.Stop(); } else { Movie.Pause(); } IsPaused = true; } /// <summary> /// Stops the media playback and resets the MoviePad. /// </summary> private void StopMovie() { timer.Stop(); if (stopwatch != null) { stopwatch.Stop(); } else { Movie.Stop(); } Reset(); } /// <summary> /// Enables or Diables MoviePad buttons according to the isEnabled parameter. /// </summary> /// <param name="isEnabled">Boolean indicating if the buttons are enabled or not.</param> private void EnableButtons(bool isEnabled) { PlayButton.IsEnabled = isEnabled; RecordButton.IsEnabled = isEnabled; PauseButton.IsEnabled = isEnabled; } #endregion Movie Pad Specific Code /// <summary> /// Loads an image. /// </summary> /// <param name="path"></param> /// <returns></returns> private static Image LoadImageFromPath(string path) { ImageSourceConverter converter = new ImageSourceConverter(); Image image = new Image(); image.Source = (ImageSource)converter.ConvertFromString(path); return image; } /// <summary> /// Adds handlers for window availability events. /// </summary> private void AddWindowAvailabilityHandlers() { // Subscribe to surface window availability events ApplicationServices.WindowInteractive += OnWindowInteractive; ApplicationServices.WindowNoninteractive += OnWindowNoninteractive; ApplicationServices.WindowUnavailable += OnWindowUnavailable; } /// <summary> /// Removes handlers for window availability events. /// </summary> private void RemoveWindowAvailabilityHandlers() { // Unsubscribe from surface window availability events ApplicationServices.WindowInteractive -= OnWindowInteractive; ApplicationServices.WindowNoninteractive -= OnWindowNoninteractive; ApplicationServices.WindowUnavailable -= OnWindowUnavailable; } /// <summary> /// This is called when the user can interact with the application's window. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnWindowInteractive(object sender, EventArgs e) { // Enable audio for our movie. Movie.IsMuted = false; } /// <summary> /// This is called when the user can see but not interact with the application's window. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnWindowNoninteractive(object sender, EventArgs e) { // Disable audio for our movie. Movie.IsMuted = true; } /// <summary> /// This is called when the application's window is not visible or interactive. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnWindowUnavailable(object sender, EventArgs e) { // If our movie is currently playing, stop it. StopMovie(); } } }
转载时须注明本文的详细链接,否则作者将保留追究其法律责任。
出处:http://www.cnblogs.com/flute-di/