WPF C# Drawing line by using coordinates of buttons - c#

I have 2 buttons created in code behind ,I am trying to draw a line that connects them by getting the button coordinates, but it doesn't work the way that I wanted , I am new to this , am I doing this the right way?
I searched internet for line connecting 2 controls, but I just want a simple one that make use of coordinates to connect them .
Here are my codes :
Button btn1 = new Button();
btn1.Content = "Hello";
btn1.Width = 150;
btn1.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
wrapPanel1.Children.Add(btn1);
btn1.PreviewMouseDown += new MouseButtonEventHandler(btn1_MouseDown);
Button btn2 = new Button();
btn2.Content = "World";
btn2.Width = 150;
btn2.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
btn2.PreviewMouseDown += new MouseButtonEventHandler(btn1_MouseDown);
wrapPanel1.Children.Add(btn2);
private void ShowLocation(ContentControl element)
{
var location = element.PointToScreen(new Point(0, 0));
MessageBox.Show(string.Format(
"{2}'s location is ({0}, {1})",
location.X,
location.Y,
element.Content));
Line redLine = new Line();
redLine.X1 = location.X;
redLine.Y1 = location.Y;
redLine.X2 = 323;
redLine.Y2 = 167;
//Create a red Brush
SolidColorBrush redBrush = new SolidColorBrush();
redBrush.Color = Colors.Red;
//Set Line's width and color
redLine.StrokeThickness = 4;
redLine.Stroke = redBrush;
// Add line to the Grid.
wrapPanel1.Children.Add(redLine);
}
private void btn1_MouseDown(object sender, RoutedEventArgs e)
{
var element = sender as ContentControl;
if (element != null)
{
ShowLocation(element);
}
}
As you notice the X2 and Y2 coordinates I just random giving it a number but the X1 and Y1 is suppose to be drawn from the button but it is not.
The line is appearing somewhere else away from the button, but the coords I use are the coords of the button.
Also, I have to dynamically create many buttons from db, This is just testing out yet I am stuck here.
__EDIT____
trying out with 1 button , i realise when maximize and minimize , the coords are different , this is how it looks like , the button seems to increase its height everytime i click on it to generate the line .
i want the line to start from the end of the button width at the side , if its not possible , it will be good for the button to overlap the line but is that possible?

The main problem is that WPF mostly uses layout-based positioning (Grid, DockPanel, etc), instead of Windows Forms, which is coordinate-based. This means, that real location of left top element's corner depends from layout control being used and its current settings (and yes, this usually blows up your mind, if you are very new to WPF).
If you want to manage position of elements by coordinates in WPF, then you should use Canvas (you can easily translate this sample from XAML to C#):
<Canvas>
<Button Canvas.Left="10" Canvas.Top="10" Width="20" Height="20" Content="A"/>
<Button Canvas.Left="50" Canvas.Top="50" Width="20" Height="20" Content="B"/>
<Line Canvas.Left="30" Canvas.Top="30" X2="20" Y2="20" StrokeThickness="4" Stroke="Red"/>
</Canvas>

Related

UWP: Get exact position of mouse (pointer) click inside image

I have a ContentDialog where I have an image and another image overlayed to place a watermark on it:
<StackPanel Orientation="Vertical">
<Grid>
<Image x:Name="ImageOriginal" Source="{x:Bind OriginalImage, Mode=OneWay}" MinHeight="120" MinWidth="160"/>
<Image x:Name="ImageOverlay" Source="{x:Bind OverlayImage, Mode=OneWay}" MinHeight="120" MinWidth="160" PointerPressed="ImageOverlay_PointerPressed" />
</Grid>
...
So the goal was, to place a text (centered horizontally and vertically) on the clicked position.
private void ImageOverlay_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
e.Handled = true;
var point = e.GetCurrentPoint(ImageOverlay);
var pointPosition = new Windows.Foundation.Point((int)point.Position.X, (int)point.Position.Y);
Position = new PointF((float)pointPosition.X, (float)pointPosition.Y);
UpdateOverlayImage();
}
(PointF) is a floating point point of ImageSharp. With the code above my text placement is always a bit off, like 50 pixels in each direction.
I also tried it with the following code, but this just makes the text placed wrongly in the opposite direction:
GeneralTransform transform = ImageOverlay.TransformToVisual(Window.Current.Content);
Windows.Foundation.Point coordinatePointToWindow = transform.TransformPoint(pointPosition);
Update:
Someone wanted to know, how I draw the text,s o here it is, this code is inside UpdateOverlayImage():
var watermarkedImage = OverlayImageBase.Clone(ctx => ctx.ApplyWatermark(font, tbWatermarkText.Text, color, Position));
OverlayImage = watermarkedImage.ToBitmap();
And the ApplyWatermark extension method looks like this:
public static IImageProcessingContext ApplyWatermark(this IImageProcessingContext processingContext,
Font font,
string text,
Color color,
PointF position)
{
var textGraphicOptions = new TextGraphicsOptions(true)
{
ColorBlendingMode = SixLabors.ImageSharp.PixelFormats.PixelColorBlendingMode.Normal,
HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment.Center,
VerticalAlignment = SixLabors.Fonts.VerticalAlignment.Center,
};
return processingContext.DrawText(textGraphicOptions, text, font, color, position);
}
Any ideas why my coordinates are off?
I found out what was wrong. As the displayed image inside the ContentDialog is scaled, the coordinates of the drawn text cannot be directly the same coordintes as received from the pointer pressed event (of course).
So we have to calculate the relative to the size of the scaling. This is how I could solve it:
private void ImageOverlay_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
e.Handled = true;
PointerPoint ptrPt = e.GetCurrentPoint(ImageOverlay);
// As the image is scaled we have to caluclate the position inside the image based on the position of the pointer pressed
float imagePressedX = (float)(OriginalImage.PixelWidth / ImageOverlay.ActualWidth * ptrPt.Position.X);
float imagePressedY = (float)(OriginalImage.PixelHeight / ImageOverlay.ActualHeight * ptrPt.Position.Y); ;
Position = new PointF(imagePressedX, imagePressedY);
UpdateOverlayImage();
}

