Can anyone provide a few links to examples of how to do Manipulation in Windows 8.1, please?
I see many examples online of how to do it for Windows 7, but it doesn't work for me on my Windows 8.1 machine (with touchscreen). I'm using VS Studio Community v15. Here's the example that I've been trying:
https://www.tutorialspoint.com/wpf/wpf_multi_touch.htm
Using either the mouse or fingers doesn't make anything happen at all - no movement, no scaling, no rotation, etc.
P.S., I'm doing my project in VB, not C#.
For example,
<Window x:Class = "WpfManipulation1.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local = "clr-namespace:WpfManipulation1"
mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "604">
<Window.Resources>
<MatrixTransform x:Key = "InitialMatrixTransform">
<MatrixTransform.Matrix>
<Matrix OffsetX = "200" OffsetY = "200"/>
</MatrixTransform.Matrix>
</MatrixTransform>
</Window.Resources>
<Canvas>
<Rectangle Name = "manRect" Width = "321" Height = "241"
RenderTransform = "{StaticResource InitialMatrixTransform}"
IsManipulationEnabled = "true" Canvas.Left = "-70" Canvas.Top = "-170">
<Rectangle.Fill>
<ImageBrush ImageSource = "ManipImage.JPG"/>
</Rectangle.Fill>
</Rectangle>
<Image Source="ManipImage.jpg" ManipulationMode="All" ManipulationDelta="UIElement_OnManipulationDelta" />
</Canvas>
</Window>
Partial success with... XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfManipulation2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Image Source="ManipImage.jpg" IsManipulationEnabled="True" ManipulationDelta="OnManipulationDelta" />
</Grid>
</Window>
VB Code-Behind:
Imports System.Windows.UIElement
Class MainWindow
Private Sub OnManipulationDelta(ByVal sender As Object, ByVal e As ManipulationDeltaEventArgs)
Dim transformGroup = GetTransformGroup(sender)
Dim translateTransform = transformGroup.Children.OfType(Of TranslateTransform).First
Dim rotateTransform = transformGroup.Children.OfType(Of RotateTransform).First
Dim scaleTransform = transformGroup.Children.OfType(Of ScaleTransform).First
translateTransform.X = (translateTransform.X + e.DeltaManipulation.Translation.X)
translateTransform.Y = (translateTransform.Y + e.DeltaManipulation.Translation.Y)
rotateTransform.Angle = (rotateTransform.Angle + e.DeltaManipulation.Rotation)
scaleTransform.ScaleY = (scaleTransform.ScaleY * e.DeltaManipulation.Scale.Y)
scaleTransform.ScaleX = (scaleTransform.ScaleX * e.DeltaManipulation.Scale.X)
End Sub
Private Function GetTransformGroup(ByVal sender As Object) As TransformGroup
Dim element = CType(sender, UIElement)
element.RenderTransformOrigin = New Point(0.5, 0.5)
Dim transformGroup = CType(element.RenderTransform, TransformGroup)
If (transformGroup Is Nothing) Then
transformGroup = New TransformGroup
transformGroup.Children.Add(New TranslateTransform)
transformGroup.Children.Add(New ScaleTransform)
transformGroup.Children.Add(New RotateTransform)
element.RenderTransform = transformGroup
End If
Return transformGroup
End Function
End Class
Error on line "Dim transformGroup = CType(element.RenderTransform, TransformGroup)": System.InvalidCastException: 'Unable to cast object of type 'System.Windows.Media.MatrixTransform' to type 'System.Windows.Media.TransformGroup'.'
Here's the result of implementing the advice provided.
XAML:
<Window x:Class="WpfTutorialSamples.Common_interface_controls.ToolbarSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ToolbarSample"
Top="50" Left="50" Height="500" Width="700">
<Window.CommandBindings>
<CommandBinding Command="New" CanExecute="CommonCommandBinding_CanExecute" />
<CommandBinding Command="Open" CanExecute="CommonCommandBinding_CanExecute" />
<CommandBinding Command="Save" CanExecute="CommonCommandBinding_CanExecute" />
</Window.CommandBindings>
<Window.Resources>
<MatrixTransform x:Key = "InitialMatrixTransform">
<MatrixTransform.Matrix>
<Matrix OffsetX = "0" OffsetY = "0"/>
</MatrixTransform.Matrix>
</MatrixTransform>
</Window.Resources>
<Grid x:Name="grid_MainWindow" Margin="0,0,0,0">
<DockPanel>
<ToolBarTray x:Name="myToolBarTray" DockPanel.Dock="Top" Panel.ZIndex="1">
<!--
<ToolBar>
<Button Command="New" Content="New" />
<Button Command="Open" Content="Open" />
<Button Command="Save" Content="Save" />
</ToolBar>
<ToolBar>
<Button Command="Cut" Content="Cut" />
<Button Command="Copy" Content="Copy" />
<Button Command="Paste" Content="Paste" />
</ToolBar>
-->
<ToolBar x:Name="myToolBar" Height="30">
<Button Name="button_HideWindows" Content="Un/Hide" />
<Button Name="button_ClearWindows" Content="Clear" />
<Button Name="button_ResetMatrix" Content="Reset" />
</ToolBar>
</ToolBarTray>
<!-- <TextBox AcceptsReturn="True" /> -->
<!-- <Canvas x:Name="canvas_Main"/> -->
<Canvas x:Name="canvas_Main" Background="LightPink" >
<!--
<Rectangle Name = "manRect" Width = "300" Height = "300"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="0,0,0,0"
IsManipulationEnabled="True" ManipulationDelta="Window_ManipulationDelta"
RenderTransform = "{StaticResource InitialMatrixTransform}"
>
<Rectangle.Fill>
<SolidColorBrush Color="LightBlue" />
</Rectangle.Fill>
</Rectangle>
-->
<Canvas Name = "manRect" Width = "300" Height = "300"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="0,0,0,0"
IsManipulationEnabled="True" ManipulationDelta="Window_ManipulationDelta"
RenderTransform = "{StaticResource InitialMatrixTransform}"
Background="LightBlue"
>
</Canvas>
<Label x:Name="label_ZoomIn" Content="ZIn" Canvas.Left="330" Canvas.Top="370" Background="LightSkyBlue"/>
<Label x:Name="label_ZoomOut" Content="ZOut" Canvas.Left="330" Canvas.Top="400" Background="LightSkyBlue"/>
</Canvas>
</DockPanel>
<StackPanel x:Name="stackpanel_NW" Background="LightYellow" HorizontalAlignment="Left" Margin="0,30,0,0" VerticalAlignment="Top" Height="100" Width="300">
<Grid x:Name="grid_NW">
<TextBox x:Name="textbox_NW" Background="LightYellow" BorderThickness="0" AcceptsReturn="True" Height="90" Width="290" Margin="5,5,5,5" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Grid>
</StackPanel>
<StackPanel x:Name="stackpanel_NE" Background="LightYellow" HorizontalAlignment="Right" Margin="0,30,0,0" VerticalAlignment="Top" Height="300" Width="300">
<Grid x:Name="grid_NE">
<TextBox x:Name="textbox_NE" Background="LightYellow" BorderThickness="0" AcceptsReturn="True" Height="290" Width="290" Margin="5,5,5,5" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Grid>
</StackPanel>
<StackPanel x:Name="stackpanel_SW" Background="LightYellow" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Bottom" Height="100" Width="300">
<Grid x:Name="grid_SW">
<TextBox x:Name="textbox_SW" Background="LightYellow" BorderThickness="0" AcceptsReturn="True" Height="90" Width="290" Margin="5,5,5,5" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Grid>
</StackPanel>
<StackPanel x:Name="stackpanel_SE" Background="LightYellow" HorizontalAlignment="Right" Margin="0,0,0,0" VerticalAlignment="Bottom" Height="100" Width="300">
<Grid x:Name="grid_SE">
<TextBox x:Name="textbox_SE" Background="LightYellow" BorderThickness="0" AcceptsReturn="True" Height="90" Width="290" Margin="5,5,5,5" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Grid>
</StackPanel>
</Grid>
</Window>
Code-Behind (VB):
'Reference: http://www.wpf-tutorial.com/common-interface-controls/toolbar-control/
Imports System
Imports System.Windows
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Shapes
Namespace WpfTutorialSamples.Common_interface_controls
Public Class ToolbarSample
Inherits Window
Dim CausedByCode As Boolean
Dim ZoomFactor As Double = 0.1
Dim ZoomMax As Double = 4.95
Dim ZoomMin As Double = 0.05
Dim NumberOfLevels As Integer = 6
Dim NumberOfCellsPerLevel As Integer = 2
Dim LvlBuffer As Double = 0.02
Dim CellBuffer As Double = 0.05
Dim LvlContainerColor As Brush = Brushes.LightGreen
Dim CellContainerColor As Brush = Brushes.LightYellow
Dim LvlDividerColor As Brush = Brushes.Red
Public Sub New()
MyBase.New
InitializeComponent()
End Sub
Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
'HideWindows(False, New StackPanel() {stackpanel_NW, stackpanel_SE, stackpanel_SW})
HideWindows(True, New StackPanel() {}) 'all
End Sub
Private Sub MainWindow_Loaded(sender As Object, e As EventArgs) Handles Me.Loaded
'add dividers
For a = 1 To (NumberOfLevels - 1)
Dim top As Double = a * (manRect.Height / NumberOfLevels)
Dim LvlDivider As New Canvas
LvlDivider.Background = LvlDividerColor
LvlDivider.Width = manRect.Width
LvlDivider.Height = 0.25
manRect.Children.Add(LvlDivider)
Canvas.SetLeft(LvlDivider, 0)
Canvas.SetTop(LvlDivider, top)
Next
'add levels
For a = 0 To (NumberOfLevels - 1)
Dim LvlWidth As Double = manRect.Width
Dim LvlHeight As Double = manRect.Height / NumberOfLevels
Dim LvlTop As Double = (a * LvlHeight) + (LvlBuffer * LvlHeight)
Dim LvlBottom As Double = LvlHeight - (2 * LvlBuffer * LvlHeight)
Dim LvlContainer As New Canvas
LvlContainer.Background = LvlContainerColor
LvlContainer.Width = LvlWidth
LvlContainer.Height = LvlBottom
manRect.Children.Add(LvlContainer)
Canvas.SetLeft(LvlContainer, 0)
Canvas.SetTop(LvlContainer, LvlTop)
'add cells
For b = 0 To (NumberOfCellsPerLevel - 1)
Dim CellWidth As Double = LvlWidth / NumberOfCellsPerLevel
Dim CellWidthBuffered As Double = CellWidth - (2 * CellBuffer * CellWidth)
Dim CellHeight As Double = LvlBottom
Dim CellTop As Double = (CellBuffer * CellHeight)
Dim CellBottom As Double = CellHeight - (2 * CellBuffer * CellHeight)
Dim CellLeft As Double = (b * CellWidth) + (CellBuffer * CellWidth)
Dim CellContainer As New Canvas
CellContainer.Background = CellContainerColor
CellContainer.Width = CellWidthBuffered
CellContainer.Height = CellBottom
LvlContainer.Children.Add(CellContainer)
Canvas.SetLeft(CellContainer, CellLeft)
Canvas.SetTop(CellContainer, CellTop)
Next
Next
'
ResetMatrix_Click(button_ResetMatrix, Nothing)
End Sub
Private Sub CommonCommandBinding_CanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
e.CanExecute = True
End Sub
Private Sub HideWindows_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles button_HideWindows.Click
HideWindows(False, New StackPanel() {stackpanel_NE, stackpanel_NW, stackpanel_SE, stackpanel_SW})
End Sub
Private Sub ClearWindows_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles button_ClearWindows.Click
ClearWindows(New StackPanel() {stackpanel_NE, stackpanel_NW, stackpanel_SE, stackpanel_SW})
End Sub
Private Sub HideWindows(ByVal Hide As Boolean, StackPanels() As StackPanel)
If StackPanels.Length = 0 Then
HideWindows(False, New StackPanel() {stackpanel_NE, stackpanel_NW, stackpanel_SE, stackpanel_SW})
Else
For Each s In StackPanels
If StackPanels.Length = 4 Then
s.Visibility = IIf((s.Visibility = Visibility.Hidden), Visibility.Visible, Visibility.Hidden)
Else
s.Visibility = IIf(Hide, Visibility.Visible, Visibility.Hidden)
End If
Next
End If
End Sub
Private Sub ClearWindows(StackPanels() As StackPanel)
For Each s In StackPanels
Dim g As Grid = (From a In s.Children Where a.GetType = GetType(Grid)
Select TryCast(a, Grid)).FirstOrDefault
Dim t As TextBox = (From a In g.Children Where a.GetType = GetType(TextBox)
Select TryCast(a, TextBox)).FirstOrDefault
If s.Visibility = Visibility.Visible Then t.Clear()
Next
End Sub
Private Sub ResetMatrix_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles button_ResetMatrix.Click
'CenterAboutPoint(New Point(150, 150), e) 'top-left
CenterAboutPoint(New Point(Me.Width / 2, (Me.Height / 2) - myToolBar.Height), False, False, False, e) 'middle of window
End Sub
Private Sub label_ZoomIn_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles label_ZoomIn.MouseDown
CenterAboutPoint(New Point(Me.Width / 2, (Me.Height / 2) - myToolBar.Height), True, True, False, e)
End Sub
Private Sub label_ZoomOut_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles label_ZoomOut.MouseDown
CenterAboutPoint(New Point(Me.Width / 2, (Me.Height / 2) - myToolBar.Height), True, False, False, e)
End Sub
Private Sub CenterAboutPoint(ByVal p As Point, ByVal JustZoom As Boolean, ByVal ZoomDir As Boolean, ByVal JustTranslate As Boolean, ByVal e As RoutedEventArgs)
'Dim rectToMove As Rectangle = CType(manRect, Rectangle)
Dim rectToMove As Canvas = CType(manRect, Canvas)
Dim rectsMatrix As Matrix = CType(rectToMove.RenderTransform, MatrixTransform).Matrix
Dim ZoomLevel As Double = rectsMatrix.M11
Dim transform = TransformToVisual(manRect)
'textbox_NE.Text = "zoom: " & ZoomLevel & ", width: " & manRect.Width & ", height: " & manRect.Height
Dim elemPosTopLeft = manRect.TranslatePoint(New Point(0, 0), canvas_Main)
'textbox_NE.AppendText(vbCrLf & "elemPosTopLeft: " & elemPosTopLeft.ToString)
Dim elemPosBottomRight = manRect.TranslatePoint(New Point(manRect.ActualWidth, manRect.ActualHeight), canvas_Main)
'textbox_NE.AppendText(vbCrLf & "elemPosBottomRight: " & elemPosBottomRight.ToString)
Dim elemPosWidth = (elemPosBottomRight.X - elemPosTopLeft.X) '* -ZoomLevel
'textbox_NE.AppendText(vbCrLf & "elemPosWidth: " & elemPosWidth.ToString)
Dim elemPosHeight = (elemPosBottomRight.Y - elemPosTopLeft.Y) '* -ZoomLevel
'textbox_NE.AppendText(vbCrLf & "elemPosHeight: " & elemPosHeight.ToString)
Dim elemPosLeft = elemPosTopLeft.X
'textbox_NE.AppendText(vbCrLf & "elemPosLeft: " & elemPosLeft.ToString)
Dim elemPosTop = elemPosTopLeft.Y
'textbox_NE.AppendText(vbCrLf & "elemPosTop: " & elemPosTop.ToString)
Dim elemPosBottom = elemPosBottomRight.Y
'textbox_NE.AppendText(vbCrLf & "elemPosBottom: " & elemPosBottom.ToString)
Dim elemPosRight = elemPosBottomRight.X
'textbox_NE.AppendText(vbCrLf & "elemPosRight: " & elemPosRight.ToString)
Dim elemPosCenterX = elemPosLeft + (elemPosWidth / 2)
'textbox_NE.AppendText(vbCrLf & "elemPosCenterX: " & elemPosCenterX.ToString)
Dim elemPosCenterY = elemPosTop + (elemPosHeight / 2)
'textbox_NE.AppendText(vbCrLf & "elemPosCenterY: " & elemPosCenterY.ToString)
'offsets from point
Dim elemPosLeftOffset = p.X - elemPosCenterX
'textbox_NE.AppendText(vbCrLf & "elemPosLeftOffset: " & elemPosLeftOffset.ToString)
Dim elemPosTopOffset = p.Y - elemPosCenterY
'textbox_NE.AppendText(vbCrLf & "elemPosTopOffset: " & elemPosTopOffset.ToString)
'scale
If JustTranslate = False Then
If JustZoom = False Then
If ZoomLevel <> 1 Then
rectsMatrix.ScaleAt((1 / ZoomLevel), (1 / ZoomLevel), elemPosCenterX, elemPosCenterY)
End If
Else
Dim NewZoomLevel = IIf(ZoomDir = True, (1 + ZoomFactor), (1 - ZoomFactor))
'textbox_NE.AppendText(vbCrLf & "NewZoomLevel: " & NewZoomLevel.ToString)
If ((ZoomLevel * NewZoomLevel) >= ZoomMin) And ((ZoomLevel * NewZoomLevel) <= ZoomMax) Then
rectsMatrix.ScaleAt(NewZoomLevel, NewZoomLevel, elemPosCenterX, elemPosCenterY)
End If
End If
End If
'translate
If JustZoom = False Then
rectsMatrix.Translate(elemPosLeftOffset, elemPosTopOffset)
End If
'transform
rectToMove.RenderTransform = New MatrixTransform(rectsMatrix)
If Not IsNothing(e) Then e.Handled = True
End Sub
Private Sub Window_ManipulationStarting(ByVal sender As Object, ByVal e As ManipulationStartingEventArgs)
e.ManipulationContainer = Me
e.Handled = True
End Sub
Private Sub Window_ManipulationDelta(ByVal sender As Object, ByVal e As ManipulationDeltaEventArgs)
'Reference: https://www.tutorialspoint.com/wpf/wpf_multi_touch.htm
'Dim rectToMove As Rectangle = CType(e.OriginalSource, Rectangle)
Dim rectToMove As Canvas = CType(e.OriginalSource, Canvas)
Dim rectsMatrix As Matrix = CType(rectToMove.RenderTransform, MatrixTransform).Matrix
Dim ZoomLevel As Double = rectsMatrix.M11
'scale
Dim scale As Double = (e.DeltaManipulation.Scale.X + e.DeltaManipulation.Scale.Y) / 2
rectsMatrix.ScaleAtPrepend(scale, scale, e.ManipulationOrigin.X, e.ManipulationOrigin.Y)
'translate
If CausedByCode = False AndAlso (Math.Abs(e.DeltaManipulation.Translation.X) > 1 Or Math.Abs(e.DeltaManipulation.Translation.Y) > 1) Then
rectsMatrix.Translate(e.DeltaManipulation.Translation.X, e.DeltaManipulation.Translation.Y)
CausedByCode = True
Else
CausedByCode = False
End If
'apply transformation
If (rectsMatrix.M11 >= ZoomMin) And (rectsMatrix.M11 <= ZoomMax) Then
rectToMove.RenderTransform = New MatrixTransform(rectsMatrix)
End If
Dim containingRect As Rect = New Rect(CType(e.ManipulationContainer, FrameworkElement).RenderSize)
Dim shapeBounds As Rect = rectToMove.RenderTransform.TransformBounds(New Rect(rectToMove.RenderSize))
If (e.IsInertial AndAlso Not containingRect.Contains(shapeBounds)) Then
e.Complete()
End If
e.Handled = True
End Sub
Private Sub Window_InertiaStarting(ByVal sender As Object, ByVal e As ManipulationInertiaStartingEventArgs)
e.TranslationBehavior.DesiredDeceleration = (10 * (96 / (1000 * 1000))) 'inches per second squared
e.ExpansionBehavior.DesiredDeceleration = (0.1 * (96 / (1000 * 1000))) 'inches per second squared
e.RotationBehavior.DesiredDeceleration = (720 / (1000 * 1000)) 'degrees per second squared
e.Handled = True
End Sub
End Class
End Namespace
The program works great, although there is an issue when making long drags of the matrix after it has been scaled down...it might be due to inertia.
The following allows basic manipulations on a specified image:
XAML
<Image Source="https://placehold.it/200x200" IsManipulationEnabled="True" ManipulationDelta="OnManipulationDelta" />
Code-Behind (C#)
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var transformGroup = GetTransformGroup(sender);
var translateTransform = transformGroup.Children.OfType<TranslateTransform>().First();
var rotateTransform = transformGroup.Children.OfType<RotateTransform>().First();
var scaleTransform = transformGroup.Children.OfType<ScaleTransform>().First();
translateTransform.X += e.DeltaManipulation.Translation.X;
translateTransform.Y += e.DeltaManipulation.Translation.Y;
rotateTransform.Angle += e.DeltaManipulation.Rotation;
scaleTransform.ScaleY *= e.DeltaManipulation.Scale.Y;
scaleTransform.ScaleX *= e.DeltaManipulation.Scale.X;
}
private TransformGroup GetTransformGroup(object sender)
{
var element = sender as UIElement;
element.RenderTransformOrigin = new Point(.5, .5);
var transformGroup = element.RenderTransform as TransformGroup;
if (transformGroup == null)
{
transformGroup = new TransformGroup();
transformGroup.Children.Add(new TranslateTransform());
transformGroup.Children.Add(new ScaleTransform());
transformGroup.Children.Add(new RotateTransform());
element.RenderTransform = transformGroup;
}
return transformGroup;
}