I am adding rectangles on a Canvas using Random because I don't know the width and height.
Code
HProDataContext db = new HProDataContext();
var RoomX = (from d in db.rooms select d.sizex).ToList();
var RoomY = (from d in db.rooms select d.sizey).ToList();
var random = new Random();
for (int i = 0; i < RoomX.Count; i++)
{
RoomX[i] = (Convert.ToDouble(RoomX[i]) * 10).ToString();
RoomY[i] = (Convert.ToDouble(RoomY[i]) * 10).ToString();
var rectangle = new Rectangle()
{
Stroke = Brushes.Black,
Fill = Brushes.SkyBlue,
Width = Convert.ToDouble(RoomX[i]),
Height = Convert.ToDouble(RoomY[i]),
Margin = new Thickness(
left: random.NextDouble() * 300,
top: random.NextDouble() * 150,
right: 0,
bottom: 0),
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top
};
mainCanvas.Children.Add(rectangle);
}
}
It works good, but some rectangles are drawing over another rectangles.
How to draw rectangles there where is empty space?
You can calculate a Rect for each WPF Rectangle and use IntersectsWith method found in Rect to test if the code can a draw a new Rectangle without overlaps. I also recommend using rectangle properties Canvas.Left and Canvas.Top instead of adjusting the Margin.
I've included a simple example that you test to get you started.
Here is some XAML containing 4 Rectangles:
<Window x:Class="OverlapRectangeTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<Canvas x:Name="c" Grid.Row="0">
<Rectangle x:Name="b1" Fill="Aqua" Canvas.Left="0" Canvas.Top="0" Width="100" Height="200"/>
<Rectangle x:Name="b2" Fill="Bisque" Canvas.Left="0" Canvas.Top="0" Width="200" Height="100"/>
<Rectangle x:Name="b3" Fill="BlueViolet" Canvas.Left="250" Canvas.Top="0" Width="100" Height="200"/>
<Rectangle x:Name="b4" Fill="Cornsilk" Canvas.Left="111" Canvas.Top="0" Width="100" Height="200"/>
</Canvas>
<Button Grid.Row="1" Click="Button_Click"/>
</Grid>
</Window>
Here is a button click event handler where I test the IntersectsWith method:
private void Button_Click(object sender, RoutedEventArgs e)
{
double top1 = (double)b1.GetValue(Canvas.TopProperty);
double left1 = (double)b1.GetValue(Canvas.LeftProperty);
Rect rect1 = new Rect(left1, top1, b1.Width, b1.Height);
double top2 = (double)b2.GetValue(Canvas.TopProperty);
double left2 = (double)b2.GetValue(Canvas.LeftProperty);
Rect rect2 = new Rect(left2, top2, b2.Width, b2.Height);
double top3 = (double)b3.GetValue(Canvas.TopProperty);
double left3 = (double)b3.GetValue(Canvas.LeftProperty);
Rect rect3 = new Rect(left3, top3, b3.Width, b3.Height);
double top4 = (double)b4.GetValue(Canvas.TopProperty);
double left4 = (double)b4.GetValue(Canvas.LeftProperty);
Rect rect4 = new Rect(left4, top4, b4.Width, b4.Height);
bool rc0 = rect1.IntersectsWith(rect4);
bool rc1 = rect1.IntersectsWith(rect2);
bool rc2 = rect2.IntersectsWith(rect3);
bool rc3 = rect3.IntersectsWith(rect1);
bool rc4 = rect2.IntersectsWith(rect4);
}
Related
Good evening! I'm trying to set SetZIndex for a childe element in a XAML layout. I found information about the Panel.SetZIndex and Canvas.SetZIndex. If I'm not mistaken, it's the same thing, but a Canvas.SetZIndex is older then Panel.SetZIndex.
So, the code below is my XAML layout
<Window x:Class="MLvisualisator.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:MLvisualisator"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="50px"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0">
<TextBox Name="CCountOfNeyrons" Width="200" Margin="10" BorderBrush="Aqua"></TextBox>
<TextBox Name="CountOfColum" Width="200" Margin="10" BorderBrush="Aqua"></TextBox>
<Button Name="Generate" Content="Generate" Click="GenerateFun"/>
</StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" Grid.Column="0" Grid.Row="1">
<Canvas Name="TestAdd">
//there will be child elements
</Canvas>
</ScrollViewer>
</Grid>
</Window>
My class, where I created an Ellipse as a child element
...
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(28, 28, 28, 0);
Ellipse ell = new Ellipse
{
Width = sc.NeuronRadius,
Height = sc.NeuronRadius,
Fill = mySolidColorBrush,
Name = id
};
Canvas.SetLeft(ell, columCanvas);
Canvas.SetTop(ell, rowCanvas);
Panel.SetZIndex(ell, 2); // It doesn't work, and I don't know why
TestAdd.Children.Add(ell);
...
Later I did the same with Lines:
...
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(120, 255, 0, 0);
Line line = new Line // x1, x2, y1, y2, sc.NeuronRadius are an integer params
{
X1 = x1 + (sc.NeuronRadius / 2),
X2 = x2 + (sc.NeuronRadius / 2),
Y1 = y1 + (sc.NeuronRadius / 2),
Y2 = y2 + (sc.NeuronRadius / 2),
Name = "W" + start + "_" + end,
StrokeThickness = 4,
Stroke = mySolidColorBrush,
};
TestAdd.Children.Add(line);
Panel.SetZIndex(line, 0); // It doesn't work, and I don't know why
...
The result is this picture
But I want the lines to be under the Ellipses
Where did I made a mistake?
The Lines are actually drawn below the Ellipses.
That is however not very obvious because you are drawing both shape types with semi-transparent Brushes.
To make this clear, write for example
Ellipse ell = new Ellipse
{
Fill = Brushes.LightGray,
...
};
and
Line line = new Line
{
Stroke = Brushes.Red,
...
};
The first parameter of the Color.FromArgb method is an alpha value, i.e. an opacity. A value of 0 means fully transparent, while 255 means fully opaque.
i used WPF application and i have drawn a line on a dicom image.but my problem is when i resize main window, line positions are changed. it wont persist on the place where i drawn. i have tried with pointtoscreen method and translatepoint method. but didnt work. what is the correct way to do this.
i used two wpf image controls. one is to display the dicom image and other is on top of it to draw annotation.following is my wpf xaml code.
<Border x:Name="Border" ClipToBounds="True">
<Grid>
<Image x:Name="ImageTileViewer"
Margin="-234"
Source="{Binding CurrentImage}"
Stretch="None"
StretchDirection="DownOnly" RenderTransformOrigin="0.5,0.5"/>
<Grid Name="AnnotationImageGrid">
<Image Name="AnnotationImage" Stretch="None" RenderTransformOrigin="0.5,0.5"/>
</Grid>
</Grid>
<Grid>
<Canvas Name="canDrawing" Height="450" Width="800"
MouseMove="canDrawing_MouseMove_NotDown"
MouseDown="canDrawing_MouseDown">
<Image Name="imgTrash"
StretchDirection="DownOnly" RenderTransformOrigin="0.5,0.5"
Source="C:\Users\Chaithika\Downloads\after_resize.PNG" />
</Canvas>
</Grid>
public Window1()
{
InitializeComponent();
TransformGroup group = new TransformGroup();
ScaleTransform xform = new ScaleTransform();
group.Children.Add(xform);
TranslateTransform tt = new TranslateTransform();
group.Children.Add(tt);
imgTrash.RenderTransform = group;
canDrawing.RenderTransform = group;
}
private void Window1_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
var width = Convert.ToInt32(e.NewSize.Width);
var height = Convert.ToInt32(e.NewSize.Height);
double factor = Math.Min((width / imgTrash.ActualWidth), (height / imgTrash.ActualHeight));
if (width >= imgTrash.ActualWidth && height >= imgTrash.ActualHeight)
{
factor = 1;
}
TransformGroup transformGroup = (TransformGroup)imgTrash.RenderTransform;
ScaleTransform st = (ScaleTransform)transformGroup.Children[0];
st.ScaleX = factor;
st.ScaleY = factor;
TransformGroup transformGroup2 = (TransformGroup)canDrawing.RenderTransform;
ScaleTransform st2 = (ScaleTransform)transformGroup2.Children[0];
st2.ScaleX = factor;
st2.ScaleY = factor;
}
I am trying to create a simple transformation box. I started to add resizing (currently only extending width on the right) but when I change the Width and then rotate, the Y translation messes up. I figure the Y translation must change based on the rotation to keep manipulations looking consistent, but for some reason, I can't fathom how to factor this in.
heres a gif of what I mean
(The following UserControl is a child of a Canvas control)
.xaml
<UserControl
x:Class="TransformBox"
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"
mc:Ignorable="d"
PointerPressed="UserControl_PointerPressed"
PointerMoved="UserControl_PointerMoved"
PointerReleased="UserControl_PointerReleased"
Width="200" Height="150">
<UserControl.Resources>
<Style x:Key="ScaleEllipseStyle" TargetType="Ellipse">
<Setter Property="Width" Value="12"/>
<Setter Property="Height" Value="12"/>
<Setter Property="Fill" Value="{StaticResource SystemAccentColor}"/>
</Style>
</UserControl.Resources>
<Grid>
<Rectangle x:Name="RotateRegion" Stroke="Purple" StrokeThickness="20" Margin="-20"/>
<Rectangle x:Name="TranslateRegion" Fill="Transparent" StrokeThickness="2"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="-6,-6,0,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-6,0,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-6,-6,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="-6,0,0,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,-6,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="-6,0,0,-6"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,-6"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-6,-6"/>
<Ellipse x:Name="OriginRegion" Width="12" Height="12" Fill="Transparent" Stroke="{StaticResource SystemAccentColor}" StrokeThickness="2"/>
</Grid>
</UserControl>
.xaml.cs
public sealed partial class TransformBox : UserControl
{
private bool isDragging;
private Point prevPosition;
private Point _clickPosition;
private TransformType action;
private double _prevRot = 0;
public float SetWidth = 200;
public float SetHeight = 150;
public Point Origin = new Point(0.5, 0.5);
public float X = 0;
public float Y = 0;
public new float Rotation = 0;
public TransformBox()
{
this.InitializeComponent();
Width = SetWidth;
Height = SetHeight;
}
private void UserControl_PointerPressed(object sender, PointerRoutedEventArgs e)
{
isDragging = true;
_clickPosition = prevPosition = e.GetCurrentPoint(Parent as UIElement).Position;
this.CapturePointer(e.Pointer);
if (e.OriginalSource == TranslateRegion) {
action = TransformType.Translate;
}
else if (e.OriginalSource == RotateRegion)
{
action = TransformType.Rotate;
_prevRot = Math.Atan2(_clickPosition.Y - (Y + Height / 2), _clickPosition.X - (X + Width / 2));
}
else if (e.OriginalSource == OriginRegion)
{
action = TransformType.TransformOrigin;
}
else
{
action = TransformType.Scale;
}
}
private void UserControl_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (isDragging)
{
Point mousePos = e.GetCurrentPoint(Parent as UIElement).Position;
switch (action)
{
case TransformType.Translate:
X += (float) (mousePos.X - prevPosition.X);
Y += (float) (mousePos.Y - prevPosition.Y);
prevPosition = mousePos;
break;
case TransformType.Rotate:
double curRot = Math.Atan2(mousePos.Y - (Y + Height / 2), mousePos.X - (X + Width / 2));
Rotation += (float) (curRot - _prevRot);
_prevRot = curRot;
break;
case TransformType.Scale:
double dist = Math.Sqrt(Math.Pow(mousePos.X - _clickPosition.X, 2) + Math.Pow(mousePos.Y - _clickPosition.Y, 2) * 1.0);
double mouseRot = Math.Atan2(mousePos.Y - _clickPosition.Y, mousePos.X - _clickPosition.X);
double proj = dist * Math.Cos(0 - mouseRot);
Width = SetWidth + proj;
break;
}
Matrix4x4 translate = Matrix4x4.CreateTranslation(new Vector3(X, Y, 0f));
Matrix4x4 rotate = Matrix4x4.CreateRotationZ(Rotation, new Vector3(X + (float) SetWidth / 2, Y + (float) SetHeight / 2, 0f));
TransformMatrix = Matrix4x4.Multiply(translate, rotate);
}
}
private void UserControl_PointerReleased(object sender, PointerRoutedEventArgs e)
{
isDragging = false;
SetWidth = (float) Width;
SetHeight = (float) Height;
this.ReleasePointerCapture(e.Pointer);
}
enum TransformType
{
Translate,
Rotate,
Scale,
TransformOrigin
}
}
Try compensating the drift of the rotation-centre in the PointerReleased event handler, anyway.
private void UserControl_PointerReleased(object sender, PointerRoutedEventArgs e)
{
isDragging = false;
double deltaCentreX = 0.5 * (Width - SetWidth);
double deltaCentreY = 0.5 * (Height - SetHeight);
X -= (float)((1.0 - Math.Cos(Rotation)) * deltaCentreX + Math.Sin(Rotation) * deltaCentreY);
Y -= (float)(-Math.Sin(Rotation) * deltaCentreX + (1.0 - Math.Cos(Rotation)) * deltaCentreY);
SetWidth = (float) Width;
SetHeight = (float) Height;
this.ReleasePointerCapture(e.Pointer);
}
Does anyone know of an easy way to animate a movement from an Image's current location to a new location (X,Y) using WPF animation with no XAML, 100% programmatically? And with no reference to "this" (with RegisterName etc).
I am trying to make an extension class for Image to do animation stuff on it. It is easy enough to change the width and height properties through animation, but after searching for location animation of an object it suddenly becomes more advanced.
As it is an extension class I will only have a reference to the actual Image object and the X and Y I want to move it to.
public static void MoveTo(this Image targetControl, double X, double Y, double Width, double Height){
//code here
...
}
Update:
Thanks. Almost working. It seems The GetTop and GetLeft returns 'NaN' not explicitly set. Found the workaround in this post: Canvas.GetTop() returning NaN
public static void MoveTo(this Image target, 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(10));
DoubleAnimation anim2 = new DoubleAnimation(0, newX - left, TimeSpan.FromSeconds(10));
trans.BeginAnimation(TranslateTransform.YProperty, anim1);
trans.BeginAnimation(TranslateTransform.XProperty, anim2);
}
I had to swap two of the values (FROM) with 0. I assume that must be because in this context the upper left corner of the picture is the origin? But now it works.
Try this:
public static void MoveTo(this Image target, double newX, double newY)
{
var top = Canvas.GetTop(target);
var left = Canvas.GetLeft(target);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(top, newY - top, TimeSpan.FromSeconds(10));
DoubleAnimation anim2 = new DoubleAnimation(left, newX - left, TimeSpan.FromSeconds(10));
trans.BeginAnimation(TranslateTransform.XProperty,anim1);
trans.BeginAnimation(TranslateTransform.YProperty,anim2);
}
Here it is... It changes the size and moves a MediaElement under the Canvas. Just input your parameters:
Storyboard story = new Storyboard();
DoubleAnimation dbWidth = new DoubleAnimation();
dbWidth.From = mediaElement1.Width;
dbWidth.To = 600;
dbWidth.Duration = new Duration(TimeSpan.FromSeconds(.25));
DoubleAnimation dbHeight = new DoubleAnimation();
dbHeight.From = mediaElement1.Height;
dbHeight.To = 400;
dbHeight.Duration = dbWidth.Duration;
story.Children.Add(dbWidth);
Storyboard.SetTargetName(dbWidth, mediaElement1.Name);
Storyboard.SetTargetProperty(dbWidth, new PropertyPath(MediaElement.WidthProperty));
story.Children.Add(dbHeight);
Storyboard.SetTargetName(dbHeight, mediaElement1.Name);
Storyboard.SetTargetProperty(dbHeight, new PropertyPath(MediaElement.HeightProperty));
DoubleAnimation dbCanvasX = new DoubleAnimation();
dbCanvasX.From = 0;
dbCanvasX.To = 5;
dbCanvasX.Duration = new Duration(TimeSpan.FromSeconds(.25));
DoubleAnimation dbCanvasY = new DoubleAnimation();
dbCanvasY.From = 0;
dbCanvasY.To = 5;
dbCanvasY.Duration = dbCanvasX.Duration;
story.Children.Add(dbCanvasX);
Storyboard.SetTargetName(dbCanvasX, mediaElement1.Name);
Storyboard.SetTargetProperty(dbCanvasX, new PropertyPath(Canvas.LeftProperty));
story.Children.Add(dbCanvasY);
Storyboard.SetTargetName(dbCanvasY, mediaElement1.Name);
Storyboard.SetTargetProperty(dbCanvasY, new PropertyPath(Canvas.TopProperty));
story.Begin(this);
<Viewbox Stretch="Uniform" StretchDirection="Both" SnapsToDevicePixels="True">
<Grid Width="640" Height="480" Name="MainLayout" SnapsToDevicePixels="True" Background="Black">
<Canvas Width="640" Height="480" Name="MainCanvas" SnapsToDevicePixels="True">
<MediaElement Height="171" HorizontalAlignment="Left" Name="mediaElement1" VerticalAlignment="Top" Width="337" LoadedBehavior="Manual" Margin="166,140,0,0" Canvas.Left="-162" Canvas.Top="-140" />
<Button Canvas.Left="294" Canvas.Top="196" Content="Button" Height="23" Name="button1" Width="75" Click="button1_Click" />
</Canvas>
</Grid>
</Viewbox>
UPDATE:
Instead of MediaElement use this line:
<Rectangle Height="171" HorizontalAlignment="Left" Name="mediaElement1" VerticalAlignment="Top" Width="337" Margin="166,140,0,0" Canvas.Left="-162" Canvas.Top="-140" Fill="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" />
And don't forget to put the C# code to:
private void button1_Click(object sender, RoutedEventArgs e) {}
You can use MediaElement as well but you have to define a VideoClip to see something ;)
Please find a solution that uses the Left and Top properties of Canvas for the extension method. See the following code:
public static void MoveTo(this Image target, Point newP)
{
Point oldP = new Point();
oldP.X = Canvas.GetLeft(target);
oldP.Y = Canvas.GetTop(target);
DoubleAnimation anim1 = new DoubleAnimation(oldP.X, newP.X, TimeSpan.FromSeconds(0.2));
DoubleAnimation anim2 = new DoubleAnimation(oldP.Y, newP.Y , TimeSpan.FromSeconds(0.2));
target.BeginAnimation(Canvas.LeftProperty , anim1);
target.BeginAnimation(Canvas.TopProperty, anim2);
}
This code is based on #DeanChalk's answer.
It moves an Image contained within a Canvas (RFID_Token) diagonally from the top-right to the bottom-left, positioned centrally over another Image within a Canvas (RFID_Reader).
<Canvas>
<Canvas x:Name="RFID_Reader_Canvas">
<Image x:Name="RFID_Reader" Source="RFID-Reader.png" Height="456" Width="682" Canvas.Left="37" Canvas.Top="524"/>
</Canvas>
<Canvas x:Name="RFID_Token_Canvas">
<Image x:Name="RFID_Token" Source="RFID-Token.png" Height="268" Width="343" Canvas.Left="874" Canvas.Top="70"/>
</Canvas>
</Canvas>
var StartX = Canvas.GetLeft(RFID_Token);
var StartY = Canvas.GetTop(RFID_Token);
var EndX = RFID_Reader.Width / 2 + Canvas.GetLeft(RFID_Reader) - StartX - (RFID_Token.Width / 2);
var EndY = RFID_Reader.Height / 2 + Canvas.GetTop(RFID_Reader) - StartY - (RFID_Token.Height / 2);
var AnimationX = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(1));
var AnimationY = new DoubleAnimation(0, EndY, TimeSpan.FromSeconds(1));
var Transform = new TranslateTransform();
RFID_Token_Canvas.RenderTransform = Transform;
Transform.BeginAnimation(TranslateTransform.XProperty, AnimationX);
Transform.BeginAnimation(TranslateTransform.YProperty, AnimationY);
I kept having NaN or 0 values for my nested elements, here's a modified version of Danny's answer :
public void MoveTo(Canvas canvas, FrameworkElement target, FrameworkElement destination)
{
Point oldPoint = target.TransformToAncestor(canvas).Transform(new Point(0, 0));
Point newPoint = destination.TransformToAncestor(canvas).Transform(new Point(0, 0));
var EndX = destination.Width / 2 + newPoint.X - oldPoint.X - (target.Width / 2);
var EndY = destination.Height / 2 + newPoint.Y - oldPoint.Y - (target.Height / 2);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(0.3));
DoubleAnimation anim2 = new DoubleAnimation(0, EndY, TimeSpan.FromSeconds(0.3));
trans.BeginAnimation(TranslateTransform.XProperty, anim1);
trans.BeginAnimation(TranslateTransform.YProperty, anim2);
}
I'm having some trouble understanding (and fixing) a bug I'm experiencing.
I have the UI like in the following picture:
All of those areas colored in lightblue are Canvases and they are movable. This is where I have my problem. The one from the top left can be moved without problems. The other two, when I'm dragging them, disappear. I can't explain why.
This is the code for the moving of elements:
// this is all inside the MouseMove event handler function
// If there is no dragged element
if (this.DraggedElement == null || !this.IsDragInProgress)
return;
/*
* Calculating the new position for the dragged element
*/
// Mouse current position
Point cursor = e.GetPosition(this);
double xMove = 0;
double yMove = 0;
// Movement detected
if (cursor != MouseClickLocation)
{
// Moving on the x-axis and y-axis
xMove = cursor.X - MouseClickLocation.X;
yMove = cursor.Y - MouseClickLocation.Y;
// Actually moving the element
if (this.ConstrainToBounds(this.DraggedElement, mainWindow))
{
TranslateTransform translate = new TranslateTransform(xMove, yMove);
this.DraggedElement.RenderTransform = translate;
}
}
The code for ConstrainToBounds() method is supposed to not allow me to move any Canvas outside the window borders (it works perfectly for the top left Canvas, but not for the others) and is as follows:
private Boolean ConstrainToBounds(Canvas element, UIElement container)
{
try
{
Boolean respects = true;
// Values used to reset the element position to a proper location
double xReset = 0;
double yReset = 0;
// Left x-axis constraint
if (element.TranslatePoint(new Point(), container).X <= new Point(0, 0).X)
{
respects = false;
// Get elements' current position and adjust
xReset = element.TranslatePoint(new Point(), container).X + 1;
yReset = element.TranslatePoint(new Point(), container).Y;
TranslateTransform translate = new TranslateTransform(xReset, yReset);
element.RenderTransform = translate;
}
// Right x-axis constraint
if (element.TranslatePoint(new Point(), container).X + element.RenderSize.Width >= container.RenderSize.Width)
{
respects = false;
// Get elements' current position and adjust
xReset = element.TranslatePoint(new Point(), container).X - 1;
yReset = element.TranslatePoint(new Point(), container).Y;
TranslateTransform translate = new TranslateTransform(xReset, yReset);
element.RenderTransform = translate;
}
// Top y-axis constraint
if (element.TranslatePoint(new Point(), container).Y <= new Point(0, 0).Y)
{
respects = false;
// Get elements' current position and adjust
xReset = element.TranslatePoint(new Point(), container).X;
yReset = element.TranslatePoint(new Point(), container).Y + 1;
TranslateTransform translate = new TranslateTransform(xReset, yReset);
element.RenderTransform = translate;
}
// Bottom y-axis constraint
if (element.TranslatePoint(new Point(), container).Y + element.RenderSize.Height >= container.RenderSize.Height)
{
respects = false;
// Get elements' current position and adjust
xReset = element.TranslatePoint(new Point(), container).X;
yReset = element.TranslatePoint(new Point(), container).Y - 1;
TranslateTransform translate = new TranslateTransform(xReset, yReset);
element.RenderTransform = translate;
}
return respects;
}
catch (Exception ex)
{
throw ex;
}
}
Edit_1: Added code from MainWindow.xaml:
<Window
Name="mainWindow"
x:Class="WPF_TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="480" Width="555">
<Grid Name="mainGrid">
<Canvas Grid.Row="0"
Grid.Column="0"
Background="AliceBlue"
Name="LeftTop"
MouseDown="onMouseDown"
MouseMove="onMouseMove"
MouseUp="onMouseUp" >
<Ellipse Fill="Blue"
Width="100"
Height="100"/>
</Canvas>
<Canvas Grid.Row="0"
Grid.Column="2"
Background="AliceBlue"
Name="RightTop"
MouseDown="onMouseDown"
MouseMove="onMouseMove"
MouseUp="onMouseUp">
<Ellipse Fill="Blue"
Width="100"
Height="100"/>
</Canvas>
<Label Grid.Row="2"
Grid.Column="0"
Name="LeftBottom">
</Label>
<Canvas Grid.Row="2"
Grid.Column="3"
Background="AliceBlue"
Name="RightBottom"
MouseDown="onMouseDown"
MouseMove="onMouseMove"
MouseUp="onMouseUp">
<Ellipse Fill="Blue"
Width="100"
Height="100"/>
</Canvas>
<Grid.RowDefinitions>
<RowDefinition Height="200" />
<RowDefinition Height="50" />
<RowDefinition Height="200" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="250" />
</Grid.ColumnDefinitions>
</Grid>
</Window>
Edit_2: So, I've discovered that when I move the top right canvas, it actually moves outside of the view (at position 600,0). Currently trying to understand why this happens.
Instead of using render transforms, might be easier to update the Canvas's Margin property:
if (cursor != MouseClickLocation)
{
// Moving on the x-axis and y-axis
xMove = cursor.X - MouseClickLocation.X;
yMove = cursor.Y - MouseClickLocation.Y;
// Actually moving the element
this.DraggedElement.Margin = this.CalculateNewPosition(this.DraggedElement, mainWindow, xMove, yMove);
}
Where CalculateNewPosition might look something like this (warning, untested):
private Thickness CalculateNewPosition(Canvas element, UIElement container, double translationX, double translationY)
{
Thickness currentPosition = element.Margin;
Thickness newPosition = new Thickness(currentPosition.Left + translationX, currentPosition.Top + translationY, 0, 0);
int containerWidth = container.ActualWidth;
int containerHeight = container.ActualHeight;
int elementWidth = element.ActualWidth;
int elementHeight = element.ActualHeight;
if (newPosition.Left < 0)
newPosition.Left = 0;
else if (newPosition.Left + elementWidth > containerWidth)
newPosition.Left = containerWidth - elementWidth;
if (newPosition.Top < 0)
newPosition.Top = 0;
else if (newPosition.Top + elementHeight > containerHeight)
newPosition.Top = containerHeight - elementHeight;
return newPosition;
}
I'm not sure why specifically your code isn't working for the other circles. Possibly it has to do with the bounds checks like:
if (element.TranslatePoint(new Point(), container).X <= new Point(0, 0).X)
if (element.TranslatePoint(new Point(), container).X + element.RenderSize.Width >= container.RenderSize.Width)
The assumption is that the new Point(0,0) and TranslatePoint are returning points relative to each containing grid cell. I'm not sure if that assumption is correct; one (or both) of the comparisons may be absolute with respect to the application or something to that effect. It would be difficult to ascertain exactly just via a cursory examination of your code; you would need to run the debugger and check your values and see where they diverge from what you expect.