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;
}
Related
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;
I have an ellipse in the middle of a Canvas using this code:
var FixedCircle = new Ellipse
{
Width = 25,
Height = 25,
Stroke = color,
Fill = color,
StrokeThickness = 3
};
var centerX = ActualWidth / 2;
var centerY = ActualHeight / 2;
FixedCircle.Margin = new Thickness(centerX, centerY, 1, 1);
Children.Add(FixedCircle);
InvalidateVisual();
I want to animate the ellipse, making it go from left and back to center (starting middle point).
I am setting coordinates (centerX,centerY) and (0,centerY) as starting and end points, also I am using the lines
Point oldPoint = FixedCircle.TransformToAncestor(this).Transform(new Point(centerX,centerY));
Point newPoint = FixedCircle.TransformToAncestor(this).Transform(new Point(0,centerY));
to indicate that transform must be set to starting pointg, I tried deleting this but the movement gets worse and starts from botton, or coordinate (0,0) is seen as the middle instead of (ActualWidth / 2, ActualHeight / 2) which is something I dont want.
TranslateTransform trans = new TranslateTransform();
FixedCircle.RenderTransform = trans;
Point oldPoint = FixedCircle.TransformToAncestor(this).Transform(new Point(centerX,centerY));
Point newPoint = FixedCircle.TransformToAncestor(this).Transform(new Point(0,centerY));
var EndX = FixedCircle.Width / 2 + newPoint.X - oldPoint.X - (FixedCircle.Width);
var EndY = FixedCircle.Height / 2 + newPoint.Y - oldPoint.Y - (FixedCircle.Height);
var a1X = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(3));
a1X.Completed += (s, e) =>
{
oldPoint = FixedCircle.TransformToAncestor(this).Transform(new Point(0, centerY));
newPoint = FixedCircle.TransformToAncestor(this).Transform(new Point(centerX, centerY));
EndX = FixedCircle.Width / 2 + newPoint.X - oldPoint.X - (FixedCircle.Width);
EndY = FixedCircle.Height / 2 + newPoint.Y - oldPoint.Y - (FixedCircle.Height);
var a2X = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(3));
trans.BeginAnimation(TranslateTransform.XProperty, a2X);
};
trans.BeginAnimation(TranslateTransform.XProperty, a1X);
The effect I get is ellipse correctly go from middle to left
then the issue of my problem, it starts in the middle and go to the right, when I need the ellipse to go from left to middle, not middle to right:
What am I missing?
The following code runs a repeated animation from middle to left and back. It uses a Path with an EllipseGeometry, because that is centered (instead of top/left aligned like an Ellipse). It also draws no stroke, but just adds the stroke thickness to the ellipse's radius.
var transform = new TranslateTransform(canvas.ActualWidth / 2, canvas.ActualHeight / 2);
var radius = 14;
var circle = new Path
{
Data = new EllipseGeometry(new Point(), radius, radius),
Fill = Brushes.White,
RenderTransform = transform
};
canvas.Children.Add(circle);
var animation = new DoubleAnimation
{
To = radius,
Duration = TimeSpan.FromSeconds(3),
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever
};
transform.BeginAnimation(TranslateTransform.XProperty, animation);
For a repeated animation from right to left and back that starts at the center point, use a negative BeginTime:
var transform = new TranslateTransform(
canvas.ActualWidth - radius, canvas.ActualHeight / 2);
var animation = new DoubleAnimation
{
To = radius,
Duration = TimeSpan.FromSeconds(6),
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever,
BeginTime = TimeSpan.FromSeconds(-3)
};
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);
}
}
I would like to dynamically add a Triangle to my canvas. I can do this but only in one very specific way that does not work for my application. What I would like to do is supply a point and a size and get back a triangle.
var poly = shape as Polygon;
Polygon p = new Polygon
{
//Width = size,
//Height = size,
Fill = new SolidColorBrush(Colors.Red),
ManipulationMode = ManipulationModes.All,
RenderTransform = new CompositeTransform()
};
int w = 200;
int h = 200;
Point start = new Point(400, 200);
var right = new Point(start.X + w, start.Y);
var top = new Point(start.X + (w / 2), start.Y - (h));
poly.Points.Add(point);
poly.Points.Add(right);
poly.Points.Add(top);
poly.Points.Add(point);
Then I add the shape to my canvas child controls and and set the X Y on the shapes RenderTransform. Nothing appears. If however I just do this:
Polygon p = new Polygon
{
//Width = size,
//Height = size,
Fill = new SolidColorBrush(Colors.Red),
ManipulationMode = ManipulationModes.All,
RenderTransform = new CompositeTransform()
};
p.Points.Add(new Point(300, 200));
p.Points.Add(new Point(400, 125));
p.Points.Add(new Point(400, 275));
p.Points.Add(new Point(300, 200));
It renders a Triangle just fine. However if you supply a width and height to the above code it will stop rendering.
Can I just create a triangle of a certain size, without having to set actual points on the canvas at first, much like you do an Ellipse or a Rectangle?
I've got the drag'n'drop image, which is located in ContentPanel. In MouseLeave event i get the position of image and then i need to align it to my grid. Tried to do it with Canvas, but nothing happens. When I try to do it changing the margin, the position is totally wrong.
Code:
private void MouseLeave(object sender, MouseEventArgs e)
{
Image rs = (Image)sender;
GeneralTransform gt = rs.TransformToVisual(ContentPanel);
Point offset = gt.Transform(new Point(0, 0));
double controlTop = offset.Y;
double controlLeft = offset.X;
tb.Text = Convert.ToInt16(controlLeft / 40).ToString();
tb2.Text = Convert.ToInt16(controlTop / 40).ToString();
double newLeft = Convert.ToInt16(controlLeft / 40)*40;
double newTop = Convert.ToInt16(controlTop / 40)*40;
//rs.Margin = new Thickness(newLeft, newTop, 0, 0);
//((Image)ContentPanel.Children[11]).SetValue(Canvas.LeftProperty, newLeft);
//((Image)ContentPanel.Children[11]).SetValue(Canvas.TopProperty, newTop);
}
So how can I set the position of image?
One way could be to apply a translate transformation to your image?
Something like that should work:
Image rs = (Image)sender;
GeneralTransform gt = rs.TransformToVisual(ContentPanel);
Point offset = gt.Transform(new Point(0, 0));
//create a translate transform
TranslateTransform tt = new TranslateTransform();
//apply the required offset
tt.X = offset.X;
tt.Y = offset.Y;
//apply the transform to the image
rs.RenderTransform = tt;
double controlTop = offset.Y;
double controlLeft = offset.X;
tb.Text = Convert.ToInt16(controlLeft / 40).ToString();
tb2.Text = Convert.ToInt16(controlTop / 40).ToString();
double newLeft = Convert.ToInt16(controlLeft / 40) * 40;
double newTop = Convert.ToInt16(controlTop / 40) * 40;