How can you outline an ellipse without using stroke property?

What I really want is a way to have a negative stroke Thickness value on a WPF shape such as an ellipse, so that the stoke outline paints outwards towards LEFT and TOP of Shape, rather than inside of the shape, over writing my text when I make the thinkness of the stroke too thick... I want the radius of my ellipse to stay constant, but the stroke to grow outwards with increased thinkness, and the LEFT, TOP placement of the shape to remain contant with the inner fill staying the same size and not getting covered up by stroke as it is increased in size.
I tried DropShadowEffect, but its kind of too blurry and not well defined enough...and looks kind of messy... really I just want a solid line going around the outside of the shape...
As you can see from attached picture above, I tried to put shadow around two the ellipses using this code below. the problem is that I want it to be a solid color around the outside like a scaletransform of another ellipse of a different color.
var e = new Ellipse();
DropShadowEffect effect = new DropShadowEffect();
effect.Color =Colors.Orange;
effect.Direction = 0;
effect.BlurRadius = 30;
effect.ShadowDepth = 4;
effect.Opacity=0;
e.Effect = effect;
t.Text = string.Format("abc");
t.Measure(new Size(gwin.XStep, gwin.YStep));
t.Arrange(new Rect(t.DesiredSize));
e.StrokeThickness = 2;
e.Stroke = Brushes.Black;
canvas.Children.Add(e);
canvas.Children.Add(t);
Another possible direction towards solving the problem:
<Ellipse RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
Convert to c# code and place one scaletransform ellipse centered inside another scaled transform ellipse of different colors... not sure how to set it up though.
Solution:
Based on suggestion below. I tried creating a grid, setting the width and height of the grid to the size of my ellipse, then adding two ellipses to the grid with different colors and one with a margin set to -10. and it works perfectly ... just need to place the larger ellipse with margin -10 behind the other ellipse when adding it to the grid...here's what it looks like now..
Solution is in here somewhere:
g = new Grid();
e = new Ellipse();
h = new Ellipse();
t = new TextBlock();
t.HorizontalAlignment = HorizontalAlignment.Center;
t.VerticalAlignment = VerticalAlignment.Center;
t.FontWeight = FontWeights.ExtraBold;
g.Children.Add(h);
g.Children.Add(e);
g.Children.Add(t);
gwin.canvas.Children.Add(g);
t.Text = String.Format("{0}.{1}", x, y);
g.Width = gwin.XStep;
g.Height = gwin.YStep;
Canvas.SetLeft (g, gwin.X1 + gwin.XStep*x*2);
Canvas.SetTop (g, gwin.Y1 + gwin.YStep*y*2);
e.StrokeThickness = 2;
e.Stroke = Brushes.Black;
h.Margin = new Thickness(-10);
You can use double ellipses inside a grid overlaying each other like this:
<Grid Width="100" Height="100">
<Ellipse Fill="Black" Margin="-10"/>
<Ellipse Fill="Red" />
</Grid>
The size of this compound is still 100x100 even though the first ellipse is bigger and rendered out of its boundaries.
You may also use a Path and then do this
I think there is something like border. Or you can draw one elipse and then a second one in smaller that has the background color.

PictureBox background ignoring WPF UserControl

