Slow performance using dashed lines on Canvas - c#

I need to create a grid using Canvas with horizontal and vertical lines. The problem is in very bad performance when I'm using dashed lines instead of solid. Is there any solution to solve this? I don't need the possibility to handle events of this dashed lines (maybe exists some 'lightweight' version of canvas object...).
If I add StrokeDashArray to the Line object the application is very slow...
private void DrawGrid()
{
var brush = new SolidColorBrush((Color) ColorConverter.ConvertFromString("#cccccc"));
for (int i = 100; i < _areaSize; i += 100)
{
var hLine = new Line
{
X1 = 0,
Y1 = i,
X2 = _areaSize,
Y2 = i,
Stroke = brush,
StrokeThickness = 1,
SnapsToDevicePixels = true,
};
var vLine = new Line
{
X1 = i,
Y1 = 0,
X2 = i,
Y2 = _areaSize,
Stroke = brush,
StrokeThickness = 1,
SnapsToDevicePixels = true
};
//hLine.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
//vLine.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
Container.Children.Add(hLine);
Container.Children.Add(vLine);
Panel.SetZIndex(hLine, -1000);
Panel.SetZIndex(vLine, -1000);
}
}

Related

Creating Rectangles one under each other

I have a program that creates a specific number of rectangles according to how many I want. When I create 1 Rectangle the Height is 400 when I create 2 Rectangles then the height is 200 when I create 4 Rectangles the height is 100, I think you understand what I'm doing. Now I want to place them one under each other. I give you 3 examples.
This is how it looks like when I create 1 Rectangle
And when I want to create 2 Rectangles the height should get divided by 2. I already did that and that's working but I can't place them
like this under each other.
Only one more example if someone doesn't understand what I'm trying. This is how it should look like when I create 5 Rectangles.
I tried putting them under each other like that:
Canvas.SetLeft(MyRectangle[i], Width / 2.0 - MyRectangle[i].Width / 2.0);
Canvas.SetTop(MyRectangle[i], i * 120);
But that looks like this, it's not on top of the screen, and the other problem is that when I would want to create 2 it looks like this. So I tried around with using i from the loop but I can't figure anything out. This is my code:
Brush brush = new SolidColorBrush(Color.FromRgb((byte)_random.Next(1, 255), (byte)_random.Next(1, 255), (byte)_random.Next(1, 255)));
int howmanyrect = 3;
Rectangle[] MyRectangle = new Rectangle[howmanyrect];
if (howmanyrect == 1)
{
Rectangle OneRectangle = new Rectangle();
OneRectangle.Fill = brush;
OneRectangle.StrokeThickness = 2;
OneRectangle.Stroke = Brushes.Black;
OneRectangle.Width = 400;
OneRectangle.Height = 400;
Canvas.SetLeft(OneRectangle, Width / 2.0 - OneRectangle.Width / 2.0);
Canvas.SetTop(OneRectangle, 30);
myCanvas.Children.Add(OneRectangle);
Content = myCanvas;
}
for (int i = 1; i <= howmanyrect - 1; i++)
{
MyRectangle[i] = new Rectangle
{
Fill = brush,
StrokeThickness = 2,
Stroke = Brushes.Black,
Width = 400,
Height = 400 / howmanyrect
};
Canvas.SetLeft(MyRectangle[i], Width / 2.0 - MyRectangle[i].Width / 2.0);
Canvas.SetTop(MyRectangle[i], i * 120);
myCanvas.Children.Add(MyRectangle[i]);
Content = myCanvas;
}
I try to fix your code :
Brush brush = new SolidColorBrush(Color.FromRgb((byte)_random.Next(1, 255), (byte)_random.Next(1, 255), (byte)_random.Next(1, 255)));
int howmanyrect = 3;
Rectangle[] MyRectangle = new Rectangle[howmanyrect];
for (int i = 0; i < howmanyrect; i++)
{
var rectangle = new Rectangle
{
Fill = brush,
StrokeThickness = 2,
Stroke = Brushes.Black,
Width = 400,
Height = 400 / howmanyrect
};
MyRectangle[i] = rectangle;
Canvas.SetLeft(rectangle, Width / 2.0 - rectangle.Width / 2.0);
var distance = 10;
Canvas.SetTop(rectangle, 30 + i * (400 / howmanyrect) + distance );
myCanvas.Children.Add(rectangle);
}
Content = myCanvas;

