Printing in Silverlight cuts off some portion - silverlight

How do I provide a print option on my silverlight application. I am able to print using the PrintDocument class but it cuts of the edges.
Is there a way to shrink the view to fit in a print paper. Thanks for any help.
I am using the below code:
PrintDocument document = new PrintDocument();
document.PrintPage += (s, args) =>
{
args.PageVisual = this.LayoutRoot;
};
document.Print("Silverlight Print Application Demo");

There are two conceptually different tasks here:
Printing an element as rendered in the ui at some point and
Render an element exclusively for printing.
Since you're complaining about the cutting off of edges, I assume you want to do the latter. For that, I wouldn't add the current control's layout root, which I suspect you're doing. Add something that is not yet added to the visual tree, or remove it from its parent before printing.
If you have to remove it first for some reason (perhaps because the user needs to interact with it first), then store the element in a variable printedPage that you can re-add to the visual tree on the PrintDocument.EndPrint event.
For the main event handler use something like this:
void HandlePrintPage(object sender, PrintPageEventArgs e)
{
var panel = new Grid();
// get an element to print that is not attached to the visual tree
printedPage = GetDetachedPageToPrint();
panel.Children.Add(printedPage.ReportPage);
// we wrap it in a Viewbox to make it full-page
var viewbox = new Viewbox() { Child = panel };
// we need to have a measure pass
viewbox.Measure(e.PrintableArea);
e.PageVisual = viewbox;
// for more pages, we would need to be more careful
//with parent detachments
e.HasMorePages = false;
}

