I have an image that is inside a popup. When I am inside the canvas, and if a key is down I want to rotate the popup. My code doesnt update the rotation unless I leave the canvas (move mouse outside of canvas), then enter again (move mouse inside canvas). I confirmed the angle is being updated when the key is down inside the canvas, so I have no clue why it doesnt update unless I exit and reenter.
<Popup Name="floatingTip"
AllowsTransparency="True"
Placement="Relative"
PlacementTarget="{Binding ElementName=MainCanvas}">
<Popup.RenderTransform>
<RotateTransform CenterX="1" CenterY="1" Angle="{Binding Angle}"/>
</Popup.RenderTransform>
<Image Source="{Binding Name}"
Width="{Binding Width}"
Height="{Binding Height}"/>
</Popup>
When the mouse enters the canvas space, I set CanvasIsActive = true.
Here is the KeyDown event:
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (CanvasIsActive && currentTool == "staticBrush")
{
cursorImage.Angle += 20;
this.Title = cursorImage.Angle.ToString();
}
}
Here is my Angle property that is updated when a key is down:
double imgAngle;
public double Angle
{
get { return imgAngle; }
set
{
imgAngle = value;
OnPropertyChanged("Angle");
}
}
I figured it out. To refresh the rotation inside the popup, the popup IsOpen needs to be set to false, then back to true. Another solution is to move the RotateTransform from the Popup to the Image.
Related
I'm making a game inventory system where I have a grid and borders that take up a grid space have a background image of the icon. For example in grid position 0,0 I have a border with a background of a health potion. I want to be able to drag this border to any other grid location. Right now I'm working on just clicking and dragging the border to follow the mouse.
What I have so far sort of works but not really. If sort of freaks out when I left click on the icon and move slowly. However I can move out of the icon fast and it doesn't keep up so it then stops moving until I'm back over it. Any ideas on how to get this kind of functionality?
public partial class MainWindow : Window
{
TranslateTransform trans = new TranslateTransform();
Border border;
public MainWindow()
{
InitializeComponent();
// create a broder and set it's background image
border = new Border();
border.Visibility = System.Windows.Visibility.Visible;
var img = (Image)MasterGrid.FindResource("notepad");
var imgBrush = new ImageBrush(img.Source);
border.Background = imgBrush;
border.Margin = new Thickness(2.0);
// add the border to the grid
Grid.SetRow(border, 0);
Grid.SetColumn(border, 1);
Grid.SetRowSpan(border, 2);
Grid.SetColumnSpan(border, 1);
InvGrid.Children.Add(border);
// hook up events
_00.MouseUp += new System.Windows.Input.MouseButtonEventHandler(_00_MouseUp);
border.MouseDown += border_MouseDown;
border.MouseMove += border_MouseMove;
}
void border_MouseMove(object sender, MouseEventArgs e)
{
// make the border follow the mouse position
trans.X = e.GetPosition(border).X;
trans.Y = e.GetPosition(border).Y;
}
void border_MouseDown(object sender, MouseButtonEventArgs e)
{
border.RenderTransform = trans;
}
private void _00_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
MessageBox.Show("test");
}
}
It's not that easy, so I will give you some steps:
You should know sizes of cells, then you are able to calculate position of each one.
Prepare a container (a Grid for example), which will be only for moving purpose.
When you start dragging put the item in the container (2.), set its position under your cursor and hide the cell's content.
When you drop the item, calculate over which cell you are (maybe there is even a function to get a control under the cursor) and do the rest of logic.
I'm not sure if dragging is possible in case of regular controls, maybe you will have to replace your Grid with Canvas.
Try something like this:
<Grid>
<Border x:Name="DragContainer" Width="40" Height="40" />
<!-- OTHER STUFF - YOUR GRID WITH ITEMS -->
</Grid>
and modify only Border's Margin.
Another way to do it is to do dragging inside Canvas, then you will be able to set Canvas.Left, Canvas.Top - it should work properly.
<Canvas>
<Border x:Name="DragContainer" Width="40" Height="40" />
<!-- OTHER STUFF - YOUR GRID WITH ITEMS -->
</Canvas>
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 try to rotate a Border and have the MainWindow change his size based on the new space taken by the Border rotation.
I've set SizeToContent="WidthAndHeight" but window size does't take change when I rotated the border.
Do I need to programmatically set the Width and Height for the MainWindow or this can be achieved changing the xaml code in some other way?
My xaml code:
<Window x:Class="MyClass.MainWindow"
WindowStyle="None" AllowsTransparency='True'
Topmost='False' Background="Transparent" ShowInTaskbar='False'
SizeToContent="WidthAndHeight" WindowStartupLocation="Manual">
<Border Name="MyBorder"
BorderBrush="Transparent"
Background="Transparent"
HorizontalAlignment="Left"
VerticalAlignment="Top"
RenderTransformOrigin="0.5,0.5">
</Border>
</Windows>
My c# code on Window_KeyDown:
# RotateTransform rt = new RotateTransform() is declared at class level.
if (e.Key == Key.I)
{
if (rt.Angle + 1 < 360)
{
rt.Angle += 1;
}
else
{
rt.Angle = 0;
}
MyBorder.RenderTransform = rt;
}
Use LayoutTransform instead of RenderTransform
From MSDN: Transforms Overview
LayoutTransform – A transform that is applied before the layout pass. After the transform is applied, the layout system processes the
transformed size and position of the element.
RenderTransform – A transform that modifies the appearance of the element but is applied after the layout pass is complete. By using the
RenderTransform property instead of the LayoutTransform property, you
can obtain performance benefits.
Example
<Border Name="MyBorder"
BorderBrush="Transparent"
Background="Transparent"
HorizontalAlignment="Left"
VerticalAlignment="Top"
RenderTransformOrigin="0.5,0.5">
<Border.LayoutTransform>
<RotateTransform Angle="90"/>
</Border.LayoutTransform>
</Border>
So in your case
RotateTransform rt = new RotateTransform(0.0, 0.5, 0.5);
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.I)
{
if (rt.Angle + 1 < 360)
{
rt.Angle += 1;
}
else
{
rt.Angle = 0;
}
MyBorder.LayoutTransform = rt;
}
}}
On a wpf I have implemented the practice proposed as answer to the question: How to drag a UserControl inside a Canvas in order to drag and move items (shapes, child canvas) on a canvas. However as correctly indicated the next answer of the same question there is a flaw inside the method:
private void Control_MouseMove(object sender, MouseEventArgs e)
{
var draggableControl = sender as UserControl;
if (isDragging && draggableControl != null)
{
Point currentPosition = e.GetPosition(this.Parent as UIElement);
var transform = draggableControl.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = new TranslateTransform();
draggableControl.RenderTransform = transform;
}
transform.X = currentPosition.X - clickPosition.X;
transform.Y = currentPosition.Y - clickPosition.Y;
}
}
It uses the RenderTranform which does not changes the position of an item permanently but its visual position instead. The result is that the item returns to its initial position just on the next mouse event, so the drag and drop does not work properly (you cannot move it actually in this way but only visually).What kind of modification should be done on it to rectify the functionality of the method? Is there alternatively a similar practice that carries out the task properly? Should I use another Transform like Layout Transform?
What you did is a possible implementation but in WPF there exists already a Control which is made for being dragged around: the Thumb.
example:
<Canvas Width="200" Height="200" Background="Yellow">
<Thumb x:Name="DragThumb" DragDelta="Mover_DragDelta" Canvas.Top="20" Canvas.Left="10" Background="Gray" Width="50" Height="50" >
<Thumb.Template>
<ControlTemplate>
<Rectangle Fill="Black" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Canvas>
with a very simple code behind:
private void Mover_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Canvas.SetLeft(DragThumb, Canvas.GetLeft(DragThumb) + e.HorizontalChange);
Canvas.SetTop(DragThumb, Canvas.GetTop(DragThumb) + e.VerticalChange);
}
already moves your canvas. (and it stays as the Canvas.Top / Canvas.Left Positions got set).
You could use Canvas.Left and Canvas.Top property instead of mocking around with the RenderTransform.
Canvas.SetLeft(this, Canvas.GetLeft(this) + delta.X);
Canvas.SetTop(this, Canvas.GetTop(this) + delta.Y);