I want to create a Checkers Board with animation on tiles.
In order to do so I created a rectangle WPF UserControl to represent the tiles.
on top of the tiles I'm using a PictureBox with a picture of red/black piece.
when creating and adding the tiles and after that the PictureBoxes I can't see the PictureBoxes because they are behind the tiles.
(if I reverse the order of the creation and adding of the elements I see the pictures above the tiles but the transparent background of the PictureBoxes shows me the form background like it ignores the UserControl beneath it.
for better explanation I attached a photo (I set the form's background to green for better understanding)
also here are the code parts I think are relevant
the WPF UserControl (named UserControlCell) Xaml
<Grid>
<Rectangle Fill="White" HorizontalAlignment="Left" Height="50" Width="50"
VerticalAlignment="Top" Name="myRect"/>
</Grid>
Each tile is treated that way
ElementHost eh = new ElementHost();
UserControlCell square = new UserControlCell ;
square.Size = new Size(someWidth, someHeight);
eh.Size = new Size(someWidth, someHeight);
eh.Location = new System.Drawing.Point(xCoordinate,yCoordinate);
if ((i + j) % 2 == 0)
square.BackColor = System.Drawing.Color.Black;
else
square.BackColor = System.Drawing.Color.White;
eh.BackColor = Color.Transparent;
eh.Child = square;
Controls.Add(eh);
and for each PictureBox
PictureBox pb = new PictureBox();
pb.Image = relevantImage;
pb.Size = new Size(someWidth, someHeight);
pb.Location = new System.Drawing.Point(xCoordinate,yCoordinate);
pb.TabStop = false;
pb.Visible = true;
pb.BackColor = Color.Transparent;
pb.Refresh();
Controls.Add(pb);
what could be the problem that causes the picture box to think that it's background is the form instead of the tiles (or why does adding them in opposite order making me not seeing the PictureBoxes at all)

WPF Storyboard: DoubleAnimation Works On Opacity But Not TranslateTransform?

To learn about animation and UI, I'm making a basic WPF/C# application where a user selects the number of vehicles to display then those vehicles (ie images of different vehicles) appear in the canvas and move around.
The WPF is very simple:
<Grid>
<Canvas x:Name="MenuTabCanvas" Visibility="Visible">
<Label x:Name="AnimateDemo" Content="Animate!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="104" Background="#25A0DA" Foreground="White" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Cursor="Hand" MouseDown="AnimateGrid" Canvas.Left="640" Canvas.Top="87"/>
<Canvas x:Name="AnimationCanvas" Canvas.Top="150" Canvas.Left="224" Width="814" Height="489">
</Canvas>
</Grid>
I started with a fade animation using the method described in this post and it works fine. Then I tried the translatetransform as shown below leaving the fade code in comments, and the images appear but nothing moves:
private void AnimateGrid(object sender, EventArgs e)
{
int NumberOfVehicles = 5;
var sb = new Storyboard();
for (int i = 0; i < NumberOfVehicles; i++)
{
//create & add the images to our target canvas
Image Img = getRandomVehicleImage(); //returns image of vehicle
AnimationCanvas.Children.Add(Img);
Canvas.SetTop(Img, 30 + 60 * i); //position image w/in canvas
Canvas.SetLeft(Img, 30 + 80 * i);
//add an animation
DoubleAnimation myAnimation = new DoubleAnimation()
{
// From = 0,
To = 150,
Duration = TimeSpan.FromSeconds(2),
};
Storyboard.SetTarget(myAnimation, Img);
// Storyboard.SetTargetProperty(myAnimation, new PropertyPath(Button.OpacityProperty));
Storyboard.SetTargetProperty(myAnimation, new PropertyPath(TranslateTransform.XProperty));
sb.Children.Add(myAnimation);
}
sb.Begin();
}
I have been able to get it to work with TranslateTransform.BeginAnimation but I would prefer to use the storyboard here.
Why does the translate transform behave differently than the opacity animation and what do I need to do to get it to function as intended?
By default, there is no TranslateTransform applied to a UIElement. So if you want to move an Image, you first have to set its RenderTransform property to a TranslateTransform, and then set the TargetProperty to the correct property path:
Img.RenderTransform = new TranslateTransform();
...
Storyboard.SetTargetProperty(myAnimation, new PropertyPath("RenderTransform.X"));
Alternatively, you could directly animate the TranslateTransform, even without a Storyboard:
var transform = new TranslateTransform();
Img.RenderTransform = transform;
transform.BeginAnimation(TranslateTransform.XProperty, myAnimation);

C# canvas going overpainted

I have a Canvas in my MainWindow and I draw a line there. When it draws over the width/height of my Canvas, the drawing continues in my MainWindow. Is there a mistake in my code or is that normal?
<Canvas x:Name="coordinateSystem" HorizontalAlignment="Right" Height="580" Margin="0,10,283,0" VerticalAlignment="Top" Width="1024" Cursor="Cross" UseLayoutRounding="False"/>
Here is my function I call everytime when I get a new coordinate for my line:
// xOld, yOld and t are static
// t represents the time
private void drawPoly(double value)
{
t++;
Point pOne = new Point(xOld, yOld);
Point pTwo = new Point(t, value);
GeometryGroup lineGroup = new GeometryGroup();
LineGeometry connectorGeometry = new LineGeometry();
connectorGeometry.StartPoint = pOne;
connectorGeometry.EndPoint = pTwo;
lineGroup.Children.Add(connectorGeometry);
System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
path.Data = lineGroup;
path.StrokeThickness = 1;
path.Stroke = path.Fill = Brushes.Red;
coordinateSystem.Children.Add(path);
xOld = t;
yOld = value;
}
thx
PS: Is there a way to save all drawn points? I want later resize my canvas (zoom out/zoom in) or if the time going to big move my painted line in my canvas and then I need to draw all points again.
Canvases do not clip child elements. If you want to stop the child elements from being drawn outside of the Canvas, you'll need to set ClipToBounds to true or set the Clip of the Canvas.

Categories

Resources