I'm trying to create a function that moves images arranged in a radial layout around in a circle by swapping each one's position with their neighbor's position. The final effect is that the images are rotating around in a circle. The transform is activated when the S (counterclockwise) or D (clockwise) keys are pressed. I'm using an array to track the positions of the images and sending those coordinates to a function that actually does the transform.
The first rotation in either direction works fine. But any consecutive rotation in the same direction produces strange unwanted movement. In essence, with every new rotation the images all move inward towards the center of the circle before moving out again to take their final positions. The amount of inward motion gets worse with each key press.
Since I'm not allowed to attach an image to this email I have posted one here:
http://i1266.photobucket.com/albums/jj532/ik_al/screencap.jpg
The image shows a series of screenshots to illustrate the phenomenon. Please note that the screenshots are all happening on ONE rotation.
Here's my XAML file:
<Window x:Class="radialLayout.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MyNamespace="clr-namespace:radialLayout"
Title="MainWindow" Height="350" Width="525" KeyUp="Window_KeyUp">
<Grid Width="1024" Height="768">
<MyNamespace:RadialPanel Margin="27,21,31,32" MouseWheel="RadialPanel_MouseWheel" x:Name="ImagePanel">
<!--Must use same namespace declared above-->
<!--Each image must have a unique name-->
<Image Height="49" Name="image1" Width="74" Source="/radialLayout;component/Images/profile.jpg" />
<Image Height="49" Name="image2" Width="74" Source="/radialLayout;component/Images/IMG_0841.JPG" />
<Image Height="49" Name="image3" Width="74" Source="/radialLayout;component/Images/profile.jpg" />
<Image Height="49" Name="image4" Width="74" Source="/radialLayout;component/Images/IMG_0841.JPG" />
<Image Height="49" Name="image5" Width="74" Source="/radialLayout;component/Images/profile.jpg" />
<Image Height="49" Name="image6" Width="74" Source="/radialLayout;component/Images/IMG_0863.JPG" />
<Image Height="49" Name="image7" Width="74" Source="/radialLayout;component/Images/profile.jpg" />
<Image Height="49" Name="image8" Width="74" Source="/radialLayout;component/Images/IMG_1043.JPG" />
<Image Height="49" Name="image9" Width="74" Source="/radialLayout;component/Images/profile.jpg" />
<Image Height="49" Name="image10" Width="74" Source="/radialLayout;component/Images/IMG_0863.JPG" />
<Image Height="49" Name="image11" Width="74" Source="/radialLayout;component/Images/profile.jpg" />
<Image Height="49" Name="image12" Width="74" Source="/radialLayout;component/Images/IMG_0863.JPG" />
</MyNamespace:RadialPanel>
And here is the function call and function implementation:
for (int o = 0; o < VisualTreeHelper.GetChildrenCount(ImagePanel); o++)
{
Visual childVisual = (Visual)VisualTreeHelper.GetChild(ImagePanel, o);
MyExtensions.MoveTo((Image)childVisual, lastPosition[o, 0], lastPosition[o, 1], ImagePanel.imageCoordinates[o, 0], ImagePanel.imageCoordinates[o, 1]);
}
public static void MoveTo(this Image target, double currentX, double currentY, double newX, double newY)
{
Vector offset = VisualTreeHelper.GetOffset(target);
var top = offset.Y;
var left = offset.X;
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, newY - top, TimeSpan.FromSeconds(1));
DoubleAnimation anim2 = new DoubleAnimation(0, newX - left, TimeSpan.FromSeconds(1));
trans.BeginAnimation(TranslateTransform.YProperty, anim1);
trans.BeginAnimation(TranslateTransform.XProperty, anim2);
}
Does anyone know what is causing this behavior or how to fix it?
After much investigation I finally figured out what I was doing wrong in my code.
I didn't understand that when using translateTranform the original position of the images is retained as the starting point for all consecutive transforms; I assumed it was updated to the last most current position. In addition, this starting point is always referenced by coordinates (0,0).
So to fix my animation problem I had to offset the start and stop positions of my images by subtracting each image's original position (before the first transform) from the current placement of the image on the screen (as stored in the array). For the first pass through these values will always add up to 0.
I was already completing this offset for the stop positions since it was necessary to get even the first transform to work. But as it turned out, this subtraction was needed to update the start positions as well.
In the code the original position of the image is stored in the left and top variables which reference the X and Y coordinates respectively.
Here's the part of the code that I changed:
DoubleAnimation anim1 = new DoubleAnimation(currentY-top, newY - top, TimeSpan.FromSeconds(1));
DoubleAnimation anim2 = new DoubleAnimation(currentX-left, newX - left, TimeSpan.FromSeconds(1));
What was interesting about this error was that the resulting animation when all 12 images were transformed together was much more complex and interesting than it would have been if each image was moved individually. So there must be some interaction between how the image transforms are computed that produces emergent output. The output I was seeing made me think the solution was much more complex than it actually was.
Related
I have an image that is dynamically loaded into a WPF image control. (It's a map.)
When the user sets a coordinate (which is a saved waypoint from a GPS) I want to mark that location on the map from C#.
When the application window is resized, the mark should stay in the correct place on the map.
The map should scale with the window, but not stretch.
I've been digging around for days looking for ways to get this done, but can't get any further than loading the image into the window. The image is in a dockpanel, with some other controls down the left side of the window.
Things I know:
I have the coordinates (longitude and latitude) of the corners of the map. I have the longitude and latitude of the waypoint.
UI XAML:
<DockPanel>
<Grid x:Name="ImageGrid">
<Image Stretch="UniformToFill" x:Name="MapImage" Source="locimages/woorim.jpg" />
<Ellipse Name="Marker1" Height="20" Fill="Red" Margin="0,179,152,0" HorizontalAlignment="Right" VerticalAlignment="Top" Width="20"/>
</Grid>
</DockPanel>
Code (Not functional, just showing how the lat/long calcs are currently being done.):
if (wayPoint.Longitude < minLong || wayPoint.Longitude > maxLong || wayPoint.Latitude < minLat ||
wayPoint.Latitude > maxLat)
{
MessageBox.Show("This point is not in this area. It will not display on this map.");
return;
}
// Map location to image coordinates.
var newX = (wayPoint.Longitude - minLong)/(maxLong - minLong) * MapImage.Source.Width;
var newY = (wayPoint.Latitude - minLat) / (maxLat - minLat) * MapImage.Source.Height;
//Marker1.Margin = ??;
I'm trying to crop a circle from one image, and put it on top another image in WPF.
The Circle's center changes according to the mouse movements, and needs to be bounded dynamically.
I tried to position two images on top of each other, and use a third image that I draw in real time as an opacity mask.
Could you please provide short code to solve this problem efficiently ?
The code below describes what you can do with an OpacityMask. It's a little counterintuitive, because we expect a XAML rendering to layer elements bottom-to-top.
However, in this case you want your "background" image to layer on top of the foreground, because the OpacityMask will serve to display only that portion of the foreground described by the position and size of the VisualBrush, rendering the rest transparent. It's given as follows:
<Grid x:Name="MainGrid" MouseMove="Grid_MouseMove">
<Rectangle Fill="Red" ></Rectangle>
<Rectangle Fill="Green">
<Rectangle.OpacityMask>
<VisualBrush Stretch="None" >
<VisualBrush.Visual>
<Ellipse Width="40" Height="40" StrokeThickness="1" Fill="Black" />
</VisualBrush.Visual>
<VisualBrush.RelativeTransform>
<TransformGroup>
<TranslateTransform x:Name="OpacityFilterTransform" X="1" Y="1"/>
</TransformGroup>
</VisualBrush.RelativeTransform>
</VisualBrush>
</Rectangle.OpacityMask>
</Rectangle>
</Grid>
Then, this event handler code computes the position of the ellipse and applies it to the OpacityFilter's TranslateTransform object, giving you control over the position of the image.
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(this);
var height = MainGrid.ActualHeight;
var width = MainGrid.ActualWidth;
// with the position values, interpolate a TranslateTransform for the opacity mask
var transX = position.X / width;
var transY = position.Y / height;
OpacityFilterTransform.X = transX - 0.5;
OpacityFilterTransform.Y = transY - 0.5;
}
This solution should work for any descendant of Visual you care to layer.
I have seen several similar questions to this, but no solution works for me, probably because my Grid is set to Stretch . This is the result I am trying to achieve. Basically, I have one Grid and I'd like to draw an X-Axis line( like in the picture at the end of this question).
My XAML
<Grid x:Name="MainGrid" HorizontalAlignment="Stretch" Background="#FF4A70F1">
My CodeBehind
Line XAxis = new Line();
XAxis.Stroke = System.Windows.Media.Brushes.Black;
XAxis.StrokeThickness = 1;
XAxis.X1 = 0;
XAxis.X2 = MainGrid.RenderSize.Width;
XAxis.Y1 = MainGrid.RenderSize.Height / 2;
XAxis.Y2 = MainGrid.RenderSize.Height / 2;
MainGrid.Children.Add(XAxis);
My Problem
I 'd like this line to span the whole window, even if I resize/maximize the window. However, the calculations above for X2,Y1,Y2 don't work( all evaluate to 0, hence NO LINE shown) , whether I use ActualSizeor *RenderSize* . Can someone please point out how to fix that? Thank you
This XAML creates a vertically centered line that stretches horizontally:
<Grid>
<Line HorizontalAlignment="Left" VerticalAlignment="Center" Stroke="Black"
X2="{Binding ActualWidth,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}"/>
</Grid>
How to find the child elements positions in a stack panel.
<StackPanel Orientation="Horizontal">
<ToggleButton Width="20"
Height="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="Visible" />
<TextBlock Margin="5"
VerticalAlignment="Center"
FontSize="15"
Text="Selection Mode" />
</StackPanel>
How to find the X,Y position of Toggle button and Text block?
You could always use TranslatePoint to translate coordinates relative to one UIElement to coordinates relative to another UIElement:
var toggleButtonPosition = toggleButton.TranslatePoint(new Point(0, 0), stackPanel);
var textBlockPosition = textBlock.TranslatePoint(new Point(0, 0), stackPanel);
The above code translates the point (0, 0) relative to the respective control to coordinates relative to the containing StackPanel, and hence gives the position of each control inside the StackPanel.
Basically, the position of a control is determined by the control which holds it, the margin property, the alignement and such.
You could use this to determine the position of the child control.
I have created a Rectangle inside of a ScrollViewer like this
<ScrollViewer ManipulationMode="Control" x:Name="songScrollViewer" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" Height="270" VerticalAlignment="Center" Width="728" Canvas.Top="20" d:LayoutOverrides="HorizontalMargin" >
<Rectangle x:Name="musicBG" Fill="#FF0692FD"/>
</ScrollViewer>
During the use of the app, the size of MusicBg changes, sometimes to something around 3,000 pixels width.
musicBG.Width = _songLength*PixelsPerSecond
However, while scrolling the scrollViewer, it allows me to scroll the rectangle all the way off the screen.
For example this line of code gives me the following values when I have moved the rectangle as far as I want to move it.
if (songScrollViewer.HorizontalOffset > songScrollViewer.ScrollableWidth)
HorizontalOffset has a value of ~1200 and ScrollableWidth has a value of about ~2900.
How can I get this to be done properly so that the rectangle is not scrolled completely off the screen?
I would expect a HorizontalOffset of about 1200 to only push the rectangle about halfway through to it's destination, and not make it start going off screen.
ANSWER:
After much frustration, I was able to solve this problem by using Canvas instead of Border or Rectangle.
I'll award points if anyone can explain why this problem happened, and if there is a less processor intensive control that would work better than canvas.
Edit: Screen shots:
Bad Code:
<ScrollViewer ManipulationMode="Control" x:Name="songScrollViewer" Width="720" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" Height="270" VerticalAlignment="Top" Canvas.Top="20" HorizontalAlignment="Left" >
<Border x:Name="musicBG" Background="#FF0692FD" VerticalAlignment="Top" HorizontalAlignment="Left" Height="270" />
</ScrollViewer>
Image of bad scroll with bad code:
Good working code:
<ScrollViewer ManipulationMode="Control" x:Name="songScrollViewer" Width="720" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" Height="270" VerticalAlignment="Top" Canvas.Top="20" HorizontalAlignment="Left" >
<Canvas x:Name="musicBG" Background ="#FF0692FD" Height="270" >
<Border Background="#FF0692FD" VerticalAlignment="Top" HorizontalAlignment="Left" Height="270" />
</Canvas>
</ScrollViewer>
Good Scroll: Notice it says 170 seconds on the bottom right instead of the smaller number of 118 seconds in the bad scroll.
I believe your right, wp7 won't render shapes that are bigger then 2048 pixels. So the reason it's scrolling of the page is because it's treating it as if it were bigger then 2048 but you can only see up to a width of 2048px and its just scrolling over to the "ghost" part of the rectangle.
I'm not sure if you can override this but the best solution I could come up with (without overriding) is by splitting up your rectangle into chucks that are smaller then 2000 (just to be safe) and then displaying them seamlessly in a horizontal stack panel inside the scroll viewer. The problem with this is that depending on how you've coded it, this solution might be hard to implement; but you might just be able to split it in your ViewModel when displaying it and your logic would only see it as one big chunk.