I have a list box defined as below in my xaml. Every item consists of a canvas with an image element inside it. I have declared ManipulationEvents for the image.
<ListBox x:Name="CategoryLB" SelectionChanged="CategoryClicked" Margin="0,131,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<Canvas Width='460' Height="130" Background="#FF0D6B97" Margin="10,10,10,10" >
<Image Width='480' Height="150" Source="{Binding Page}" Stretch="None" Opacity="1" CacheMode="BitmapCache"
ManipulationDelta="ImageManipulationDelta"
ManipulationCompleted="ImageManipulationCompleted"
ManipulationStarted="ImageManipulationStarted"/>
</Canvas>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem is ManipulationDelta is not fired at all. Only ManipulationStarted and ManipulationCompleted gets called and hence my animation does not get the translation values. Looks straightforward to me.
These are the ManipulationEvents
private void ImageManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
FrameworkElement transformElement = ((FrameworkElement)sender) as FrameworkElement;
transformElement.SetHorizontalOffset(0);
}
private void ImageManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
FrameworkElement transformElement = ((FrameworkElement)sender) as FrameworkElement;
if (Math.Abs(e.TotalManipulation.Translation.X) > transformElement.ActualWidth / 3)
{
if (e.TotalManipulation.Translation.X < 0.0)
{
ToDoItemDeletedAction(transformElement);
}
else
{
ToDoItemCompletedAction(transformElement);
}
}
else
{
ToDoItemBounceBack(transformElement);
}
}
private void ImageManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
FrameworkElement transformElement = ((FrameworkElement)sender) as FrameworkElement;
// handle the drag to offset the element
double offset = transformElement.GetHorizontalOffset().Value + e.DeltaManipulation.Translation.X;
transformElement.SetHorizontalOffset(offset);
}
Anything that Im missing here?
your code is all right but the problem is that the delta event will not fire on the emulator and i have tested your code both on emulator and device and its working fine on device so dont worry and test your code on device ....
i have done manipulation on image which goes something like this ..
I hope this might help you ..
<Image x:Name="imgViewer" CacheMode="BitmapCache" Source="/MetroImages/mapmetro.png" Stretch="Uniform">
<Image.RenderTransform>
<ScaleTransform x:Name="scaleTrans" ScaleX="2" ScaleY="2" CenterX="150" CenterY="200" />
</Image.RenderTransform>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener
PinchStarted="GestureListener_PinchStart"
PinchDelta="GestureListener_PinchDelta"
PinchCompleted="GestureListener_PinchComplete"
DragStarted="GestureListener_DragStart"
DragDelta="GestureListener_DragDelta"
DragCompleted="GestureListener_DragCompleted"/>
</toolkit:GestureService.GestureListener>
</Image>
and then in eventhandlers
private readonly DispatcherTimer m_animationTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
private double _cx, _cy;
private void GestureListener_PinchStart(object sender, PinchStartedGestureEventArgs e)
{
Point p1 = e.GetPosition(imgViewer, 0);
Point p2 = e.GetPosition(imgViewer, 1);
scaleTrans.CenterX = (p1.X + ((p2.X - p1.X) / 2));
scaleTrans.CenterY = (p1.Y + ((p2.Y - p1.Y) / 2));
_cx = scaleTrans.ScaleX;
_cy = scaleTrans.ScaleY;
}
private void GestureListener_PinchDelta(object sender, PinchGestureEventArgs e)
{
// Compute new scaling factors
double cx = _cx * e.DistanceRatio;
double cy = _cy * e.DistanceRatio;
// If they're between 1.0 and 4.0, inclusive, apply them
if (cx >= 1.0 && cx <= 4.0 && cy >= 1.0 && cy <= 4.0)
{
if ((cy - 1) < 0.1 && (cx - 1) < 0.1)
cx = cy = 1;
scaleTrans.ScaleX = cx;
scaleTrans.ScaleY = cy;
}
}
private void GestureListener_PinchComplete(object sender, PinchGestureEventArgs e)
{
}
private void GestureListener_DragStart(object sender, DragStartedGestureEventArgs e)
{
}
private void GestureListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
{
scaleTrans.CenterX = (scaleTrans.CenterX - e.HorizontalChange);
scaleTrans.CenterY = (scaleTrans.CenterY - e.VerticalChange);
if (scaleTrans.CenterX < 0)
{
scaleTrans.CenterX = 0;
}
else if (scaleTrans.CenterX > imgViewer.ActualWidth)
scaleTrans.CenterX = imgViewer.ActualWidth;
if (scaleTrans.CenterY < 0)
{
scaleTrans.CenterY = 0;
}
else if (scaleTrans.CenterY > imgViewer.ActualHeight)
scaleTrans.CenterY = imgViewer.ActualHeight;
}
private void GestureListener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
{
}
Related
I'm new to Xamarin and I'm currently doing a project in Xamarin Forms PCL.
Is there a way to change the font colour and size of Picker?
<Picker x:Name="pkr_color" Grid.Row="4" Grid.Column="1" HorizontalOptions="FillAndExpand"
BackgroundColor="#ededed" Title="Select Color">
<Picker.Items>
<x:String>Red</x:String>
<x:String>Blue</x:String>
<x:String>Green</x:String>
</Picker.Items>
</Picker>
Thanks in advance!
You will need to write a custom renderer for each platform.
using System;
using Project.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer (typeof (Picker), typeof (CustomPickerRenderer))]
namespace Project.iOS
{
public class CustomPickerRenderer : PickerRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged (e);
if (Control != null) {
Control.TextColor = UIKit.UIColor.White;
}
}
}
}
Here is an example for iOS. This would change the color of the text, you will need to do something similar for Android, and just add your font sizing change as well.
Font size of a picker can be changed with PCL code.
Create MainPage.xaml file
<Picker x:Name="PickerList" Title="Select Any One" IsVisible="False" SelectedIndexChanged="PickerList_SelectedIndexChanged">
<Picker.Items>
<x:String>Option 1</x:String>
<x:String>Option 2</x:String>
<x:String>Option 3</x:String>
</Picker.Items>
</Picker>
<Label x:Name="PickerLabel" Text="Tap to Select Picker" FontSize="14" HorizontalOptions="Start">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</Label.GestureRecognizers>
</Label>
Create MainPage.xaml.cs file
private void PickerList_SelectedIndexChanged(object sender, EventArgs e)
{
PickerLabel.Text = PickerList.Items[PickerList.SelectedIndex];
// PickerLabel.Text = PickerList.SelectedItem.ToString() ;
}
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
PickerList.Focus();
}
this solves the problem for Android and IOS.
I Hope Below Code Helpful to Get Your TextColor
**In Xaml**
<Picker SelectedIndexChanged="OnColorPickerSelected" TextColor="{Binding TextColor}"/>
**In Code Behind**
private void OnColorPickerSelected(object sender, EventArgs e)
{
((ViewModel)BindingContext).Color= pkr_color.Items[pkr_color.SelectedIndex];
ChooseColorPickerTextColor(((ViewModel)BindingContext).Color, pkr_color);
}
**Implement ChooseColorPickerTextColor Method Here**
private void ChooseColorPickerTextColor(string selectedColor, Picker pickerName)
{
Picker colorPickerTextColor = pickerName;
if (selectedColor == "Red")
{
colorPickerTextColor.TextColor = Color.Red;
}
else if (selectedColor == "Yellow")
{
colorPickerTextColor.TextColor = Color.Yellow;
}
else if (selectedColor == "Green")
{
colorPickerTextColor.TextColor = Color.Green;
}
else if (selectedColor == "Blue")
{
colorPickerTextColor.TextColor = Color.Blue;
}
else if (selectedColor == "Maroon")
{
colorPickerTextColor.TextColor = Color.Maroon;
}
else if (selectedColor == "Pink")
{
colorPickerTextColor.TextColor = Color.Pink;
}
else
{
colorPickerTextColor.TextColor = Color.Aqua;
}
}
By using "WidthRequest" We can Increase size of the picker
For changing typeface, size, underline, text, textcolor, alert dialog button position, button text in Android native numberpicker (xamarin form picker), you can handle it with a custom render like this:
using System;
using System.Collections.Generic;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Graphics.Drawables;
using Android.Graphics;
[assembly: ExportRenderer(typeof(Picker), typeof(MyPickerRenderer))]
namespace Daddy.Droid
{
public class MyPickerRenderer : Xamarin.Forms.Platform.Android.PickerRenderer
{
Typeface fontFace = null;
private IElementController ElementController => Element as IElementController;
private AlertDialog _dialog;
public MyPickerRenderer(Context context) : base(context)
{
AutoPackage = false;
}
[Obsolete("This constructor is obsolete as of version 2.5. Please use PickerRenderer(Context) instead.")]
public MyPickerRenderer()
{
AutoPackage = false;
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null || Control == null)
return;
fontFace = Typeface.CreateFromAsset(this.Context.Assets, "somefont.ttf");
GradientDrawable gd = new GradientDrawable();
gd.SetStroke(0, Android.Graphics.Color.Transparent);
Control.SetBackground(gd);
Control.TextSize = 14f;
Control.SetTypeface(fontFace, TypefaceStyle.Normal);
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
NumberPicker picker = new NumberPicker(Context);
int count = picker.ChildCount;
for (int i = 0; i < count; i++)
{
Android.Views.View v = picker.GetChildAt(i);
if(v.GetType() == typeof(EditText))
{
Java.Lang.Reflect.Field field = picker.Class.GetDeclaredField("mSelectorWheelPaint");
field.Accessible = true;
((Paint)field.Get(picker)).SetTypeface(fontFace);
((EditText)v).SetTypeface(fontFace, TypefaceStyle.Normal);
picker.Invalidate();
}
}
if (model.Items != null && model.Items.Any())
{
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetDisplayedValues(model.Items.ToArray());
picker.WrapSelectorWheel = false;
picker.DescendantFocusability = DescendantFocusability.BlockDescendants;
picker.Value = model.SelectedIndex;
picker.Visibility = ViewStates.Visible;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.Visibility = ViewStates.Visible;
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
Control?.ClearFocus();
_dialog = null;
});
builder.SetPositiveButton("This One", (s, a) =>
{
ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, picker.Value);
if (Element != null)
{
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
Control?.ClearFocus();
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
Android.Widget.Button nbutton = _dialog.GetButton((int)Android.Content.DialogButtonType.Positive);
nbutton.SetTypeface(fontFace, TypefaceStyle.Normal);
nbutton.SetTextColor(Android.Graphics.Color.ParseColor("#33b5e5"));
nbutton.TextSize = 16f;
LinearLayout layOut = (LinearLayout)nbutton.Parent;
layOut.SetGravity(GravityFlags.CenterHorizontal);
Android.Views.View v1 = layOut.GetChildAt(1);
v1.Visibility = ViewStates.Gone;
int res = Resources.GetIdentifier("alertTitle", "id", "android");
TextView textView = (TextView)_dialog.FindViewById(res);
textView.SetTextColor(Android.Graphics.Color.Gray);
textView.SetTypeface(fontFace, TypefaceStyle.Normal);
textView.Gravity = GravityFlags.Center;
}
}
}
Put the Label and Picker in the same Grid cell.Don't set Title of the picker instead the Text of the Label will work as Title.
<Label x:Name="PickerLabel" Text="Picker Title" TextColor="Any Color"></Label>
<Picker x:Name="Picker" SelectedIndexChanged="Picker_SelectedIndexChanged" TextColor="Any Color" />
Now make the Text of Label Invisible when an Item is selected from Picker.
void Picker_SelectedIndexChanged(object sender, System.EventArgs e)
{
PickerLabel.IsVisible = false;
}
I need to have Image control in Xamarin.Forms with rounded corners. But I did not find any property that could make it. How to have circular Image ?
I use the FFImageLoading libraries CachedImage control with a circle transformation for circle images:
<ffimageloading:CachedImage
DownsampleToViewSize="true"
Aspect="AspectFill"
Source = "{Binding Image}"
LoadingPlaceholder = "{Binding DefaultImage}"
ErrorPlaceholder = "{Binding DefaultImage}">
<ffimageloading:CachedImage.Transformations>
<fftransformations:CircleTransformation />
</ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>
You can use Image Circle Control Plugin
<controls:CircleImage Source="{Binding Image}" Aspect="AspectFill">
<controls:CircleImage.WidthRequest>
<OnPlatform x:TypeArguments="x:Double"
iOS="55"
Android="55"
WinPhone="75"/>
</controls:CircleImage.WidthRequest>
<controls:CircleImage.HeightRequest>
<OnPlatform x:TypeArguments="x:Double"
iOS="55"
Android="55"
WinPhone="75"/>
</controls:CircleImage.HeightRequest>
</controls:CircleImage>
Read more at Project github readme
You can also use CircleImage from Xamarin-Forms-Labs project.
If you need a solution using Custom Renderers (to tweak the control the way you want), here is my implementation for that.
public class ImageCircle:Image
{
public ImageCircle ()
{
}
}
[assembly: ExportRenderer(typeof(ImageCircle), typeof(ImageCircleRenderer))]
namespace myNamespace.Droid
{
public class ImageCircleRenderer:ImageRenderer
{
public ImageCircleRenderer ()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
if ((int)Android.OS.Build.VERSION.SdkInt < 20)
SetLayerType(Android.Views.LayerType.Software, null);
}
}
protected override bool DrawChild(Canvas canvas, global::Android.Views.View child, long drawingTime)
{
try
{
var radius = Math.Min(Width, Height) / 2;
var strokeWidth = 10;
radius -= strokeWidth / 2;
//Create path to clip
var path = new Path();
path.AddCircle(Width / 2, Height / 2, radius, Path.Direction.Ccw);
canvas.Save();
canvas.ClipPath(path);
var result = base.DrawChild(canvas, child, drawingTime);
canvas.Restore();
// Create path for circle border
path = new Path();
path.AddCircle(Width / 2, Height / 2, radius, Path.Direction.Ccw);
var paint = new Paint();
paint.AntiAlias = true;
paint.StrokeWidth = 5;
paint.SetStyle(Paint.Style.Stroke);
paint.Color = global::Android.Graphics.Color.White;
canvas.DrawPath(path, paint);
//Properly dispose
paint.Dispose();
path.Dispose();
return result;
}
catch (Exception ex)
{
Console.WriteLine("Unable to create circle image: " + ex);
}
return base.DrawChild(canvas, child, drawingTime);
}
}
}
[assembly: ExportRenderer(typeof(ImageCircle), typeof(ImageCircleRenderer))]
namespace LifesTopTen.iOS
{
public class ImageCircleRenderer:ImageRenderer
{
public ImageCircleRenderer ()
{
}
private void CreateCircle()
{
try
{
double min = Math.Min(Element.Width, Element.Height);
Control.Layer.CornerRadius = (float)(min / 2.0);
Control.Layer.MasksToBounds = false;
Control.Layer.BorderColor = Color.White.ToCGColor();
Control.Layer.BorderWidth = 3;
Control.ClipsToBounds = true;
}
catch(Exception ex)
{
Console.WriteLine ("Unable to create circle image: " + ex);
}
}
protected override void OnElementChanged (ElementChangedEventArgs<Image> e)
{
base.OnElementChanged (e);
if (e.OldElement != null || Element == null)
return;
CreateCircle();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == VisualElement.HeightProperty.PropertyName ||
e.PropertyName == VisualElement.WidthProperty.PropertyName)
{
CreateCircle();
}
}
}
}
I used following code to drag and drop Button in C# and it works like charm when my Form.RightToLeftLayout=False,
but
when I set RightToLeftLayout=True
it doesnt work and move the control in wrong direction!!!
public partial class Form1 : Form
{
int xPosition;
int yPosition;
bool isDraged;
public Form1()
{
InitializeComponent();
}
private void btnMoveable_MouseDown(object sender, MouseEventArgs e)
{
this.Cursor = Cursors.SizeAll;
xPosition = e.X;
yPosition = e.Y;
isDraged = true;
}
private void btnMoveable_MouseUp(object sender, MouseEventArgs e)
{
isDraged = false;
this.Cursor = Cursors.Default;
}
private void btnMoveable_MouseMove(object sender, MouseEventArgs e)
{
if (isDraged)
{
btnMoveable.Left = btnMoveable.Left + e.X - xPosition;
btnMoveable.Top = btnMoveable.Top + e.Y - yPosition;
}
}
}
Well, you're discovering how RightToLeft is implemented. Everything is still in their normal logical position but the coordinate system is mirror-imaged along the Y-axis. So movement along the X-axis is inverted. You'll need to accommodate that. Fix:
int dx = e.X - xPosition;
if (this.RightToLeft == RightToLeft.Yes) dx = -dx;
btnMoveable.Left = btnMoveable.Left + dx;
I got Exception of System.Windows.Markup.XamlParseException occurred in System.Windows.ni.dll,
System.ArgumentException occurred in mscorlib.ni.dll and wasn't handled before a managed/native boundary
my MainPage.xaml:
<Slider Name="timelineSlider" Margin="0,185,0,376" ValueChanged="SeekToMediaPosition"/>
<TextBlock Height="51" HorizontalAlignment="Left" Margin="27,334,0,0" x:Name="TitleTextBlock" Text="Title:" VerticalAlignment="Top" FontSize="28" Foreground="{StaticResource PhoneAccentBrush}" Width="400" />
<TextBlock Height="39" HorizontalAlignment="Left" Margin="27,383,0,0" x:Name="ArtistTextBlock" Text="Artist:" VerticalAlignment="Top" FontSize="24" FontStyle="Italic" Foreground="White" Width="400" />
<TextBlock Height="30" HorizontalAlignment="Left" Margin="37,559,0,0" x:Name="StartTextBlock" Text="00:00:00" VerticalAlignment="Top" Width="100" />
<TextBlock Height="30" HorizontalAlignment="Left" TextAlignment="Right" Margin="327,559,0,0" x:Name="EndTextBlock" Text="00:00:00" VerticalAlignment="Top" Width="88" />
<Slider HorizontalAlignment="Left" Margin="43,200,0,0" VerticalAlignment="Top" Width="395" Name="SongSlider" ManipulationCompleted="SoundSlider_ManipulationCompleted"/>
My MainPage.xaml.cs file is:
private void NotProvided()
{
if (BackgroundAudioPlayer.Instance.Track != null)
{
// show soung info
TitleTextBlock.Text = BackgroundAudioPlayer.Instance.Track.Title
ArtistTextBlock.Text = BackgroundAudioPlayer.Instance.Track.Artist;
// handle slider and texts
SongSlider.Minimum = 0;
SongSlider.Maximum = BackgroundAudioPlayer.Instance.Track.Duration.TotalMilliseconds;
string text = BackgroundAudioPlayer.Instance.Track.Duration.ToString();
EndTextBlock.Text = text.Substring(0, 8);
}
if (null != BackgroundAudioPlayer.Instance.Track)
{
txtCurrentTrack.Text = BackgroundAudioPlayer.Instance.Track.Title +
" by " +
BackgroundAudioPlayer.Instance.Track.Artist;
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState)
{
playButton.Content = "pause";
txtCurrentTrack.Text = BackgroundAudioPlayer.Instance.Track.Title +
" by " +
BackgroundAudioPlayer.Instance.Track.Artist;
}
else
{
playButton.Content = "play";
txtCurrentTrack.Text = "";
}
}
private DispatcherTimer dispatcherTimer = new DispatcherTimer();
private void startTimer()
{
// start timer to check position of the song
dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// song is playing
if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState)
{
// handle slider
SongSlider.Minimum = 0;
SongSlider.Value = BackgroundAudioPlayer.Instance.Position.TotalMilliseconds;
SongSlider.Maximum = BackgroundAudioPlayer.Instance.Track.Duration.TotalMilliseconds;
// display text
string text = BackgroundAudioPlayer.Instance.Position.ToString();
StartTextBlock.Text = text.Substring(0, 8);
text = BackgroundAudioPlayer.Instance.Track.Duration.ToString();
EndTextBlock.Text = text.Substring(0, 8);
}
}
private void SoundSlider_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
// get slider value
int sliderValue = (int)SongSlider.Value;
// create timespan object with milliseconds (from slider value)
TimeSpan timeSpan = new TimeSpan(0, 0, 0, 0, sliderValue);
// set a new position of the song
BackgroundAudioPlayer.Instance.Position = timeSpan;
}
private void PrevButton_Click(object sender, RoutedEventArgs e)
{
// skip to previous song
BackgroundAudioPlayer.Instance.SkipPrevious();
// handle text and slider
playButton.Content = "Pause";
SongSlider.Value = 0;
}
private void NextButton_Click(object sender, RoutedEventArgs e)
{
// skip to next song
BackgroundAudioPlayer.Instance.SkipNext();
// handle text and slider
playButton.Content = "Pause";
SongSlider.Value = 0;
}
I cannot find the property or the object that I need..
I have implemented a draggable and zoomable image but I can drag it out of the screen, so I lose my Image out of the view.
Where should I put my image to keep it inside a box (and, possibly with the nice bouncing effect)?
EDIT:
Pratically my image has to remain in the stackpanel, with only the possibility of zoom-in.
(max zoom-out as the first rendering).
A bit of code:
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
initialAngle = compositeTransform.Rotation;
initialScale = compositeTransform.ScaleX;
}
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
compositeTransform.ScaleX = initialScale * e.DistanceRatio;
compositeTransform.ScaleY = initialScale * e.DistanceRatio;
}
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
compositeTransform.TranslateX += e.HorizontalChange;
compositeTransform.TranslateY += e.VerticalChange;
}
<StackPanel x:Name="container">
<Image x:Name="image_chart">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="scale" />
<TranslateTransform x:Name="transform" />
<CompositeTransform x:Name="compositeTransform"/>
</TransformGroup>
</Image.RenderTransform>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener PinchStarted="OnPinchStarted" PinchDelta="OnPinchDelta"
DragDelta="OnDragDelta"/>
</toolkit:GestureService.GestureListener>
</Image>
</StackPanel>
EDIT 2 - Half Answer
I've finally find out how to stop the zoom-out at the original size!
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
if (1.0 <= (initialScale * e.DistanceRatio))
{
compositeTransform.ScaleX = initialScale * e.DistanceRatio;
compositeTransform.ScaleY = initialScale * e.DistanceRatio;
}
}
The If condition means: if I'm zooming in -> no problem because the e.DistanceRatio is >1. If I'm zooming out I will stop until the initialScale will be the same!
Now I still need help on how avoid the Drag outside.
Ok, I find out the solution, but I need some improvement.
The scaling work but when the image is on the right it goes all on the left (because it's scaling starting from the top_left corner..
Here's the code for the "blocking drag":
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
double realWidth = image_chart.ActualWidth*compositeTransform.ScaleX;
double realHeight = image_chart.ActualHeight * compositeTransform.ScaleY;
if(compositeTransform.TranslateX>=0)
compositeTransform.TranslateX = Math.Max(container.ActualWidth - realWidth,
Math.Min(0, compositeTransform.TranslateX + e.HorizontalChange));
else
compositeTransform.TranslateX = Math.Max(container.ActualWidth - realWidth,
Math.Min(0, compositeTransform.TranslateX + e.HorizontalChange));
if(compositeTransform.TranslateY>=0)
compositeTransform.TranslateY = Math.Max(container.ActualHeight - realHeight,
Math.Min(0, compositeTransform.TranslateY + e.VerticalChange));
else
compositeTransform.TranslateY = Math.Max(container.ActualHeight - realHeight,
Math.Min(0, compositeTransform.TranslateY + e.VerticalChange));
EDIT:
In the end I've decide to use the WebBrowser.. much more "smoother" and enjoyable!
my solution:
XAML code
<StackPanel x:Name="Scroll" Margin="0">
<Image CacheMode="BitmapCache" Name="FrontCover" Source="{Binding FullCover}" >
<Image.RenderTransform>
<CompositeTransform x:Name="transform" ScaleX="1" ScaleY="1" />
</Image.RenderTransform>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener PinchDelta="OnPinchDelta" PinchStarted="OnPinchStarted" DragDelta="OnDragDelta" />
</toolkit:GestureService.GestureListener>
</Image>
</StackPanel>
double initialScale;
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
initialScale = transform.ScaleX;
}
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
var curZoom = initialScale * e.DistanceRatio;
if (curZoom >= 1 && curZoom <= 3)
{
transform.ScaleX = curZoom;
transform.ScaleY = curZoom;
}
}
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
transform.CenterX = (transform.CenterX - e.HorizontalChange);
transform.CenterY = (transform.CenterY - e.VerticalChange);
if (transform.CenterX < 0)
transform.CenterX = 0;
else if ( transform.CenterX > Scroll.ActualWidth)
transform.CenterX = Scroll.ActualWidth;
else if (transform.CenterX > (FrontCover.Height * transform.ScaleX))
transform.CenterX = FrontCover.Height * transform.ScaleX;
if (transform.CenterY < 0)
transform.CenterY = 0;
else if (transform.CenterY > Scroll.ActualHeight)
transform.CenterY = Scroll.ActualHeight;
else if (transform.CenterY > (FrontCover.Height * transform.ScaleY))
transform.CenterY = FrontCover.Height * transform.ScaleY;
}
Think it should help others
Assuming that at beginning image is in top-left corner (and please check if this still works after scaling):
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
compositeTransform.TranslateX = Math.Min(container.ActualWidth - image_chart.ActualWidth,
Math.Max(0,
compositeTransform.TranslateX + e.HorizontalChange));
compositeTransform.TranslateY = Math.Min(container.ActualHeight - image_chart.ActualHeight,
Math.Max(0,
compositeTransform.TranslateY + e.VerticalChange));
}