created a sports table that shows different statistics sorted by one property. When sorting for a different property I created completely new labels that just sit on top of the old ones instead of replacing them.
This is ran when a button is pressed:
private void Points_Order(object sender, RoutedEventArgs e)
{
List<Team> leagueTeams = new List<Team>();
using (StreamReader sr = new StreamReader("TXT1.txt"))
{
using (JsonReader jr = new JsonTextReader(sr))
{
JsonSerializer js = new JsonSerializer();
leagueTeams = js.Deserialize<List<Team>>(jr);
}
}
List<Team> sortedList = leagueTeams.OrderByDescending(o => o.points).ToList(); //orders the keagueTeams list by points and stores in a new list using linq
List<Label> TeamLabels = new List<Label>(); //makes list of labels that show the teams
List<string> Names = new List<string>(); //Creates a list for the names
List<Label> gamesPlayedLabels = new List<Label>();
List<int> GamesPlayed = new List<int>();
foreach (var properties in sortedList)
{
Names.Add(properties.name);//adds the name of each object into the Names list
GamesPlayed.Add(properties.gamesPlayed);
}
for (int i = 0; i < 20; i++)
{
string nameLab = Names[i];
TeamLabels.Add(new Label { Height = 100, Width = 100, Content = nameLab }); //sets position of the name labels
Canvas.SetLeft(TeamLabels[i], 0);
Canvas.SetTop(TeamLabels[i], (i * 19) + 19);
canvas1.Children.Add(TeamLabels[i]);
string played = Convert.ToString(GamesPlayed[i]);
gamesPlayedLabels.Add(new Label { Height = 100, Width = 100, Content = played });
Canvas.SetLeft(gamesPlayedLabels[i], 112);
Canvas.SetTop(gamesPlayedLabels[i], (i * 19) + 19);
canvas1.Children.Add(gamesPlayedLabels[i]);
}
}
When a second button is pressed the exact same code is ran but apart from
List<Team> sortedList = leagueTeams.OrderByDescending(o => o.points).ToList()
it is
List<Team> sortedList = leagueTeams.OrderBy(o => o.name).ToList();
so the question is can you replace existing labels so the table can be sorted?
Related
I'm using ColumnDocumentRenderer to draw content in two columns.
Below are the codes.
public void ManipulatePdf(string dest)
{
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc, PageSize.A4);
doc.SetMargins(55f, 55f, 45f, 55f);
var interval = 20f;
var columnWidth = (doc.GetPdfDocument().GetDefaultPageSize().GetWidth() - 110 - interval) / 2;
var pageHeight = doc.GetPdfDocument().GetDefaultPageSize().GetHeight() - 110;
var baseText = "We have seen too many reports, too many words, too many good intentions, too many families torn apart, and too many excruciatingly painful deaths to see yet more delays in taking collective action.";
var pTitle = new Paragraph(baseText);
pTitle.SetFontSize(20);
pTitle.SetTextAlignment(iText.Layout.Properties.TextAlignment.JUSTIFIED);
doc.Add(pTitle);
var currentYLine = doc.GetRenderer().GetCurrentArea().GetBBox().GetTop();
Rectangle[] columns = {
new Rectangle(55, 55,
columnWidth,
currentYLine - 55),
new Rectangle(55 + columnWidth + interval, 55,
columnWidth,
currentYLine - 55) };
doc.SetRenderer(new ColumnDocumentRenderer(doc, columns));
for (int i = 1; i <= 12; i++)
{
var text = baseText;
for (int j = 1; j <= i; j++)
{
text += " Additional Text. ";
}
var p = new Paragraph(text);
p.SetTextAlignment(iText.Layout.Properties.TextAlignment.JUSTIFIED);
doc.Add(p);
if (i == 6)
{
var tp = new Paragraph("Introduction");
tp.SetFontSize(20);
tp.SetMarginTop(50);
tp.SetTextAlignment(iText.Layout.Properties.TextAlignment.JUSTIFIED);
doc.Add(tp);
}
}
doc.Close();
}
Below is the created PDF:
Please take a look at the red line in this screenshot, my question is that how to make bottom of two columns alignment in a straight line?
Thanks & Regards.
As discussed in the comments to the original question, algorithm to auto-align the bottom lines does not exist in iText and defining its behavior turned out to be tricky even for the person asking the question.
However, it is possible to calculate the difference between the vertical positions of the bottom lines when the document is being rendered and then you can correct the content and create another document with proper layout.
Here is the example implementation which analyzes the positions of the content when it's being drawn on the document:
class CustomColumnDocumentRenderer : ColumnDocumentRenderer {
public CustomColumnDocumentRenderer(Document document, Rectangle[] columns) : base(document, columns) {
}
public CustomColumnDocumentRenderer(Document document, bool immediateFlush, Rectangle[] columns) : base(document, immediateFlush, columns) {
}
IDictionary<int, float> leftColumnBottom = new Dictionary<int, float>();
IDictionary<int, float> rightColumnBottom = new Dictionary<int, float>();
protected override void FlushSingleRenderer(IRenderer resultRenderer) {
TraverseRecursively(resultRenderer, leftColumnBottom, rightColumnBottom);
base.FlushSingleRenderer(resultRenderer);
}
void TraverseRecursively(IRenderer child, IDictionary<int, float> leftColumnBottom, IDictionary<int, float> rightColumnBottom) {
if (child is LineRenderer) {
int page = child.GetOccupiedArea().GetPageNumber();
if (!leftColumnBottom.ContainsKey(page)) {
leftColumnBottom[page] = 1000;
}
if (!rightColumnBottom.ContainsKey(page)) {
rightColumnBottom[page] = 1000;
}
bool isLeftColumn = !(child.GetOccupiedArea().GetBBox().GetX() > PageSize.A4.GetWidth() / 2);
if (isLeftColumn) {
leftColumnBottom[page] =
Math.Min(leftColumnBottom[page], child.GetOccupiedArea().GetBBox().GetBottom());
} else {
rightColumnBottom[page] = Math.Min(rightColumnBottom[page],
child.GetOccupiedArea().GetBBox().GetBottom());
}
} else {
foreach (IRenderer ownChild in (child is ParagraphRenderer ? (((ParagraphRenderer)child).GetLines().Cast<IRenderer>()) : child.GetChildRenderers())) {
TraverseRecursively(ownChild, leftColumnBottom, rightColumnBottom);
}
}
}
public List<float> getDiffs() {
List<float> ans = new List<float>();
foreach (int pageNum in leftColumnBottom.Keys) {
ans.Add(leftColumnBottom[pageNum] - rightColumnBottom[pageNum]);
}
return ans;
}
}
To use it, make sure to pass the customized document renderer to the Document instance:
CustomColumnDocumentRenderer renderer = new CustomColumnDocumentRenderer(doc, true, columns);
doc.SetRenderer(renderer);
Then you can get page-by-page diffs:
Console.WriteLine(renderer.getDiffs()[0]);
private int SumOfNodes()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> SumOfHardware = t.GetHardware().FindAll(x => x.Nodes != 0);
int SumOfNodes = 0;
foreach (Hardware i in SumOfHardware )
{
SumOfNodes += i.Nodes;
}
return SumOfNodes;
}
private int SumOfRepeaters()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> SumOfHardware = t.GetHardware().FindAll(x => x.Repeaters != 0);
int SumOfRepeaters = 0;
foreach (Hardware i in SumOfHardware)
{
SumOfRepeaters += i.Repeaters;
}
return SumOfRepeaters;
}
private int SumOfHubs()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> SumOfHardware = t.GetHardware().FindAll(x => x.Hubs != 0);
int SumOfHubs= 0;
foreach (Hardware i in SumOfHardware)
{
SumOfHubs += i.Hubs;
}
return SumOfHubs;
}
private string Month()
{
DateTime now = DateTime.Now;
string month = DateTime.Now.ToString("MMMM");
return month;
}
private void DisplayData()
{
SeriesCollection = new SeriesCollection
{
new ColumnSeries
{
Title = "Nodes",
Values = new ChartValues<int> { SumOfNodes() }
},
};
SeriesCollection.Add
(
new ColumnSeries
{
Title = "Repeaters",
Values = new ChartValues<int> { SumOfRepeaters() }
}
);
SeriesCollection.Add
(
new ColumnSeries
{
Title = "Hubs",
Values = new ChartValues<int> { SumOfHubs() }
}
);
Labels = new[] { Month() };
Formatter = value => value.ToString("N");
DataContext = this;
}
enter image description here
At this points I've managed to create an app that adds/removes and updates my items on my database. I'm also planning to add some stats (Started off with graph visualisation) but I'm facing an issue.
I want to seperate columns based on months. So for example as seen by the image attached no matter how many items i add , remove or update the total amount for each item is added to Decemeber. But when January comes any newly added modification to the quantity of my items I would like to see adjacent to the Decemeber one.
P.S.: There is alot of code repitition which will accounted for later on.
I've managed to put something together regarding my answer above which may be usefull for someone in the future
What I've done was to
1) Get my data from my db.
2) Find the Month of interest using LINQ queries **
instead of selecting the items (i.e repeaters, nodes)
**
3) By doing so it also accounts for the items I'm interested in during the month of interest.
4) Do an incremental foreach loop as shown in my code above.
5) Change the methodd i want to display (i.e DisplayData to DisplayDecemberData) and use in the same way as above.
6) Create further methods for the subsequent months and just add the additional information in the ChartValues object.
See Below
private int DecemberNodes()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> decembernodes = t.GetHardware().FindAll(x => x.Date.Month==12);
int DecemberSumOfNodes = 0;
foreach (Hardware i in decembernodes)
{
DecemberSumOfNodes += i.Nodes;
}
return DecemberSumOfNodes;
}
private int JanuaryNodes()
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
List<Hardware> januarynodes = t.GetHardware().FindAll(x => x.Date.Month==01);
int JanuarySumOfNodes = 0;
foreach (Hardware i in januarynodes)
{
JanuarySumOfNodes += i.Nodes;
}
private void DisplayDecemberData()
{
SeriesCollection = new SeriesCollection
{
new ColumnSeries
{
Title = "Nodes",
Values = new ChartValues<int> { DecemberNodes() }
},
};
private void DisplayJanuaryData()
{
SeriesCollection = new SeriesCollection
{
new ColumnSeries
{
Title = "Nodes",
**Values = new ChartValues<int> { DecemberNodes(), JanuaryNodes() }**
},
};
There is some code repetition and I'm pretty sure there is a more code concise way of actually doing it but for now this seems to do the trick. When I simplify the code i will post it.
I have a book library and I want to print library labels for them. When you select books from GridControl and click Print Labels button, the program creates labels on a pdf file like this:
private void btnQRPrintLabels_Click(object sender, EventArgs e)
{
// These are books' ids. Normally, these are retrieved from GridControl element.
var books_ids = new List<int>() {1, 2, 5, 6, 7, 12};
// I don't use PanelControl. But in the future, maybe I can use it later.
CreateLabels(books_ids, panelControl1);
}
And here is the contents of CreateLabels method:
public void CreateLabels(List<int> books_ids, PanelControl p)
{
var doc = new Document(PageSize.A4, 10, 10, 10, 10);
var m = new SaveFileDialog
{
Filter = #"PDF File Format|*.pdf",
FileName = "Labels.pdf"
};
if (m.ShowDialog() != DialogResult.OK) return;
var file = new FileStream(m.FileName, FileMode.Create);
wr = PdfWriter.GetInstance(doc, file);
doc.Open();
cb = wr.DirectContent;
wr.PageEvent = this;
// Labels are created from this method.
Labels(doc, books_ids);
doc.Close();
}
Finally, the contents of Labels method:
protected void Labels(Document doc, List<int> books_ids)
{
var i = 1;
foreach (var book_id in books_ids)
{
var alignment = (i % 2 != 0) ? Element.ALIGN_LEFT : Element.ALIGN_RIGHT;
// Book's informations are retrieved from Linq Connect Model.
var _connection = new LinqtoSQLiteDataContext();
var book = _connection.books.SingleOrDefault(b_no => b_no.id == book_id);
// Outer table to get rounded corner...
var table = new PdfPTable(1) { WidthPercentage = 48, HorizontalAlignment = alignment };
// Inner table: First cell for QR code and second cell for book's informations.
var inner_table = new PdfPTable(2) { WidthPercentage = 100 };
inner_table.DefaultCell.Border = Rectangle.NO_BORDER;
var inner_measurements = new[] { 40f, 60f };
inner_table.SetWidths(inner_measurements);
// Generate QR code in the `Tools` class, `GenerateQR` method.
System.Drawing.Image image = Tools.GenerateQR(100, 100, book?.isbn);
var pdfImage = iTextSharp.text.Image.GetInstance(image, System.Drawing.Imaging.ImageFormat.Jpeg);
var qr = iTextSharp.text.Image.GetInstance(pdfImage);
var a = new PdfPCell(qr) { Border = Rectangle.NO_BORDER };
var b = new PdfPCell(new Phrase(book?.name, font_normal)) { Border = Rectangle.NO_BORDER };
inner_table.AddCell(a);
inner_table.AddCell(b);
var s = new PdfPCell(inner_table)
{
CellEvent = new RoundRectangle(),
Border = Rectangle.NO_BORDER,
Padding = 2,
HorizontalAlignment = Element.ALIGN_LEFT
};
table.AddCell(s);
doc.Add(table);
i++;
}
}
These codes bring me labels like this:
But I want to get labels like this:
Because if I manage getting labels side by side, I print it to computer labels easily. Here is the computer label that I want to print to on it:
How can I iterate tables side by side?
Use one outer Table with
new PdfPTable(2) { WidthPercentage = 100 }
Then add all your inner tables to this one and at last add the outer table to the document. This way there is NO need to use that toggeling left/right alignment.
:edit:
var table = new PdfPTable(2) { WidthPercentage = 100 };
foreach (var book_id in books_ids)
{
// Book's informations are retrieved from Linq Connect Model.
var _connection = new LinqtoSQLiteDataContext();
var book = _connection.books.SingleOrDefault(b_no => b_no.id == book_id);
// Outer table to get rounded corner...
// Inner table: First cell for QR code and second cell for book's informations.
var inner_table = new PdfPTable(2) { WidthPercentage = 100 };
inner_table.DefaultCell.Border = Rectangle.NO_BORDER;
var inner_measurements = new[] { 40f, 60f };
inner_table.SetWidths(inner_measurements);
// Generate QR code in the `Tools` class, `GenerateQR` method.
System.Drawing.Image image = Tools.GenerateQR(100, 100, book?.isbn);
var pdfImage = iTextSharp.text.Image.GetInstance(image, System.Drawing.Imaging.ImageFormat.Jpeg);
var qr = iTextSharp.text.Image.GetInstance(pdfImage);
var a = new PdfPCell(qr) { Border = Rectangle.NO_BORDER };
var b = new PdfPCell(new Phrase(book?.name, font_normal)) { Border = Rectangle.NO_BORDER };
inner_table.AddCell(a);
inner_table.AddCell(b);
PdfPCell labelCell = new PdfPCell(inner_table)
{
CellEvent = new RoundRectangle(),
Border = Rectangle.NO_BORDER,
Padding = 2,
HorizontalAlignment = Element.ALIGN_LEFT
};
table.AddCell(labelCell);
}
doc.Add(table);
Just add page-level table (2 columns, 6 rows) and place each label into individual cell in this page-level table.
With a List of 6 images names and a list of six picture boxes, I want to show images from resources in picture boxes randomly.
This code is not working. Just empty picture boxes.... Why?
List<PictureBox> box = new List<PictureBox>();
box.Add(pictureBox1);
box.Add(pictureBox2);
box.Add(pictureBox3);
box.Add(pictureBox4);
box.Add(pictureBox5);
box.Add(pictureBox6);
List<string> name = new List<string>();
name.Add("_1.jpg");
name.Add("_2.jpg");
name.Add("_3.jpg");
name.Add("_4.jpg");
name.Add("_5.jpg");
name.Add("_6.jpg");
Random r = new Random();
for (int i = 0; i < box.Count; i++)
{
int rand = r.Next(0, 6);
String imgname = name[rand];
object Ob= Properties.Resources.ResourceManager.GetObject(imgname);
box[i].Image = Ob as Image;
}
I modified your code:
List<PictureBox> box = new List<PictureBox>();
box.AddRange(new PictureBox[]
{
pictureBox1,
pictureBox2,
pictureBox3,
pictureBox4,
pictureBox5,
pictureBox6,
});
List<string> name = new List<string>()
{
"_1.jpg",
"_2.jpg",
"_3.jpg",
"_4.jpg",
"_5.jpg",
"_6.jpg"
};
Random r = new Random();
for (int i = 0; i < this.Count; i++)
{
var rm = new System.Resources.ResourceManager(((System.Reflection.Assembly)System.Reflection.Assembly.GetExecutingAssembly()).GetName().Name + ".Properties.Resources", ((System.Reflection.Assembly)System.Reflection.Assembly.GetExecutingAssembly()));
this[i].Image = (Bitmap)rm.GetObject(name[r.Next(0, 6)]);
}
Your resources typically do not include the file extension, so try just using the name of the image.
name.Add("_1");
name.Add("_2");
Personally, I would not include an underscore in the file names — might complicate things.
I have an ASP.NET web application. This application renders out glossary type items, similar to the following:
This goes through all the letters in the alphabet for items. I am rendering this out and appending it directly to a Controls collection in a Server Control using the following:
List<char> alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray().ToList();
foreach (char c in alpha)
{
Label lblAlphaCharacter = new Label();
lblAlphaCharacter.Font.Size = 24;
lblAlphaCharacter.Font.Bold = true;
lblAlphaCharacter.Text = c.ToString(CultureInfo.InvariantCulture);
Controls.Add(lblAlphaCharacter);
Controls.Add(new LiteralControl("<p>"));
FilterOnAlphaCharacter(this, Page, c);
Controls.Add(new LiteralControl("<p>"));
}
private static void FilterOnAlphaCharacter(Control control, Page page, char character)
{
foreach (List<Things> item in items)
{
string title = item.Title;
string description = item.Definition;
HyperLink link = new HyperLink();
link.Text = title;
control.Controls.Add(link);
Label lblDescription = new Label();
lblDescription.Text = string.Format(" - {0}", description);
control.Controls.Add(lblDescription);
}
}
}
I am trying to, depending on the content, equally split this, so that it looks like this:
This can have different amounts of items. So in reality, there could be 25 entries under "A", and perhaps 1 under "Z". The above is just an example, it goes through all letters A-Z. The expected result would be based on the amount of content, it would equally split between two columns. I have to do this server-side (I was thinking using Table or HtmlTable and related objects).
Howe would you implement a solution for splitting the content equally in a Table or the likes (sort of indifferent on approach).
try this:
//it shows the number of line that inserting during the process
private int _inserteditemCount;
//its number of items in each column
private int _itemsCount;
//line height use for determine paragraph line height
private const string Lineheight = "30px";
protected void Page_Load(object sender, EventArgs e)
{
_inserteditemCount = 0;
var alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
//you can do this query in data access layer
var listCountcount = new Thingsclass().GetThings().Count;
//Count of rows on dictionary + number of leters
_itemsCount = (listCountcount + alpha.Count()) / 2;
var leftdiv = new HtmlGenericControl("div");
var rightdiv = new HtmlGenericControl("div");
//you can change this styles
leftdiv.Style.Add("display", "inline-block");
leftdiv.Style.Add("width", "50%");
leftdiv.Style.Add("float", "Left");
rightdiv.Style.Add("display", "inline-block");
rightdiv.Style.Add("float", "right");
rightdiv.Style.Add("width", "50%");
foreach (var c in alpha)
{
var lblAlphaCharacter = new Label();
lblAlphaCharacter.Font.Size = 24;
lblAlphaCharacter.Font.Bold = true;
lblAlphaCharacter.Text = c.ToString(CultureInfo.InvariantCulture);
var control = _inserteditemCount <= _itemsCount ? leftdiv : rightdiv;
var paragraph = new HtmlGenericControl("p");
paragraph.Style.Add("line-height", Lineheight);
paragraph.Controls.Add(lblAlphaCharacter);
control.Controls.Add(paragraph);
FilterOnAlphaCharacter(leftdiv, rightdiv, c.ToString());
_inserteditemCount++;
}
Panel1.Controls.Add(leftdiv);
Panel1.Controls.Add(rightdiv);
}
private void FilterOnAlphaCharacter(Control leftctr, Control rightctr, string character)
{
//you can do this query in data access layer
var items = new Thingsclass().GetThings().Where(c => c.chara.ToLower().Equals(character.ToLower()));
foreach (var item in items)
{
var paragraph = new HtmlGenericControl("p");
paragraph.Style.Add("line-height", Lineheight);
var control = _inserteditemCount <= _itemsCount ? leftctr : rightctr;
var title = item.Title;
var description = item.Description;
var link = new HyperLink { Text = title };
paragraph.Controls.Add(link);
var lblDescription = new Label { Text = string.Format(" - {0}", description) };
paragraph.Controls.Add(lblDescription);
_inserteditemCount++;
control.Controls.Add(paragraph);
}
}