I am attempting to print a WPF control 4" tall by 3" wide.
I have used a ScaleTransform on the control (a Canvas) to scale it accordingly; however, when I print to a printer part of the image is cut off (the top and left edges).
According to this thread:
The reason of this problem is that the printer provides an unprinted margin around the edge of the paper, but the PrintDialog.PrintVisual method intends to print to the edge of the paper. So the area that lies in the unprinted margin around the edge of the paper is clipped.
The thread fails to mention how to retrieve the margins or how to force the printer to ignore these margins. How do I obtain these values so that I can print using WPF without clipping?
You'll need to combine information from the PrintDocumentImageableArea with the Measure and Arrange members on your UIElement:
// I could not find another way to change the margins other than the dialog
var result = printDialog.ShowDialog();
if (result.HasValue && result.Value)
{
var queue = printDialog.PrintQueue;
// Contains extents and offsets
var area = queue.GetPrintCapabilities(printDialog.PrintTicket)
.PageImageableArea;
// scale = area.ExtentWidth and area.ExtentHeight and your UIElement's bounds
// margin = area.OriginWidth and area.OriginHeight
// 1. Use the scale in your ScaleTransform
// 2. Use the margin and extent information to Measure and Arrange
// 3. Print the visual
}
Related
I'm creating a custom installer and working on the progress bar. I have a timer function that increments the specified rectangle's Height property by +1.
statusBar->Height += 1;
if (statusBar->Height >= 285)
{
StatusBarTimer->Stop();
}
However, the origin point of the shape seems to be located in the top left of the shape, and therefore the Height acts opposite of its controls. I.E. the rectangle extends it's height the opposite direction of where I would like to.
customInstaller
(See arrowed object)
Is there a work-around for this, or better yet, is there a way to change the origin point of an image?
All common modern GUI toolkits position their controls top-down and left-right. The position you specify will be the top-left corner of the control, the size will extend downward and to the right.
If you want a control to grow upward, adjust the top and the height at the same time. Or use an existing progress bar control that supports being vertical.
When I have a group of fabric IText objects and I change all their font sizes together, the grouped text objects all shrink or grow according to the font size, but the group controls and border stay in place rather than also changing in size. My question is, how do I get the group controls to auto-size after changing the objects inside the group?
Looking through the code for Fabric, I can't figure out how the boundaries get set in the first place on the group. I have tried calling _setCoords() and setCoords() on the group, but that moves the group to 0, 0 on the canvas and keeps the height and width the same. Calling _setCoords(true) has no effect.
I have also tried the following code in which I attempt to replace the old group with a new one containing the same objects, which would theoretically draw new boundaries, but it seems like a clunky solution. Also, it doesn't work: the new group boundaries and controls do not appear.
var selected = [];
var all = scope.canvas.getObjects();
for(var x in all) {
if(all.active) {
selected[selected.length] = x;
}
}
scope.canvas._discardActiveGroup();
var objects = [];
for(var x in selected) {
objects[x] = scope.canvas.getObjects()[x];
}
var group = new fabric.Group(objects,{
originX: 'center',
originY: 'center',
canvas: scope.canvas
});
//Using $timeout so that this will wait until current $scope digest is finished
$timeout(function () {
scope.canvas.setActiveGroup(group);
scope.canvas.renderAll();
}, 0);
scope.canvas.renderAll();
EDIT:
I think this is related to another problem where after the text object changes size due to font size change, its selection area remains where it was before the size change. That is, it selects the object if you click within the pre-resize bounding rectangle, regardless of the post-resize bounding rectangle. Only after selecting some other object or deselecting all objects does selection work as expected for the object. I call setCoords() for the objects, so their boundaries are drawn correctly.
There were two parts to this problem, as I said in my edit to the question. Solving my problem required both of the solutions below.
Bug #1: IText object's selection area remains as it was before size change even if you use setCoords(). This is because setCoords() "sets corner position coordinates based on current angle, width and height" -- but it does not update width and height based on changes to the text. That only occurs during the method _renderViaNative(ctx):
//inside _renderViaNative:
this.width = this._getTextWidth(ctx, textLines);
this.height = this._getTextHeight(ctx, textLines);
Solution: Because I am actually using a custom descending class of IText, I used an override of the setCoords function to fix it. Something like this override should really be part of the IText class:
/** #Override */
setCoords : function () {
var ctx = this.canvas.getContext();
var textLines = this.text.split(this._reNewline);
this._setTextStyles(ctx);
this.width = this._getTextWidth(ctx, textLines);
this.height = this._getTextHeight(ctx, textLines);
this.callSuper('setCoords');
},
Bug #2: The group's selection area does not refresh after the IText object(s) that it contains change size, even if you call setCoords. If you call _calcBounds with a false parameter, it sets the group's center point by calculating the objects' center point -- but because the objects have been reset to have coordinates relative to the group center as (0,0) for them, the recalculation comes up with (0,0) as the center, and therefore, the whole group moves so that its center is at (0,0) on the canvas. If you call _calcBounds with a true parameter, it resizes around its original center, rather than around the new center, and you can't manually reset the center because that will make the objects move, too.
Solution: Recalculate the boundaries from scratch by resetting the objects' coordinates. I did this mainly by cannibalizing the function addWithUpdate(object).
// set the objects to their not-in-group coordinates
activeGroup.destroy();
// since _restoreObjectsState (inside destroy) set objects inactive, set them active
activeGroup.forEachObject(activeGroup._setObjectActive, activeGroup);
// now we can call _calcBounds without bouncing the group to 0,0
activeGroup._calcBounds();
// set the objects to their in-group coordinates
activeGroup._updateObjectsCoords();
I have a Grid (container) wich in turn has several grids(subContainers) arranged by rows. Each one of those "subContainers" has diferent columns and controls. And each of those "subContainers" has the horizontal alignment set to stretch, and it has to stay that way, since the layout this viewer depends on it.
I use the "container" to set each control on it's adequate position. So far so good. Now comes my headache... I want to remove the control from the grid and put it in a canvas, at the same exact position, only, the position it returns is as if the control is set to the beggining of the grid and not it's true position.
For testing purposes, I've set the "subContainters" horizontal alignment to center and (despite the layout is totally wrong) every control is in it's right position when sent to a canvas, wich it doesn't happen when HA = stretch.
Here's the code I'm using to get position:
GeneralTransform gt = nc.TransformToVisual(gridZoom);
Point offset = gt.Transform(new Point());
So you can understand, for example, my first control should be somewhere like (80, 1090), but the point that I get is (3,3).
Can anyone help me? Thanks
You should try using the top level container (which contains all other subcontainers) to get the offset point.
Also you can try:
var transform = mySubSubSubElement.TransformToVisual(Application.Current.RootVisual);
var position = transform.Transform(new Point(0, 0));
A control reports its display rectangle in .DisplayRectangle -- what is the simplest way of finding the area available for the control if .Padding is set?
For example, a Label has .DisplayRectangle always equal to .ClientRectangle, even if .Padding is not (0,0,0,0). I want to obtain the size of the rectangle in .ClientRectangle after paying attention to the .Padding setting (and whatever additional properties might limit the "net" display area, for that matter).
Isn't there some method, or other framework-supported way to do it? Do I have to re-code all those rectangle calculations that probably are already in the framework code?
Padding isn't simply a straight modification to Display Rectangle. Some built-in controls factor it in, some do not.
Best is to read the documentation for Padding and see which parts apply to you.
The hopefully on-target simple answer is "No, there's no framework method. Build your own like below"
private Rectangle GetPaddedRectangle(Control control)
{
var rect = control.ClientRectangle;
var pad = control.Padding;
return new Rectangle( rect.X + pad.Left,
rect.Y + pad.Top,
rect.Width - (pad.Left+pad.Right),
rect.Height - (pad.Top+pad.Bottom));
}
I'm trying to do simple drawing in a subclass of a decorator, similar to what they're doing here...
How can I draw a border with squared corners in wpf?
...except with a single-pixel border thickness instead of the two they're using there. However, no matter what I do, WPF decides it needs to do its 'smoothing' (e.g. instead of rendering a single-pixel line, it renders a two-pixel line with each 'half' about 50% of the opacity.) In other words, it's trying to anti-alias the drawing. I do not want anti-aliased drawing. I want to say if I draw a line from 0,0 to 10,0 that I get a single-pixel-wide line that's exactly 10 pixels long without smoothing.
Now I know WPF does that, but I thought that's specifically why they introduced SnapsToDevicePixels and UseLayoutRounding, both of which I've set to 'True' in the XAML. I'm also making sure that the numbers I'm using are actual integers and not fractional numbers, but still I'm not getting the nice, crisp, one-pixel-wide lines I'm hoping for.
Help!!!
Mark
Aaaaah.... got it! WPF considers a line from 0,0 to 10,0 to literally be on that logical line, not the row of pixels as it is in GDI. To better explain, think of the coordinates in WPF being representative of the lines drawn on a piece of graph paper whereas the pixels are the squares those lines make up (assuming 96 DPI that is. You'd need to adjust accordingly if they are different.)
So... to get the drawing to refer to the pixel locations, we need to shift the drawing from the lines themselves to be the center of the pixels (squares on graph paper) so we shift all drawing by 0.5, 0.5 (again, assuming a DPI of 96)
So if it is a 96 DPI setting, simply adding this in the OnRender method worked like a charm...
drawingContext.PushTransform(new TranslateTransform(.5, .5));
Hope this helps others!
M
Have a look at this article: Draw lines exactly on physical device pixels
UPD
Some valuable quotes from the link:
The reason why the lines appear blurry, is that our points are center
points of the lines not edges. With a pen width of 1 the edges are
drawn excactly between two pixels.
A first approach is to round each point to an integer value (snap to a
logical pixel) an give it an offset of half the pen width. This
ensures, that the edges of the line align with logical pixels.
Fortunately the developers of the milcore (MIL stands for media
integration layer, that's WPFs rendering engine) give us a way to
guide the rendering engine to align a logical coordinate excatly on a
physical device pixels. To achieve this, we need to create a
GuidelineSet
protected override void OnRender(DrawingContext drawingContext)
{
Pen pen = new Pen(Brushes.Black, 1);
Rect rect = new Rect(20,20, 50, 60);
double halfPenWidth = pen.Thickness / 2;
// Create a guidelines set
GuidelineSet guidelines = new GuidelineSet();
guidelines.GuidelinesX.Add(rect.Left + halfPenWidth);
guidelines.GuidelinesX.Add(rect.Right + halfPenWidth);
guidelines.GuidelinesY.Add(rect.Top + halfPenWidth);
guidelines.GuidelinesY.Add(rect.Bottom + halfPenWidth);
drawingContext.PushGuidelineSet(guidelines);
drawingContext.DrawRectangle(null, pen, rect);
drawingContext.Pop();
}