This code results in a display of the same line of the table name for every one of the 230 records in the table but not the fields:
ListView lv = new ListView();
lv.ItemsSource = await App.Database.GetItemsAsync();
StackLayout sl = new StackLayout();
Label ln = new Label();
ln.SetBinding(Label.TextProperty, nameof(ContactsModel.LastName));
Label fn = new Label();
fn.SetBinding(Label.TextProperty, nameof(ContactsModel.FirstName));
sl.Children.Add(ln);
sl.Children.Add(fn);
sl.Children.Add(lv);
Content = sl;
But adding the datatemplate results in a blank screen
ListView lv = new ListView();
lv.ItemsSource = await App.Database.GetItemsAsync();
var template = new DataTemplate(typeof(ViewCell));
lv.ItemTemplate = template;
StackLayout sl = new StackLayout();
Label ln = new Label();
ln.SetBinding(Label.TextProperty, nameof(ContactsModel.LastName));
Label fn = new Label();
fn.SetBinding(Label.TextProperty, nameof(ContactsModel.FirstName));
sl.Children.Add(ln);
sl.Children.Add(fn);
sl.Children.Add(lv);
Content = sl;
first, this would be easier to do in XAML. But if you need to do it in code
var template = new DataTemplate(() =>
{
Label ln = new Label();
ln.SetBinding(Label.TextProperty, nameof(ContactsModel.LastName));
Label fn = new Label();
fn.SetBinding(Label.TextProperty, nameof(ContactsModel.FirstName));
var stack = new StackLayout();
stack.Children.Add(ln);
stack.Children.Add(fn);
return new ViewCell { View = stack };
});
lv.ItemTemplate = template;
I could create seprately the "page x of y" and re-ordered the TOC with the official examples. "Page x of y" is created according to iText 7: Building Blocks Chapter 7: Handling events; setting viewer preferences and writer properties with the examples Solving the "Page X of Y" problem; and TOC is created with reference to iText 7 examples TOC as first page.
Now I want the generated PDF to have both "page x of y" and re-ordered TOC. And "page x of y" shall be shown on all pages, i.e. on the 1st page (the TOC page), it shall show "Page 1 of 35", the 2nd page (start page of the main text) shall show "Page 2 of 35" (In this Jekyll and Hyde example, TOC has one page).
But when I tried to put "page x of y" and re-order TOC together, I found a problem in the generated PDF: the 1st page (the TOC page) showed correctly "Page 1 of 35", but the 2nd page (start page of the main text) showed also "Page 1 of 35".
What is the tricks to let the 2nd page to show "Page 2 of 35" with re-ordered TOC?
==code for Page X of Y and re-order TOC==
package main;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import com.itextpdf.io.IOException;
import com.itextpdf.io.font.FontConstants;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfOutline;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.action.PdfAction;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.canvas.draw.DottedLine;
import com.itextpdf.kernel.pdf.navigation.PdfDestination;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Tab;
import com.itextpdf.layout.element.TabStop;
import com.itextpdf.layout.hyphenation.HyphenationConfig;
import com.itextpdf.layout.layout.LayoutContext;
import com.itextpdf.layout.layout.LayoutResult;
import com.itextpdf.layout.property.AreaBreakType;
import com.itextpdf.layout.property.TabAlignment;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.renderer.ParagraphRenderer;
public class CreateTOC {
public static final String SRC = "D:/work/java_workspace/result/jekyll_hyde.txt";
public static final String DEST = "D:/work/java_workspace/result/test_toc.pdf";
public static void main(String args[]) throws IOException, Exception {
File file = new File(DEST);
file.getParentFile().mkdirs();
new CreateTOC().createPdf(DEST);
}
public void createPdf(String dest) throws IOException, java.io.IOException {
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
pdf.getCatalog().setPageMode(PdfName.UseOutlines);
PageXofY event = new PageXofY(pdf);
pdf.addEventHandler(PdfDocumentEvent.END_PAGE, event);
PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);
PdfFont bold = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
Document document = new Document(pdf);
document.setTextAlignment(TextAlignment.JUSTIFIED)
.setHyphenation(new HyphenationConfig("en", "uk", 3, 3))
.setFont(font)
.setFontSize(11);
// // add the cover
// document.add(new Paragraph("this is the cover 1"));
// document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
//
//
// document.add(new Paragraph("this is the cover 2"));
// document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
// parse text to PDF
BufferedReader br = new BufferedReader(new FileReader(SRC));
String name, line;
Paragraph p;
boolean title = true;
int counter = 0;
PdfOutline outline = null;
List<SimpleEntry<String,SimpleEntry<String, Integer>>> toc = new ArrayList<>();
while ((line = br.readLine()) != null) {
p = new Paragraph(line);
p.setKeepTogether(true);
if (title) {
name = String.format("title%02d", counter++);
outline = createOutline(outline, pdf, line, name);
int pagesWithoutCover = pdf.getNumberOfPages();
SimpleEntry<String, Integer> titlePage = new SimpleEntry(line, pagesWithoutCover);
p.setFont(bold).setFontSize(12)
.setKeepWithNext(true)
.setDestination(name)
.setNextRenderer(new UpdatePageRenderer(p, titlePage));
title = false;
document.add(p);
toc.add(new SimpleEntry(name, titlePage));
}
else {
p.setFirstLineIndent(18);
if (line.isEmpty()) {
p.setMarginBottom(12);
title = true;
}
else {
p.setMarginBottom(0);
}
document.add(p);
}
}
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
// create table of contents
int startToc = pdf.getNumberOfPages();
p = new Paragraph().setFont(bold).add("Table of Contents").setDestination("toc");
document.add(p);
toc.remove(0);
List<TabStop> tabstops = new ArrayList();
tabstops.add(new TabStop(580, TabAlignment.RIGHT, new DottedLine()));
for (SimpleEntry<String, SimpleEntry<String, Integer>> entry : toc) {
SimpleEntry<String, Integer> text = entry.getValue();
p = new Paragraph()
.addTabStops(tabstops)
.add(text.getKey())
// .setFixedLeading(150)
.add(new Tab())
.add(String.valueOf(text.getValue()))
.setAction(PdfAction.createGoTo(entry.getKey()));
document.add(p);
}
int tocPages = pdf.getNumberOfPages() - startToc;
// reorder pages
PdfPage page;
for (int i = 0; i <= tocPages; i++) {
page = pdf.removePage(startToc + i);
pdf.addPage(i + 1, page);
}
event.writeTotal(pdf);
document.close();
}
protected class UpdatePageRenderer extends ParagraphRenderer {
protected SimpleEntry<String, Integer> entry;
public UpdatePageRenderer(Paragraph modelElement, SimpleEntry<String, Integer> entry) {
super(modelElement);
this.entry = entry;
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
entry.setValue(layoutContext.getArea().getPageNumber());
return result;
}
}
public PdfOutline createOutline(PdfOutline outline, PdfDocument pdf, String title, String name) {
if (outline == null) {
outline = pdf.getOutlines(false);
outline = outline.addOutline(title);
outline.addDestination(PdfDestination.makeDestination(new PdfString(name)));
return outline;
}
PdfOutline kid = outline.addOutline(title);
kid.addDestination(PdfDestination.makeDestination(new PdfString(name)));
return outline;
}
protected class PageXofY implements IEventHandler {
protected PdfFormXObject placeholder;
protected float side = 20;
protected float x = 300;
protected float y = 25;
protected float space = 4.5f;
protected float descent = 3;
public PageXofY(PdfDocument pdf) {
placeholder = new PdfFormXObject(new Rectangle(0, 0, side, side));
}
#Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
int pageNumber = pdf.getPageNumber(page);
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(
page.newContentStreamBefore(), page.getResources(), pdf);
Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
Paragraph p = new Paragraph().add("Page ").add(String.valueOf(pageNumber)).add(" of");
canvas.showTextAligned(p, x, y, TextAlignment.RIGHT);
pdfCanvas.addXObject(placeholder, x + space, y - descent);
pdfCanvas.release();
}
public void writeTotal(PdfDocument pdf) {
Canvas canvas = new Canvas(placeholder, pdf);
canvas.showTextAligned(String.valueOf(pdf.getNumberOfPages()),
0, descent, TextAlignment.LEFT);
}
}
}
In general
You will obviously run into trouble if you first create pages including a "page x/y" using the current page number of each page and then re-order the pages.
If you know beforehand how many pages you will move up front, you can take this re-ordering into account by adding this number as offset to the page number in your event listener. Be sure to reset that offset when you start creating the TOC pages.
If you don't know that number, it does not make sense to try to number the pages before re-ordering at all. Instead add page numbers afterwards as described in the iText 7: Building Blocks Chapter 2: Working with the RootElement example Adding a Page X of Y footer, i.e. loop over every page in the document and add a "Page X of Y" Paragraph to each page:
int n = pdf.getNumberOfPages();
Paragraph footer;
for (int page = 1; page <= n; page++) {
footer = new Paragraph(String.format("Page %s of %s", page, n));
document.showTextAligned(footer, 297.5f, 20, page,
TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);
}
document.close();
Don't forget to set immediateFlush to false as described right after that example.
Using an offset
In a comment you indicated that you did not want to use the solution from chapter 2 referenced above as you didn't want to keep the whole PDF in memory. Then you posted your code.
Thus, let's try and implement the offset mentioned above in your code.
The offset variable is best located right in the event listener. Having added it, it might looks like this:
protected class PageXofY implements IEventHandler
{
// vvv added
int offset = 0;
// ^^^ added
protected PdfFormXObject placeholder;
protected float side = 20;
protected float x = 300;
protected float y = 25;
protected float space = 4.5f;
protected float descent = 3;
public PageXofY(PdfDocument pdf)
{
placeholder = new PdfFormXObject(new Rectangle(0, 0, side, side));
}
#Override
public void handleEvent(Event event)
{
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
int pageNumber = pdf.getPageNumber(page);
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(
page.newContentStreamBefore(), page.getResources(), pdf);
Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
// vvv changed
Paragraph p = new Paragraph().add("Page ").add(String.valueOf(pageNumber + offset)).add(" of");
// ^^^ changed
canvas.showTextAligned(p, x, y, TextAlignment.RIGHT);
pdfCanvas.addXObject(placeholder, x + space, y - descent);
pdfCanvas.release();
}
public void writeTotal(PdfDocument pdf)
{
Canvas canvas = new Canvas(placeholder, pdf);
canvas.showTextAligned(String.valueOf(pdf.getNumberOfPages()),
0, descent, TextAlignment.LEFT);
}
}
(PageXofY)
(You might want to add getters and setters for the offset.)
When importing the text body your page numbers currently are created off-by-one as the TOC page will later be pulled up to the front. Thus, you need to use an offset of 1 (1 page TOC) during that import.
Afterwards, before starting the TOC page, you will have to reset the offset to 0 as nothing will be pulled before the TOC page thereafter.
Id est:
public void createPdf(Reader reader, String dest) throws IOException
{
[...]
Document document = new Document(pdf);
document.setTextAlignment(TextAlignment.JUSTIFIED)
.setHyphenation(new HyphenationConfig("en", "uk", 3, 3))
.setFont(font)
.setFontSize(11);
// vvv added
event.offset = 1;
// ^^^ added
// // add the cover
[...]
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
// vvv added
event.offset = 0;
// ^^^ added
// create table of contents
int startToc = pdf.getNumberOfPages();
[...]
}
(CreateTOC method createPdf)
In the current iText 7 development 7.0.3-SNAPSHOT version this results in desired page numbering.
Beware: There had been reports on delayed page event execution. Probably the event timing meanwhile has been changed. With older versions, therefore, the code might still apply wrong page numbers.
I have a custom control, basically a stacklayout that I can bind a concatenated string, and have this break up into multiple objects, like a tag list.
public void Render()
{
if (ItemsSource == null)
return;
this.Orientation = Orientation == StackOrientation.Vertical ? StackOrientation.Vertical : StackOrientation.Horizontal;
this.Margin = new Thickness (0, 0, 0, 0);
List<string> items = ItemsSource.Split('|').ToList();
foreach (var item in items)
{
var frame = new Frame();
frame.BindingContext = item;
frame.BackgroundColor = Color.FromHex("#FCFACF");
frame.OutlineColor = Color.FromHex("#D0C0AD");
frame.HasShadow = false;
frame.Padding = new Thickness(4, 2, 4, 0);
var label = new Label();
label.Text = "{Binding}";
label.FontSize = 10;
label.Parent = frame;
Children.Add(frame);
}
}
but I can't seem to get this right, how do I add a lable to be a child of a frame, and how can I fix my binding, as at present if I add the label to the children of the stacklayout the text of the labels is {binding} and not the actual text.
Can someone please help me with this. I had all of this working if I added a ItemTemplate with a datatemplate and viewcell in the XAML, but I don't want this done in XAML as I'd like to reuse all of this in other views.
Frame has Content property
Frame.Content = label;
To assign a binding in code, use
label.SetBinding(Label.TextProperty, new Binding("."));
I want to make a component clickable. I am using this approach : https://groups.google.com/forum/#!topic/codenameone-discussions/hfVoqRwd9lI (create a component with custom style, and set multibutton as lead component into that container).
This is my code :
Component[] listingsToAdd = new Component[listings.size()];
for (int iter = 0; iter < listingsToAdd.length; iter++) {
MultiButton mb = new MultiButton();
final Map<String, Object> currentListing = listings.get(iter);
Container c = new Container(new BoxLayout((BoxLayout.Y_AXIS)));
String guid = (String) currentListing.get("seq").toString();
Label date = new Label((String) currentListing.get("dt"));
Label name = new Label((String) currentListing.get("name"));
Label startMt = new Label((String) currentListing.get("start_mt"));
Label place = new Label((String) currentListing.get("place"));
Label description = new Label((String) currentListing.get("description"));
c.add(date).add(name).add(startMt).add(place).add(description);
mb.addActionListener(evt
-> showScheduleDetails(searchResults, currentListing));
c.setLeadComponent(mb);
listingsToAdd[iter] = c;
}
But, when I run it, the container still can't be clickable. How to solve that?
MultiButton is already a lead component. You should set a regular Button or any component that doesn't derive Container as your lead.
I am working on a Xamarin Mac application and am currently creating a newsfeed list.
What I would like to have is that when the user click on one of the row I do some stuff (open up a browser and show the full story).
Here is how my custom cell (row) looks like.
public class CustomLatestNewsCell : NSView
{
public NSText Title;
public NSText Date;
public NSText Details;
public CustomLatestNewsCell()
{
Title = new NSText();
Title.Frame = new CoreGraphics.CGRect(0, 120, 300, 70);
Title.TextColor = NSColor.FromSrgb((nfloat)0.207, (nfloat)0.576, (nfloat)0.639, (nfloat)1);
Title.Font = NSFont.UserFontOfSize(20);
Title.Editable = false;
Title.Selectable = false;
this.AddSubview(Title);
Date = new NSText();
Date.Frame = new CoreGraphics.CGRect(0, 25, 300, 30);
Date.TextColor = NSColor.FromSrgb((nfloat)0.702, (nfloat)0.702, (nfloat)0.702, (nfloat)1);
Date.Editable = false;
Date.Selectable = false;
this.AddSubview(Date);
Details = new NSText();
Details.Frame = new CoreGraphics.CGRect(310, 120, 550, 32);
Details.TextColor = NSColor.FromSrgb((nfloat)0.5, (nfloat)0.5, (nfloat)0.5, (nfloat)1);
Details.Editable = false;
Details.Selectable = false;
this.AddSubview(Details);
this.Frame = new CoreGraphics.CGRect(0, 0, 850, 150);
}
}
I have tried a lot of things but there doesn't seem to be a click/activated/somethingElse function which will trigger when I click on anything in the cell. The only thing that works is
public override void SelectionIsChanging(NSNotification notification)
{
var row = (NSTableView)notification.Object;
var selectedRow = row.SelectedRow;
if(selectedRow < 0)
return;
var selectedFeed = feed[(int)selectedRow];
row.DeselectAll(row);
NSWorkspace ws = new NSWorkspace();
NSError error = new NSError();
ws.OpenURL(new NSUrl(selectedFeed.Url),
NSWorkspaceLaunchOptions.AllowingClassicStartup,
new NSDictionary(),
out error);
}
but in this case the user has to click on some "empty" parts of the row. Is there a way that I catch a click on any of the parts in the cell and then trigger the action?
I am quite new to Mac development so I am quite lost at the moment