I have a rectangle and on my MouseMove event I want to transform the rectangle whenever the rectangle's width has changed.
I have code sorta like this:
private Rectangle _rectangle;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_rectangle = GetTemplatedChild("PART_RangeRectangle") as Rectangle;
if(_rectangle != null)
{
_rectangle.MouseMove += new MouseEventHandler(_rectangle_MouseMove);
}
}
private void _rectangle_MouseMove(object sender, MouseEventArgs e)
{
if(e.LeftButton == MouseButtonState.Pressed && _rectangle != null)
{
_rectangle.Width += 50;
_rectangle.RenderTransform = new TranslateTransform(-10, 0);
}
}
My Xaml looks sorta like this:
<Grid>
<Canvas>
<Rectangle Name="PART_RangeRectangle" StrokeThickness="5"
RenderTransformOrigin="0.5, 0.5" />
<Canvas>
</Grid>
When I first trigger the MouseMove event the translation occurs as expected. But this only occurs once. I am getting into that block of code and the width of the rectangle is updating fine, but I've yet to figure out why the transform is not updating.
You're replacing the old transform with an identical transform. You need to modify the existing transform and use += like you do with Width.
Example:
if (_rectangle.RenderTransform is TranslateTransform)
{
(_rectangle.RenderTransform as TranslateTransform).X -= 10;
}
else _rectangle.RenderTransform = new TranslateTransform(-10, 0);
You're not changing your transform. Assigning a RenderTransform does not move the rectangle, it sets an offset. You're not changing that offset after your first assignment.
Related
I have few textblocks in a grid which can be dragged. I want to restrict the user so that user can't drag a textblock outside the grid.
I've tried a few ways like getting the position of the grid so that somehow i can control but it didn't work as expected.
Thanks in advance.
This can be done pretty easily using a Canvas inside the Grid, calculating the coordinates of the TextBlock inside of the Canvas and then continuously checking whether the TextBlock is still within its bounds. When the TextBlock leaves its bounds, the Transform then reverts back to it's last known 'good' coordinates.
XAML
<Grid>
<Grid Name="GridBounds" Width="600" Height="600" Background="Aqua">
<Canvas>
<TextBlock Name="TextBlock1" Text="Drag Me" FontSize="40" ManipulationDelta="TextBlock1_ManipulationDelta" ManipulationMode="TranslateX, TranslateY" HorizontalAlignment="Stretch" Canvas.Left="216" Canvas.Top="234" VerticalAlignment="Stretch"/>
</Canvas>
</Grid>
</Grid>
Code Behind
CompositeTransform TextDrag = new CompositeTransform();
CompositeTransform savedTransform = new CompositeTransform();
public MainPage()
{
this.InitializeComponent();
TextBlock1.RenderTransform = TextDrag;
}
// Copy Transform X and Y if TextBlock is within bounds
private void CopyTransform(CompositeTransform orig, CompositeTransform copy)
{
copy.TranslateX = orig.TranslateX;
copy.TranslateY = orig.TranslateY;
}
private void TextBlock1_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
TextDrag.TranslateX += e.Delta.Translation.X;
TextDrag.TranslateY += e.Delta.Translation.Y;
CompositeTransform transform = TextBlock1.RenderTransform as CompositeTransform;
// Get current Top-Left coordinates of TextBlock
var TextTopLeft = TextBlock1.TransformToVisual(GridBounds).TransformPoint(new Point(0, 0));
// Get current Bottom-Right coordinates of TextBlock
var TextBottomRight = TextBlock1.TransformToVisual(GridBounds).TransformPoint(new Point(TextBlock1.ActualWidth, TextBlock1.ActualHeight));
// Get Top-Left grid coordinates
var GridTopLeft = (new Point(0, 0));
// Get Bottom-Right grid coordinates
var GridBottomRight = (new Point(GridBounds.ActualWidth, GridBounds.ActualHeight));
if (TextTopLeft.X < GridTopLeft.X || TextBottomRight.X > GridBottomRight.X)
{
// Out of bounds on X axis - Revert to copied X transform
TextDrag.TranslateX = savedTransform.TranslateX; ;
}
else if (TextTopLeft.Y < GridTopLeft.Y || TextBottomRight.Y > GridBottomRight.Y)
{
// Out of bounds on Y axis - Revert to copied Y transform
TextDrag.TranslateY = savedTransform.TranslateY;
}
else
{
// TextBlock is within bounds - Copy X and Y Transform
CopyTransform(transform, savedTransform);
}
}
Here it is in action.
I am trying to reproduce an animation for a button, like the animation of the native camera app buttons of Windows 10 Mobile, but I'm lost.
I'm using this example as a basis:
CameraStarterKit
The rotation of the buttons is already ok. o que eu gostaria de implementar era a animação.
Here is the code that the rotate buttons:
private void UpdateButtonOrientation()
{
int device = ConvertDeviceOrientationToDegress(_deviceOrientation);
int display = ConvertDisplayOrientationToDegrees(_displayOrientation);
if (_displayInformation.NativeOrientation == DisplayOrientations.Portrait)
{
device -= 90;
}
var angle = (360 + display + device) % 360;
var transform = new RotateTransform { Angle = angle };
PhotoButton.RenderTransform = transform;
}
How do I include an animation in this code.
Already grateful for any help!
Add RotateTransform property to your Button
<Button Grid.Row="1"
x:Name="PhotoButton"
Content="PhotoButton"
Click="PhotoButton_Click"
HorizontalAlignment="Center">
<Button.RenderTransform>
<RotateTransform x:Name="PhotoButtonRotateTransform"/>
</Button.RenderTransform>
</Button>
After that, you can create and manage Storyboard in code like this:
private void PhotoButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
AnimateProgressRingSlice(PhotoButtonRotateTransform.Angle + 90);
}
private void AnimateProgressRingSlice(double to, double miliseconds = 350)
{
var storyboard = new Storyboard();
var doubleAnimation = new DoubleAnimation();
doubleAnimation.Duration = TimeSpan.FromMilliseconds(miliseconds);
doubleAnimation.EnableDependentAnimation = true;
doubleAnimation.To = to;
Storyboard.SetTargetProperty(doubleAnimation, "Angle");
Storyboard.SetTarget(doubleAnimation, PhotoButtonRotateTransform);
storyboard.Children.Add(doubleAnimation);
storyboard.Begin();
}
Remark
But be careful if you don't know about RenderTransformOrigin property. Read More Info
I guess you need to use this property with 0.5, 0.5 value, so, modify your XAML and add to Button
RenderTransformOrigin="0.5, 0.5
Look the result:
You can define the animation as a Storyboard in the resources of your page:
<Page.Resources>
<Storyboard x:Name="rotate90">
... some animation that changes the RenderTransform of the button
<DoubleAnimation Storyboard.TargetName="PhotoButtonRotateTransform"
Storyboard.TargetProperty="Angle"
Value="90" x:Name="RotationAnimation" />
</Storyboard>
</Page.Resources>
And then start the animation from your code-behind:
private void UpdateButtonOrientation()
{
//here you can put some logic that
//sets the target value for the RotationAnimation's Value property
rotateButtonStoryboard.Begin();
}
For more information about animations and Storyboards check out the MSDN documentation
I'm using the ManipulationDelta event handler to drag and drop a simple ellipse in a canvas across the screen. I'm using the standard approach posted online in several places. Following is the code in my event handler:
Ellipse dragableItem = sender as Ellipse;
TranslateTransform translateTransform = dragableItem.RenderTransform as TranslateTransform;
double newPosX = Canvas.GetLeft(dragableItem) + translateTransform.X + e.Delta.Translation.X;
double newPosY = Canvas.GetTop(dragableItem) + translateTransform.Y + e.Delta.Translation.Y;
if (!isCanvasBoundary(newPosX, TestCanvas.ActualWidth - dragableItem.ActualWidth, 0))
translateTransform.X += e.Delta.Translation.X;
if (!isCanvasBoundary(newPosY, TestCanvas.ActualHeight - dragableItem.ActualHeight, 0))
translateTransform.Y += e.Delta.Translation.Y;
The drag and drop operation works fine, but there's a nasty delay of around 1 second between when the user begins dragging to when the ellipse actually changes its position. I can see by printing to the Debugger that the event handler itself finishes executing almost instantly, so I'm guessing it has something to do a pre-programmed refresh rate for all UIElements on the screen that's causing this delay?
Is there anyway around this issue?
I had the same problem some time ago. I guess that the delay is to decide whether the gesture is drag or tap. It's hard to touch a screen without any accidental drag.
To eliminate this lag, you can use PointerMove and PointerPressed events. Here's my example. You have canvas with two ellipses which can be dragged without any delay.
XAML
<Grid>
<Canvas x:Name="Board" PointerMoved="Canvas_PointerMoved" Background="Black">
<Ellipse Width="64" Height="64" Fill="Red"
Canvas.Left="32" Canvas.Top="128" PointerPressed="Ellipse_PointerPressed"/>
<Ellipse Width="96" Height=" 96" Fill="Blue"
Canvas.Left="128" Canvas.Top="16" PointerPressed="Ellipse_PointerPressed"/>
</Canvas>
</Grid>
As you can see, I'm handling PointerMoved event in canvas and PointerPressed event in ellipses. It's important that the background of the canvas is not transparent to handle touch events.
C#
public sealed partial class MainPage : Page
{
UIElement draggedItem = null;
Point offset;
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
private void Ellipse_PointerPressed(object sender, PointerRoutedEventArgs e)
{
draggedItem = sender as UIElement;
offset = e.GetCurrentPoint(draggedItem).Position;
}
private void Canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (draggedItem == null)
return;
Point dragPoint = e.GetCurrentPoint(Board).Position;
Canvas.SetLeft(draggedItem, dragPoint.X - offset.X);
Canvas.SetTop(draggedItem, dragPoint.Y - offset.Y);
}
}
I think the code is quite simple and understandable. I use PointerPressed to decide which object is dragged. I'm also calculating some offset, because we want to move the object relative to the point where user touches.
I have a textblock control on canvas which can be dragged horizontally to the right correctly as shown in the first and second image.
Then after I a 90 degree rotation angle is applied to its CompositeTransform, dragging the textblock to the right actually move it vertically towards the top as shown by the third and fourth image. What am I missing?
public CompositeTransform CurrentTransform = new CompositeTransform();
.....
TextBlock.RenderTransform = CurrentTransform;
....
private double angle;
public double Angle
{
get
{
return angle;
}
set
{
if (angle != value)
{
angle = value;
CurrentTransform.CenterX = 0;
CurrentTransform.CenterY = 0;
CurrentTransform.Rotation = angle;
}
}
}
The moving of the textbox is handled inside
private void CanvasText_ManipulationDelta(object sender, System.Windows.Input.ManipulationDeltaEventArgs e)
{
CurrentTransform.TranslateX += e.DeltaManipulation.Translation.X;
CurrentTransform.TranslateY += e.DeltaManipulation.Translation.Y;
}
For those who are in the same boat, I managed to fix this by attaching external gesture listener from Windows Phone toolkit instead of using the built-in CanvasText_ManipulationDelta event. The textbox dragging works correctly even after rotation.
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Image x:Name="ImageOriginal"
Source="{Binding WbPreview, Mode=TwoWay}"
Stretch="Uniform"/>
<Grid x:Name="GridDraw"
Tap="GridDraw_Tap"
Background="Transparent"/>
<Canvas x:Name="CanvasText">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="GestureListener_Tap"
DragDelta="GestureListener_DragDelta"/>
</toolkit:GestureService.GestureListener>
</Canvas>
</Grid>
I have such slider:
<Slider Minimum="0" Maximum="100"
TickFrequency="1"
IsMoveToPointEnabled="True"
IsSnapToTickEnabled="False"/>
I want to make next behavior:
when MouseDown occured at any point of slider, Thumb not only moves once to that point, but also following cursor until MouseUp occurs.
Sorry if it was asked already, i couldn't find that question.
This behavior will happen only when you drag the slider thumb itself, it is by design.
However, here's some code that will do it ;-)
Hook to the MouseMove event in XAML:
<Slider MouseMove="Slider_OnMouseMove" IsMoveToPointEnabled="True"/>
Then insert this code:
private void Slider_OnMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var slider = (Slider)sender;
Point position = e.GetPosition(slider);
double d = 1.0d / slider.ActualWidth * position.X;
var p = slider.Maximum * d;
slider.Value = p;
}
}
Note :
You might have to take margins and padding of your slider into account should they differ, I haven't.
This was done on an horizontal slider.
Minimum value of my slider was 0, make sure to adjust the calculation should your minimum be negative.
Finally, it does seem to work only when IsMoveToPointEnabled is set.
Aybe's solution works better in the thumb_MouseMove event.
void timelineThumb_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var thumb = sender as Thumb;
Point pos = e.GetPosition(uiVideoPlayerTimelineSlider);
double d = 1.0d / uiVideoPlayerTimelineSlider.ActualWidth * pos.X;
uiVideoPlayerTimelineSlider.Value = uiVideoPlayerTimelineSlider.Maximum * d;
}
}