Filling a big rectangle with a variable quantity of little rectangles

I'm trying to fill a rectangle with a variable quantity of little rectangles but adjusting the distance between them depending on the number of units (more units->the lesser the distance between).
I'm a newbie programming WPF in C# and i don´t know how to advance from this point.
How can I do it?
The code so far:
int units = 20;
int width = 10;
int height = 20;
int top = 200;
int left = 200;
int rectangleWidth = 300;
int rectangleHeight = 100;
for (int i = 0; i < units; i++)
{
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec);
Canvas.SetTop(rec, top);
Canvas.SetLeft(rec, left + (i*50));
}
I have updated the code, but doesn´t work.
I don´t know what am i doing wrong.
The piece of code so far:
int rectangleWidth = 500;
int rectangleHeight = 100;
int units = 60;
int unitsX = 10;
int unitsY = 6;
var childWidht = (rectangleWidth - 2*Left) / unitsX;
var childHeigth = (rectangleHeight - 2*Top ) / unitsY;
int width = 10;
int height = 20;
double top = 100;
double left = 100;
for (int i = 0; i < units; i++)
{
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec);
for (int j = 0; j < unitsY; j++)
{
Rectangle rec2 = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec2);
Canvas.SetTop(rec, top + (j * childHeigth));
for (int k = 0; k < unitsX; k++)
{
Rectangle rec3 = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Black,
Stroke = Brushes.White,
StrokeThickness = 1,
RadiusX = 3,
RadiusY = 3,
};
cuadernodibujo.Children.Add(rec3);
Canvas.SetLeft(rec, left + (k * childWidht));
}
}
}
If I understand correctly, you want to spread the little rectangles uniformly over the width of the parent rectangle.
This is less a programming problem, then a maths problem.
Given the parent rectangle's width parentWidht and the number of child rectangles units each child rectangle has a width of:
var childWidht = parentWidht / units;
If you want to add a left and right margin (given your left variable), you need to subtract the margin from parentWidht.
var childWidht = (parentWidht - 2 * left) / units; // 2 times left, to add the margin on both sides.
This gives you the width of each child, you now only have to move each child rectangle according to the previously calculated childWidht.
...
var childWidht = (parentWidht - 2 * left) / units;
for (int i = 0; i < units; i++)
{
...
Canvas.SetLeft(rec, left + (i*childWidht));
}
Update to question in the comments
With that I can fill a single line, but how can I fill the rest of the lines (to fill the parent height as well)?
We can apply the same logic as for the horizontal filling.
First calculate the child rectangles height (parentHeight - 2 * top)
Then wrap the horizontal rectangles into a loop and move each line according to the calculated height.
Here the listing with horizontal and vertical filling.
...
var childWidht = (parentWidht - 2 * left) / unitsX;
var childHeigth = (parentHeigth - 2 * top) / unitsY;
for (int j = 0; j < unitsY; i++) // first loop for vertical filling
{
for (int i = 0; i < unitsX; i++) // second loop for horizontal
{
var rect = new Rectangle { ... } ;
Canvas.Children.Add(rect); // Only add once in the inner loop.
Canvas.SetTop(rec, top + (j * childHeigth)); // here we use j, the vertical index
Canvas.SetLeft(rec, left + (i*childWidht)); // here we use i, the horizontal index
}
}

Printing Image from Silverlight