I had the same problem a couple of months ago and tried several solutions.
I`ve already answered this question in another post. Please check out this:
https://stackoverflow.com/a/20896774/1141477
It converts the framework element into images and fit it inside a rectangle, which is from the same size as the printableArea. It worked well to me.
Hope it helps!

Related

printing with silverlight the same image multiple time

I have an image an a textbox. the user enters into the textbox the number of copies (N) and clicks print. The printer prints the photo N times (3 photos per page, one under the other).
Hiw would you do something like that? Do I need to first generate a silverlight page?
Silverlight has a really simple to use Printing API. I have recently been using it and learnt how to do so using the tutorial Melodatron listed, and this one over at the Visiblox site. This is specific to their charting library but I still found it very useful.
I am assuming that you only have 1 image to print at a time here, let me know if you have more. First, you need to set up a grid or something similar to put your photos in. You will need N number of rows in this grid. You'll have to do this in code behind as you have a certain number of rows you'd like to create, but this can easily be achieved by something like this:
Grid grid = new Grid();
// Set the column and row definitions
for (int i = 0; i < Number of rows; i++)
{
grid.RowDefinitions.Add(new RowDefinition());
}
That should set up your grid that you need. After you have the grid, you need to populate it with the images that you have. This is pretty straightforward, the only issue will be that your image can't be used multiple times - i.e. the image that you already have can only appear on the grid once. You will have to duplicate it in order to place it on the grid multiple times. I'm not sure how you are creating your images, but you could possibly create a new image based on the source path of the existing image? There is a thread on how to do this here. You'll need to give more details if you need help with that.
// Set the column and row definitions
for (int i = 0; i < Number of rows; i++)
{
// Duplicate your existing image here.
Image image = new Image();
grid.Children.Add(image);
Grid.SetRow(image, i);
}
All of the code above should be in this method:
private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
//Code from above...
e.PageVisual = grid;
}
In the button event handler where you wish to print, do this:
private void Button_Click(object sender, RoutedEventArgs e)
{
PrintDocument printDocument = new PrintDocument();
printDocument.PrintPage += new EventHandler<PrintPageEventArgs>(printDocument_PrintPage);
printDocument.Print("TITLE");
}
Hope this helps!
There's a pretty good tutorial (link below) with an example and source code on advanced printing techniques in Silverlight.
You will need to create the layout as a custom control, which should not be so difficult, but you won't need to display it.
http://www.silverlightshow.net/items/Advanced-printing-in-Silverlight-4.aspx
Hopefully this gets you started in the right direction.

Winforms Usercontrol: Perfomance issues when creating and adding to parent

I have built a UserControl for display a list of other UserControls which themselves are bound to individual data objects. The link below shows an example implementation of this control.
Each of the individual user rows is its own UserControl/XtraUserControl, laid out in a FlowLayoutPanel.
The problem I have is with perfomance, to populate the list above takes around 500ms (excluding any data loading) - this is a combination of creating each control and then adding them to the FlowLayoutPanel using the AddRange(controls[]) method.
Does anyone know any way I can improve perfomance here? Do I have to manually paint the items instead of using User Controls?
Thanks in advance.
EDIT: I've added my own response below showing the solution I have stuck with for now.
Whether manually painting would help is a guess. Even if it were right (which I doubt) it's better not to guess.
I've seen this kind of issue before, and chances are there's a lot of stuff that goes on in the binding.
The way I've solved the problem is with this approach, but it's definitely "out there" in terms of programmer acceptance.
I gues you are using devexpress controls because you mention XtraUserControl. If so, why don't use an XtraGrid?You can add images column and button columns, and I think you'll get better performance and simpler/less code
First of all, try use pair SuspendLayout()/ResumeLayout(), then it has sense to stop painting at all by hiding the container control until all child usercontrols added.
Anyway, placing lots of child controls to a container is not a good idea.
You can have the same result by using highly customized grid or by custom painting (which is preferable).
Good luck!
I had a brainwave for another solution which I'm not quite sure is appropriate. I would really appreciate any feedback on this.
Two rationales led to this solution:
Firstly I wanted the flexibility of creating rows like any other control.
Secondly the lists that would use this approach only intend to display brief chunks of data, never more than say 20 items - for anything larger, ListViews are used.
So anyway, what I decided to do was cache a set number of the Panels (I've referred to the custom controls or rows as Panels throughout the code) and to build up this cache as the control is created. When populating the control with BusinessObjects, the existing cached Panels are displayed with their bound BusinessObject. You can see how this works exactly from the code below, so there is no need for a in-depth description.
The fact of the matter is that I've managed to reduce the data population time (after the initial cache setup of around 180ms for 10 Panels) from 500ms to 150ms for the list shown in the image above.
private int cacheSize = 10;
private List<P> cachedPanels = new List<P>(10);
private void InitItems()
{
this.contentPanel.SuspendLayout();
// Create the cached panels from the default cache value.
for (int i = 0; i < cacheSize; i++)
cachedPanels.Add(new P() { Margin = new Padding(0), Visible = false });
this.contentPanel.Controls.AddRange(cachedPanels.ToArray());
this.contentPanel.ResumeLayout(true);
}
private void PopulateListFromCache()
{
this.contentPanel.SuspendLayout();
// Iterate against both BusinessObjects and Panels to ensure that nothing is missed, for
// instance, where there are too many panels, the rest are hidden, and too many Business
// Objects, then more Panels are created.
for (int i = 0; i < this.businessObjects.Count || i < this.cachedPanels.Count; i++)
{
if (i >= this.cachedPanels.Count)
{
// Here, we have more BusinessObjects than Panels, thus we must create
// and assign a new panel.
this.cachedPanels.Add(new P() { Margin = new Padding(0) });
this.cachedPanels[i].Item = this.businessObjects[i];
this.contentPanel.Controls.Add(this.cachedPanels[i]);
}
else if (i >= this.businessObjects.Count)
{
// Here, we still have Panels cached but have run out of BusinessObjects,
// let's just hide them and clear their bindings.
this.cachedPanels[i].Item = default(T);
this.cachedPanels[i].Visible = false;
}
else
{
// Here, we have both BusinessObjects and Panels to put them in, so just
// update the binding and ensure the Panel is visible.
this.cachedPanels[i].Item = this.businessObjects[i];
this.cachedPanels[i].Visible = true;
}
}
this.contentPanel.ResumeLayout(true);
}
Obviously, more optimizations can be made, such as un-caching Panels after a certain amount of time of not being used etc. Also, I'm not entirely sure if keeping these controls - which are rather simple - in a cache will affect memory usage much.
If anyone can think of any other pointers then please, be my guest. Oh, and if you got this far, then thank you for reading this.

WPF Transforms movement

Ive done some tranforms for my object in the canvas, like rotating and transforming.
How can i store the new coordinates afterwards? If I try to move the object around it will only use the new Transforms.
From what I can see, this is fairly non-trivial. Generally, you'll want to let WPF handle that kind of thing for you.
That said, here's what I've been able to come up with, based on information from here. Given a text block "textBlockName":
HwndSource hwndSource = PresentationSource.FromVisual(textBlockName) as HwndSource;
Visual root = hwndSource.RootVisual;
// Translate the point from the visual to the root.
GeneralTransform transformToRoot = textBlockName.TransformToVisual(root);
Point p = new Point(0,0);
p = transformToRoot.Transform(p);
p = hwndSource.CompositionTarget.TransformToDevice.Transform(p ) ;
//Display the top left point of the text box, after transforms.
MessageBox.Show(p.ToString() );
Edit: Looking at this further, I cannot find a better solution to this. This seems to handle every situation I can think to throw at it, though.

How can I change the way InkCanvas draws?

I've searched for examples for this, but the ones I've ran across seem to focus on simpler stuff like setting the InkCanvas DefaultDrawingAttributes such as Width, Height, Color etc. Doesn't seem like there's a lot of material for this.
For example, if I hold down the mouse button I can see it drawing lines. What if I want to draw ellipses instead of lines, or draw ellipses around sampled points between the start and end of the line?
I know I can get new points with the StrokeCollected event, but beyond that I have no idea where to go. This guy seemed like he got msdn's code working, but I couldn't do it. I only know how to build the interface using XAML, and there doesn't seem to be a sample either.
edit
Created a StrokeCollection class variable called thisIsNotNice, initialized in the constructor and did this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = thisIsNotNice;
foreach (StylusPoint p in e.Stroke.StylusPoints)
{
StylusPointCollection spc = new StylusPointCollection();
spc.Add(p);
Stroke s = new Stroke(spc);
s.DrawingAttributes.Height = 3;
s.DrawingAttributes.Width = 3;
thisIsNotNice.Add(s);
}
e.Handled = true;
}
But it doesn't work as it should. The ellipses are drawn, but the lines drawn by the mouse are still there. Also, for some reason, the first time it works as it should, drawing just the ellipses, but afterward it draws both the ellipses and the lines. But, if I do this instead:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = new System.Windows.Ink.StrokeCollection();
e.Handled = true;
}
The lines aren't kept on the screen. So, I don't understand why they aren't being erased in the above code.
If I do this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
foreach (Stroke s in myInkCanvas.Strokes)
System.Diagnostics.Trace.WriteLine(s);
e.Handled = true;
}
I can also see that the canvas contains the line strokes.
While erasing the strokes after they have been added to the collection is far from ideal, it at least does what I want. I could set up the line color to be the same of the background, but then I wouldn't be able to retrieve just the ellipses. I could copy them to a separate collection too, but that's just awful.
It sounds like you want to customize the way strokes appear on your inkCanvas. There are two separate things to consider here:
1) The way they look as the ink flows off the pen, before it is lifted (the DynamicRenderer, who runs on another thread to ensure that ink is always fast, is responsible for this. It sounds like you're happy with your solution to this already.
2) The way the eventual stroke sitting on the canvas looks. To customize this you might try subclassing Stroke, overriding:
protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes);
Each time you get a strokeCollected (and here's the same horrible thing you were worried about but there you go), you remove the incoming stroke from the canvas and replace it with your custom implementation, stealing the stroke data from the incoming one.
Your implementation of DrawCore would look something like (pseudocode):
foreach(sp in this.StylusPoints)
drawingContext.DrawEllipse(RADIUS, sp.X, sp.Y)
And so as not to get the lines that normally happen you would not call base.DrawCore(context,attributes) at any point.

Animate ListBoxItem from one ListBox to another ListBox

I would like to create a visual effect when an item in a listbox is double clicked. So far I have drag and drop functionality where the item is visually attached to the mouse and can be moved to a drop target. From that functionality I am able to animate the item using the same logic of getting the item container, however I am not able to leave the items control. Is there any way to remove the item from the ListBox and visually animate it to another location? Basically the main list box is a hand of cards. When a card is double-clicked I want it to visually move from the hand listbox to a discard listbox. As of now, the logic for moving the item from one collection to another is no problem, however I would really like an animated visual representation of this event. Any ideas or references on how to do something like this would be appreciated.
Thanks,
Brandon
Further Details on what I have attempted:
There are some concepts that I do not yet have a strong grasp of yet, which has led me to run face first into this wall. I have a method that I pass in(some may be unnecessary) the ListBox as an ItemsControl, a FrameworkElement that is the listbox item, and the data object associated with the ListBox item. What I attempted to do was FindVisualChild of the ListBoxItem that is a canvas. I can do that. In my mind I was wanting to somehow clone the canvas either as a canvas or as a bitmap, add it to the children of the child of the page in the same location, remove the ListBoxItem from the ListBox, and animate the clone to the discard pile. When the animation completes the clone will be removed or hidden, and as that object is added to the discard pile collection it will effectively take the replace the clone.
My problem with this, is that I feel like there really is a simpler way of doing this using the adorner layer or something. I also, don't know how I would position the clone at the exact same position in the element further up the visual tree. I am going to keep working on it and researching other approaches, and I will just hope that someone will share some insight on this.
Here's some code I worked up to draw a visual to a bitmap. You may be able to adapt this to your needs, and draw the bitmap by adorning the UIElement that represents a common ancestor of the two list views. Note the use of FrameworkElement.TransformToAncestor to get the coordinates of a nested element in terms of an ancestor element.
public static BitmapSource CreateBitmapFromElement(FrameworkElement element, Double dpiX, Double dpiY)
{
Size elementSize = new Size(element.ActualWidth, element.ActualHeight);
Visual root = GetAdornerDecoratorAncestor(element);
Rect elementBounds = element.TransformToAncestor(root).TransformBounds(new Rect(elementSize));
RenderTargetBitmap rtb = new RenderTargetBitmap((Int32)(elementBounds.Size.Width * dpiX / 96.0),
(Int32)(elementBounds.Size.Height * dpiY / 96.0),
dpiX,
dpiY,
PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(root);
vb.ViewboxUnits = BrushMappingMode.Absolute;
vb.Stretch = Stretch.None;
vb.Viewbox = elementBounds;
dc.DrawRectangle(vb, null, new Rect(new Point(), elementBounds.Size));
}
rtb.Render(dv);
return rtb;
}
public static Visual GetAdornerDecoratorAncestor(DependencyObject obj)
{
while(obj != null && !(obj is AdornerDecorator))
{
obj = VisualTreeHelper.GetParent(obj);
}
return obj as AdornerDecorator;
}
OK, you could try taking a Visual element and setting it's background to a visualbrush of your ListItem and animate that to the other list box. You can wait for the storyboard completed event to actually do the switch. If this were me, I would animate from one box only to the edge of the other. If the switch happens fast enough it should look pretty seamless to the user. Finding the exact position of where the item is supposed to go into the list box would be pretty complex based on any sorting/filtering rules you have.
If the two listboxes are always in the same position, you could try animating the double-clicked item to a predetermined spot, let's say half-way between the old list and the new list. Then execute the code to move the item to the new list, but use a style that immediately starts an animation on that item starting it from that predetermined location and animating to its location in the new list. You'd probably have to tweak the initial offset of the new item at runtime based on where it's inserted in the list.

Resources