I am trying to print a report where we have several different components within the xaml.
By what I`ve found, when printing, you have to treat every UIelement as a single one, thus if the desiredSize is bigger than the AvailableSize you have to activate the flag HasMorePages.
But here comes the problem.
My user can write as much text as he/she wants on the grid, therefore, depending on the amount, the row expands and goes off the printable area, as you can see on the picture below.
I thought about giving a whole page to the grid, but it was to big still, which got me into a loop where the DesizedSize was always bigger than the PrintableArea.
My code is not very different from any source you find on internet when searching for Multiple Page printing.
It is based on this http://eswarbandaru.blogspot.com.au/2011/02/print-mulitple-pages-using-silverlight.html , but using Stackpanels instead of Textboxes.
Any idea?
Thank you in advance.
First you need to work out how many pages are needed
Dim pagesNeeded As Integer = Math.Ceiling(gridHeight / pageHeight) 'gets number of pages needed
Then once the first page has been sent to the printer, you need to move that data out of view and bring the new data into view ready to print. I do this by converting the whole dataset into an image/UI element, i can then adjust Y value accordingly to bring the next set of required data on screen.
transformGroup.Children.Add(New TranslateTransform() With {.Y = -(pageIndex * pageHeight)})
Then once the number of needed pages is reached, tell the printer to stop
If pagesLeft <= 0 Then
e.HasMorePages = False
Exit Sub
Else
e.HasMorePages = True
End If
Or if this is too much work, you can simply just scale all the notes to fit onto screen. Again probably by converting to UI element.
Check out this link for converting to a UI element.
http://www.codeproject.com/Tips/248553/Silverlight-converting-to-image-and-printing-an-UI
Hope this helps
Related
I am creating a text editor as a way of getting more familiar with C and gtk+. I am using gtk+-2.0 & gtksourceview-2.0, and gtk_scrolled_window . As a first attempt at creating a goto function browser I thought I would just simply create an array of functions found in the document and a corresponding array of lines on which they occur. I have that much done. I was surprised to find that there is no goto line capability that I can easily find in devhelp. It sounds like gtk_text_view_scroll_to_mark () is what I want (after creating a mark), but all the *scroll_to functions require a within_margin, which to be honest I don't really understand.:
From devhelp:
The effective screen for purposes of this function is reduced by a margin of size within_margin.
What does that mean?
Am I even close? How can I create this scroll to line number functionality?
Thanks.
UPDATE: The following three functions were used to scroll to a line in the buffer:
gtk_text_iter_set_line (&start, lineNums[9]);
gtk_text_buffer_add_mark (tbuffer, scroll2mark, &start);
gtk_text_view_scroll_to_mark (text_view, scroll2mark, 0.0, TRUE, 0.0, 0.17);
The last parameter of gtk_text_view_scroll_to_mark was used to get the target line number to line up with the very top line in the buffer. I imagine this parameter will not work on all screen sizes, but I have not tested it.
The gtk_text_view_scroll_mark_onscreen function got me close to the line number, but it was just a couple of lines off the bottom of the text area.
The within_margin parameter controls the area of the screen in which the scrolled-to text should appear or more precisely it sets the amount of space at the border of the screen in which the text should not appear.
This exists so that when you set use_align to false (i.e. you don't want the text to appear at a specific position on the screen), you can still make sure that the text doesn't appear directly at the top of bottom of the screen (which might be bad for readability purposes).
If you don't care at all about the position at which the text will appear, you can use g_text_view_scroll_mark_on_screen which only takes the text view and a mark and no further arguments. This will always scroll the minimum amount to make the text appear on screen.
I'm currently writing an eBook reader for Windows Phone Seven, and I'm trying to style it like the Kindle reader. In order to do so, I need to split my books up into pages, and this is going to get a lot more complex when variable font sizes are added.
To do this at the moment, I just add a word at a time into the textblock until it becomes higher than its container. As you can imagine though, with a document of over 120,000 words, this takes an unacceptable period of time.
Is there a way I can find out when the text would exceed the bounds (logically dividing it into pages), without having to actually render it? That way I'd be able to run it in a background thread so the user can keep reading in the meantime.
So far, the only idea that has occurred to me is to find out how the textblock decides its bounds (in the measure call?), but I have no idea how to find that code, because reflector didn't show anything.
Thanks in advance!
From what I can see the Kindle app appears to use a similar algorithm to the one you suggest. Note that:
it generally shows the % position through the book - it doesn't show total number of pages.
if you change the font size, then the first word on the page remains the same (so that's where the % comes from) - so the Kindle app just does one page worth of repagination assuming the first word of the page stays the same.
if you change the font size and then scroll back to the first page, then actually there is a discontinuity - they pull content forwards again in order to fill the first page.
Based on this, I would suggest you do not index the whole book. Instead just concentrate on the current page based on a "position" of some kind (e.g. character count - displayed as a percentage). If you have to do something on a background thread, then just look at the next page (and maybe the prev page) in order that scrolling can be more responsive.
Further to optimise your experience, there are a couple of changes you could make to your current algorithm that you could try:
try a different starting point and search increment for your algorithm - no need to start at one word and to then only add one word at a time.
assuming most of your books are ASCII, try caching the width of the common characters, and then work out the width of textblocks yourself.
Beyond that, I'd also quite like to try using <Run> blocks within your TextBlock - it may be possible to get the relative position of each Run within the TextBlock - although I've not managed to do this yet.
I do something similar to adjust font size for individual textboxes (to ensure they all fit). Basically, I create a TextBlock in code, set all my properties and check the ActualWidth and ActualHeight properties. Here is some pseudo code to help with your problem:
public static String PageText(TextBlock txtPage, String BookText)
{
TextBlock t = new TextBlock();
t.FontFamily = txtPage.FontFamily;
t.FontStyle = txtPage.FontStyle;
t.FontWeight = txtPage.FontWeight;
t.FontSize = txtPage.FontSize;
t.Text = BookText;
Size Actual = new Size();
Actual.Width = t.ActualWidth;
Actual.Height = t.ActualHeight;
if(Actual.Height <= txtPage.ActualHeight)
return BookText;
Double hRatio = txtPage.ActualHeight / Actual.Height;
return s.Substring((int)((s.Length - 1) * hRatio));
}
The above is untested code, but hopefully can get you started. Basically it sees if the text can fit in the box, if so you're good to go. If not, it finds out what percentage of the text can fit and returns it. This does not take word breaks into account, and may not be a perfect match, but should get you close.
You could alter this code to return the length rather than the actual substring and use that as your page size. Creating the textblock in code (with no display) actually performs pretty well (I do it in some table views with no noticeable lag). I wouldn't send all 120,000 words to this function, but a reasonable subset of some sort.
Once you have the ideal length you can use a RegEx to split the book into pages. There are examples on this site of RegEx that break on word boundaries after a specific length.
Another option, is to calculate page size ahead of time for each potential fontsize (and hardcode it with a switch statement). This could easily get crazy if you are allowing any font and any size combinations, and would be awful if you allowed mixed fonts/sizes, but would perform very well. Most likely you have a particular range of readable sizes, and just a few fonts. Creating a test app to calculate the text length of a page for each of these combinations wouldn't be that hard and would probably make your life easier - even if it doesn't "feel" right as a programmer :)
I didn't find any reference to this example from Microsoft called: "Principles of Pagination".
It has some interesting sample code running in Windows Phone.
http://msdn.microsoft.com/en-us/magazine/hh205757.aspx
You can also look this article about Page Transitions in Windows Phone and this other about the final touches in the E-Book project.
The code is downloadable: http://archive.msdn.microsoft.com/mag201111UIFrontiers/Release/ProjectReleases.aspx?ReleaseId=5776
You can query the FormattedText class that is used AFAIK inside textBlock. since this is the class being used to format text in preparation for Rendering, this is the most lower-level class available, and should be fast.
My 1st question here on stackoverflow.
I am trying to print a long grid, which was dynamically generated.
pdoc.PrintPage += (p, args) =>
{
args.PageVisual = myGrid;
args.HasMorePages = false;
};
When I use args.HasMorePages = false;, it prints the first page of the grid as it should (although it takes some time, since it sends a 123MB big bitmap to the poor printer - thanks for silverlight 4's print feature implementation.).
However, when I enable printing more pages withargs.HasMorePages = true;, the printing job runs amok on the memory and sends endless copies of the first printing page of the document - effectively disabling my developer machine. Even if the grid is only 2 pages long.
Why does this happen?
What is a possible workaround here? All I found on the net is that SL handles printing badly, but not a real solution.
The HasMorePages property indicates to silverlight printing that you have a least one more page to print. The PrintPage page event fires for each page to be printed.
Hence when you set HasMorePages to true you will get another PrintPage event, if you always set it true (as your code appears to be doing) you are creating an infinite loop.
At some point the code has to leave HasMorePages set to false.
Ultimately its up to you the developer to perform all the pagination logic and decide what appears on each page, Silverlight does not automagically do that for you.
I am creating printouts in WPF using flow documents. These printouts are set in separate window where DocumentViewer is placed.
When user clicks print I would like to show a progress bar that informs about current page that is sending to the printer. How can I do this?
I'm not sure exactly where your print code is, or where you want the progress bar, but I did something similar to this recently. This will be in VB.net.
First of all, create a new progressbar in the same class as the code you use to send the page to the printer. Then, we're going to take advantage of the "top-down" order in a block of code to change the progress bar.
The progress bar's value should be set to "0" be default. Now, in the code for sending the page to the printer, you're going to increase the progressbar's value (such as with the code "MyProgressBar.Value = MyProgressBar.Value + 1"). Put this code in between each line of the code you want to show progress for.
I would change the "+ 1" part of the code, however, to another value, so your progress bar progresses equally after each step. If you have three lines of code, then use "+ 33" (100\3), four lines use "+ 25", etc.
Finally, at the end of the code, set "MyProgressBar.Value = 100"
This only works, however, if you have access to a code longer than one line. For one line of code, I'm not sure how this works, unless you can get to the block of code that line points to.
If you have to use code from another class, you may need to do something like...
Dim MyWindowWhereProgressIs As New MyWindowWhereProgressIs
And then, each time you need to change the value, try...
MyWindowWhereProgressIs.MyProgressBar.Value = MyWindowWhereProgressIs.MyProgressBar.Value + 1
I'm not entirely sure whether or not those last two lines of code will work, as I'm away from Visual Studio right now, but it is worth a shot.
How many rows should be in the (main) buffer of a virtual Listview control?
I am witting an application in pure 'c' to the Win32 API. There is an ODBC connection to a database which will retrieve the items (actually rows).
The MSDN sample code implies a fixed size buffer of 30 for the end cache (Which would almost certainly not be optimal). I think the end cache and the main cache should be the same size.
My thinking is that the buffer should be more than the maximum number of items that could be displayed by the list view at one time. I guess this could be re-calculated each time the Listivew was resized?
Or, is it just better to go with a large fixed value. If so what is that value?
Use the ListView_ApproximateViewRect (or the LVM_APPROXIMATEVIEWRECT message) to get the view rect height.
Use the ListView_GetItemRect (or the LVM_GETITEMRECT message) to get the height of an item.
Divide the view rect height by the height of an item to get the number of items that can fit in your view.
Do this calculation on each size event.
Then create your buffer accordingly.
The LVN_ODCACHEHINT notification message will let you know how many items it is going to ask. This could help you in deciding how big your cache should be.
#Brian R. Bondy Thanks for the explicit help for how to do get the number of items. In fact I was all ready working my way to understanding that it could be done (for list or report view) with ListView_GetCountPerPage, and I would use you way to get it for for the others, though I don't need the ListView_ApproximateViewRect since I will all ready know the new size of the ListView.
#Lars Truijens I am already using the LVN_ODCACHEHINT and had though about using that to set the buffer size, however I need to read to the end of the SQL data to find the last item to get the number of rows returned from ODBC. Since that would be the best time to fill the 'end cache' I think I have to set up the number of items (and therefore fill the buffer) before we get a call to LVN_ODCACHEHIN.
I guess my real question is one of optimization for which I think Brian hinted at the answer. The amount of overhead in trashing your buffer and reallocating memory is smaller than the overhead of going out to the network and doing an ODBC read, some make the buffer fairly small and change it often rather.
Is this right?
I have done a little more playing around, and it seems that think that LVN_ODCACHEHINT generally fills the main buffer correctly, and only misses if a row (in report mode) is partially visible.
So I think the answer for the size of the cache is: The total number of displayed items, plus one row of displayed items (since in the icons views you have multiple items per row).
You would then re-read the cache with each WM_SIZE and LVN_ODCACHEHINT if either had a different begin and end item number.
The answer would seem to be: (Or a random collection of notes as I fiddle around with ideas)
As a general answer for buffers:
Start with some amount, in this case a screen full (I add an extra row in case the next is partially uncovered), and then every time the screen is scrolled, double the buffer size (up to the point before you run out of memory).
Which would seem to be wrong. As it turns out, most ways of loading data are all ready buffered. ODBC calls of File I/O. Pretty much anything that isn't that I can think off is either in memory or is recalculated on the fly. This means the answer really is: take the values provided in LVN_ODCACHEHINT (and add 1 either side - this just seems to work faster if you don't have an integral height).