I'm trying to print a Image in Landscape mode in Silverlight.
I found a great example here. Where most of the code comes from. The code worked perfectly as expected. When I changed the Line to an Image it failed.
Code
Canvas OuterCanvas = new Canvas();
/* a container for everything that will print */
Border OuterBorder = new Border()
{
BorderThickness = new Thickness(3),
BorderBrush = new SolidColorBrush(Colors.Red),
Margin = new Thickness(10)
};
double Width = e.PrintableArea.Width - OuterBorder.Margin.Left - OuterBorder.Margin.Right;
double Height = e.PrintableArea.Height - OuterBorder.Margin.Top - OuterBorder.Margin.Bottom;
/* NOTE: We're trying to force landscape, so swop the width and height */
OuterBorder.Width = Height;
OuterBorder.Height = Width;
/* on portrait, this line goes down (leave the printer settings, we're trying to force landscape) */
Line Line = new Line()
{
X1 = OuterBorder.Width / 2,
Y1 = 0,
X2 = OuterBorder.Width / 2,
Y2 = OuterBorder.Height,
Stroke = new SolidColorBrush(Colors.Blue),
StrokeThickness = 3
};
//
// Here is where I changed the Line to an Image
//
OuterBorder.Child = imageElementInXaml; //Line;
OuterCanvas.Children.Add(OuterBorder);
/* rotate 90 degrees, and move into place */
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new RotateTransform() { Angle = 90 });
transformGroup.Children.Add(new TranslateTransform() { X = e.PrintableArea.Width });
OuterBorder.RenderTransform = transformGroup;
e.PageVisual = OuterCanvas;
e.HasMorePages = false;
I know that a Border can only contain 1 element in which I have done so, and when I printed the image on its own without trying to make it landscape this worked too. So why wont it work when I simply replace the Line with the image Element
So since posting this I found some code (cant remember where now) that has helped me get the printing working. Its not as clean as I would have liked but it works.
void pd_PrintPage(object sender, PrintPageEventArgs e)
{
Image image = new Image();
image.Source = imgPlayer.Source;
//This is important
image.Stretch = Stretch.Uniform;
// Find the full size of the page
Size pageSize = new Size(e.PrintableArea.Width + e.PageMargins.Left + e.PageMargins.Right, e.PrintableArea.Height + e.PageMargins.Top + e.PageMargins.Bottom);
var MARGIN= 10;
// Get additional margins to bring the total to MARGIN (= 96)
Thickness additionalMargin = new Thickness
{
Left = Math.Max(0, MARGIN - e.PageMargins.Left),
Top = Math.Max(0, MARGIN - e.PageMargins.Top),
Right = Math.Max(0, MARGIN - e.PageMargins.Right),
Bottom = Math.Max(0, MARGIN - e.PageMargins.Bottom)
};
// Find the area for display purposes
Size displayArea = new Size(e.PrintableArea.Width - additionalMargin.Left - additionalMargin.Right, e.PrintableArea.Height - additionalMargin.Top - additionalMargin.Bottom);
bool pageIsLandscape = displayArea.Width > displayArea.Height;
bool imageIsLandscape = image.ActualWidth > image.ActualHeight;
double displayAspectRatio = displayArea.Width / displayArea.Height;
double imageAspectRatio = (double)image.ActualWidth / image.ActualHeight;
double scaleX = Math.Min(1, imageAspectRatio / displayAspectRatio);
double scaleY = Math.Min(1, displayAspectRatio / imageAspectRatio);
// Calculate the transform matrix
MatrixTransform transform = new MatrixTransform();
if (pageIsLandscape == imageIsLandscape)
{
// Pure scaling
transform.Matrix = new Matrix(scaleX, 0, 0, scaleY, 0, 0);
}
else
{
// Scaling with rotation
scaleX *= pageIsLandscape ? displayAspectRatio : 1 / displayAspectRatio;
scaleY *= pageIsLandscape ? displayAspectRatio : 1 / displayAspectRatio;
transform.Matrix = new Matrix(0, scaleX, -scaleY, 0, 0, 0);
}
Image image2 = new Image
{
Source = image.Source,
Stretch = Stretch.Fill,
Width = displayArea.Width,
Height = displayArea.Height,
RenderTransform = transform,
RenderTransformOrigin = new Point(0.5, 0.5),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = additionalMargin,
};
Border border = new Border
{
Child = image,
};
e.PageVisual = border;
}

How to do c# collision detection?

Are there any predefined methods in c# which allow for collision detection?
I am new to c# and am trying to get collision detection of two ellipses are there any predefined ways collision detection can be implemented?
I already have code which draws the ellipses, what would be a good way to start the collision detection?
private void timer1_Tick(object sender, EventArgs e)
{
//Remove the previous ellipse from the paint canvas.
canvas1.Children.Remove(ellipse);
if (--loopCounter == 0)
timer.Stop();
//Add the ellipse to the canvas
ellipse = CreateAnEllipse(20, 20);
canvas1.Children.Add(ellipse);
Canvas.SetLeft(ellipse, rand.Next(0, 500));
Canvas.SetTop(ellipse, rand.Next(0, 310));
}
// Customize your ellipse in this method
public Ellipse CreateAnEllipse(int height, int width)
{
SolidColorBrush fillBrush = new SolidColorBrush() { Color = Colors.Yellow};
SolidColorBrush borderBrush = new SolidColorBrush() { Color = Colors.Black };
return new Ellipse()
{
Height = height,
Width = width,
StrokeThickness = 1,
Stroke = borderBrush,
Fill = fillBrush
};
}
this is the code to draw an ellipse which then gets removed and appears in another position.
I have tested this, it worked, at least for me
var x1 = Canvas.GetLeft(e1);
var y1 = Canvas.GetTop(e1);
Rect r1 = new Rect(x1, y1, e1.ActualWidth, e1.ActualHeight);
var x2 = Canvas.GetLeft(e2);
var y2 = Canvas.GetTop(e2);
Rect r2 = new Rect(x2, y2, e2.ActualWidth, e2.ActualHeight);
if (r1.IntersectsWith(r2))
MessageBox.Show("Intersected!");
else
MessageBox.Show("Non-Intersected!");
Would something like the following work?
var ellipse1Geom = ellipse1.RenderedGeometry;
var ellipse2Geom = ellipse2.RenderedGeometry;
var detail = ellipse1Geom.FillContainsWithDetail(ellipse2Geom);
if(detail != IntersectionDetail.Empty)
{
// We have an intersection or one contained inside the other
}
The Geometry.FillContainsWithDetail(Geometry) method is defined as
Returns a value that describes the intersection between the current geometry and the specified geometry.
I think you should definitely give a look at the XNA framework, it has loads of method to do collision detection.
Check out this other link on how to implement it manually in c# it might be helpful.
Provided that your Ellipses are always circles (i.e. their Width and Height properties are set to the same value) and they always have the Canvas.Left and Canvas.Top properties set, the following helper method checks for a collision:
public static bool CheckCollision(Ellipse e1, Ellipse e2)
{
var r1 = e1.ActualWidth / 2;
var x1 = Canvas.GetLeft(e1) + r1;
var y1 = Canvas.GetTop(e1) + r1;
var r2 = e2.ActualWidth / 2;
var x2 = Canvas.GetLeft(e2) + r2;
var y2 = Canvas.GetTop(e2) + r2;
var d = new Vector(x2 - x1, y2 - y1);
return d.Length <= r1 + r2;
}

How to display only 2 lines when a mousemove event occurs on a canvas

I'm trying to make two lines that follows the mouse every time the MouseMove event occurs. I can create the two lines for the event, but the problem is that they will create two lines for every MouseMove. Here's my code. I'm using WPF and C#.
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
double xPos = e.GetPosition(m_Grid).X;
double yPos = e.GetPosition(m_Grid).Y;
Line vertLine = new Line();
Line horzLine = new Line();
vertLine.X1 = xPos;
vertLine.Y1 = 0;
vertLine.X2 = xPos;
vertLine.Y2 = m_Grid.Height;
horzLine.X1 = 0;
horzLine.Y1 = yPos;
horzLine.X2 = m_Grid.Width;
horzLine.Y2 = yPos;
vertLine.StrokeThickness = 1;
horzLine.StrokeThickness = 1;
vertLine.Stroke = Brushes.Black;
horzLine.Stroke = Brushes.Black;
m_Grid.Children.Add(vertLine); //m_Grid is my Canvas
m_Grid.Children.Add(horzLine);
m_Grid.UpdateLayout();
}
Just Clear the Grid.Children every time (also don't call UpdateLayout, it's none of your business).
It would be best to find the existing lines and then remove them, this way if you have anything else in your canvas you won't be removing it, only these specific lines.
You can do this by giving them a name and then searching for them with that name.
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
double xPos = e.GetPosition(m_Grid).X;
double yPos = e.GetPosition(m_Grid).Y;
Line vertLine = new Line
{
Name = "vertLine",
X1 = xPos,
Y1 = 0,
X2 = xPos,
Y2 = m_Grid.Height,
Stroke = Brushes.Black,
StrokeThickness = 1
};
Line horzLine = new Line
{
Name = "horzLine",
X1 = 0,
Y1 = yPos,
X2 = m_Grid.Width,
Y2 = yPos,
StrokeThickness = 1,
Stroke = Brushes.Black
};
m_Grid.Children.Remove((Line) m_Grid.Children.OfType<Line>().FirstOrDefault(x => x.Name == "vertLine"));
m_Grid.Children.Remove((Line) m_Grid.Children.OfType<Line>().FirstOrDefault(x => x.Name == "horzLine"));
m_Grid.Children.Add(vertLine); //m_Grid is my Canvas
m_Grid.Children.Add(horzLine);
m_Grid.UpdateLayout();
}

Categories