I have a PDFP Table and I want to have it laid out like so:
Item1 ............ $10.00
Item1123123 ...... $50.00
Item3 ............ $75.00
This is what I have so far:
var tableFont = FontFactory.GetFont(FontFactory.HELVETICA, 7);
var items = from p in ctx.quote_server_totals
where p.item_id == id
&& p.name != "total"
&& p.type != "totals"
select p;
foreach (var innerItem in items)
{
detailsTable.AddCell(new Phrase(innerItem.type == "discount" ? "ADJUSTMENT -" + innerItem.name : innerItem.name, tableFont));
detailsTable.AddCell(new Phrase(".......................................................", tableFont));
detailsTable.AddCell(new Phrase(Convert.ToDecimal(innerItem.value).ToString("c"), tableFont));
}
document.Add(detailsTable);
As can see, the only way I've been able to get the dots to extend is by manually entering them; however, this obviously won't work because the the first column's width will be different every time I run this code. Is there a way I can accomplish this? Thanks.
Please download chapter 2 of my book and search for DottedLineSeparator. This separator class will draw a dotted line between two parts of a Paragraph (as shown in the figures in the book). You can find the C# version of the Java book samples here.
If you can use a fixed-width font such as FontFactory.COURIER your task will be a lot easier.
//Our main font
var tableFont = FontFactory.GetFont(FontFactory.COURIER, 20);
//Will hold the shortname from the database
string itemShortName;
//Will hold the long name which includes the periods
string itemNameFull;
//Maximum number of characters that will fit into the cell
int maxLineLength = 23;
//Our table
var t = new PdfPTable(new float[] { 75, 25 });
for (var i = 1; i < 10000; i+=100) {
//Get our item name from "the database"
itemShortName = "Item " + i.ToString();
//Add dots based on the length
itemNameFull = itemShortName + ' ' + new String('.', maxLineLength - itemShortName.Length + 1);
//Add the two cells
t.AddCell(new PdfPCell(new Phrase(itemNameFull, tableFont)) { Border = PdfPCell.NO_BORDER });
t.AddCell(new PdfPCell(new Phrase(25.ToString("c"), tableFont)) { Border = PdfPCell.NO_BORDER });
}
Related
In a categorical axis, I have labels for each datum.
The strings are sometimes too long so they overlap with other strings.
There are other elements in oxyplot that can be clipped, e.g. axisTitle, Title, etc.
I would like to clip the axis labels depending on the available space between labels.
This is somehow what I would like to achieve:
LabelFormatter = a => a.Length > 10 ? a.Substring(0,9) : a
But LabelFormatter only works for numbers, not strings.
Do you have an approach I could do this so labels are trimmed in the axis, but they still appear complete in the tracker tooltip?
If you wanted to replicate the problem I have, you can:
Open Visual Studio
Load oxyplot.WPF.sln (previously downloaded from github, master branch)
Locate Examples \ Example Browser \ CategoryAxisExamples.cs
Modify line 34 and replace "A" with "Very long item A", etc.
You can now run the Example Browser application and find the example at Category Axis -> ItemsSource - string[] example
public static PlotModel ItemsSourceStrings()
{
var model = new PlotModel { Title = "CategoryAxis with string[] as ItemsSource" };
model.Axes.Add(new CategoryAxis
{
StringFormat = "Very long item {0}",
ItemsSource = new[] { "A", "B", "C" }
});
var linearAxis = new LinearAxis { Position = AxisPosition.Left };
model.Axes.Add(linearAxis);
return model;
}
I was able to get your concept working for the LabelFormatter with this code. The number passed into the label formatter is the category index.
var categoryAxis = new CategoryAxis
{
StringFormat = "Very long item {0}",
ItemsSource = new[] { "A", "B", "C" }
};
categoryAxis.LabelFormatter = categoryIndex =>
{
var actualLabel = categoryAxis.ActualLabels[(int)categoryIndex];
return actualLabel.Length > 10 ? $"{actualLabel.Substring(0, 9)}...{actualLabel.Last()}" : actualLabel;
};
But unfortunately, it looks like the label formatter is incorporated into the text that is used for the tracker text
If you are OK with extra lines in your labels, you could have a label formatter that adds lines at the end of words depending on the current length of the line. Example code shown below
var categoryAxis = new CategoryAxis
{
StringFormat = "Very long item {0}",
ItemsSource = new[] { "A", "B", "C" }
};
categoryAxis.LabelFormatter = categoryIndex =>
{
var actualLabel = categoryAxis.ActualLabels[(int)categoryIndex];
var spaceMatches = Regex.Matches(actualLabel, " ");
var currentLineStartIndex = 0;
var formattedLabel = actualLabel;
foreach(Match space in spaceMatches)
{
//you can change the length of the current line allowed before inserting a new line.
if (space.Index - currentLineStartIndex > 5)
{
//start counting the length of the next line starting from the current new line character
currentLineStartIndex = space.Index;
//replace the space with a new line character
formattedLabel = formattedLabel.Remove(space.Index, 1);
formattedLabel = formattedLabel.Insert(space.Index, "\n");
}
}
return formattedLabel;
};
i have added a table with two rows and two columns and set border to all cells. but it shows the border only for first column
Sample code is shown below
var tableestdet = new Table
{
ColumnWidths = "120,120",
Margin = new MarginInfo { Top = 40, Left = 10 },
DefaultCellBorder = new BorderInfo((int)BorderSide.All, 1F),
};
tableestdet.DefaultCellTextInfo = new TextInfo { Alignment = AlignmentType.Center };
var estdet1 = tableestdet.Rows.Add();
estdet1.DefaultRowCellPadding = new MarginInfo { Top = 5, Bottom = 5 };
var req=estdet1.Cells.Add("Requested By:");
var estde=estdet1.Cells.Add("Entered By:");
var estdet2 = tableestdet.Rows.Add();
estdet2.DefaultCellTextInfo = new TextInfo
{
FontSize = 8,
Alignment = AlignmentType.Center
};
estdet2.DefaultRowCellPadding = new MarginInfo { Top = 5, Bottom = 5 };
estdet2.Cells.Add(Requestedby);
estdet2.Cells.Add(CustomerName);
sec1.Paragraphs.Add(tableestdet);
We have noticed that you are working with an outdated version of the API so please upgrade to Aspose.PDF for .NET 19.2 which is latest available version of the API as it includes more features and bug fixes. You may use below code snippet to add a table on page of PDF document, while setting borders for all cells.
// Load source PDF document
Aspose.Pdf.Document document = new Aspose.Pdf.Document();
// Add a page to the document
Page page = document.Pages.Add();
// Initializes a new instance of the Table
Aspose.Pdf.Table table = new Aspose.Pdf.Table();
// Set the table border color as LightGray
table.Border = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, .5f, Aspose.Pdf.Color.FromRgb(System.Drawing.Color.LightGray));
// Set the border for table cells
table.DefaultCellBorder = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, .5f, Aspose.Pdf.Color.FromRgb(System.Drawing.Color.LightGray));
// Create a loop to add 10 rows
for (int row_count = 1; row_count <= 10; row_count++)
{
// Add row to table
Aspose.Pdf.Row row = table.Rows.Add();
// Add table cells
row.Cells.Add("Column (" + row_count + ", 1)");
row.Cells.Add("Column (" + row_count + ", 2)");
}
// Add table object to first page of input document
page.Paragraphs.Add(table);
// Save updated document containing table object
document.Save(dataDir + "Table_19.2.pdf");
Generated PDF document has been attached for your kind reference Table19.2.pdf. Please feel free to let us know if you need any further assistance.
PS: I work with Aspose as Developer Evangelist.
I want to be able to fetch .csv files from a folder and plot them up in a chart.
Currently, I'm just saving the files and displaying an individual curve like this:
RunTest function:
public List<Tuple<double,double>> runTest()
{
_dpg = new Root(this, "english", false);
_dpg.Init();
var filename = "Dpg10Export_" + DateTime.Now.ToString("yyyyMMdd_HHmm") + ".csv";
List<Tuple<double, double>> results = new List<Tuple<double, double>>();
var measurement = new Measurement();
var resultCode = RunMeasurement(60, 1000, 2200, ref measurement, null /* TODO: ref ProgressBar? */);
using (var fileStream = new StreamWriter(filename))
{
var count = measurement.I_inc_Values.Count;
for (var i = 0; i < count; i++)
{
var item = measurement.I_inc_Values[i + 1];
var current = (float)Convert.ToDouble(item);
item = measurement.LI_inc_Values[i + 1];
var inductance = (float)Convert.ToDouble(item);
var line = current.ToString() + ";" + inductance.ToString() + ";";
fileStream.WriteLine(line);
currentList.Add(current);
inductanceList.Add(inductance);
results.Add(new Tuple<double, double>(current,inductance));
if (i == 0 || (i + 1) % 32 == 0)
{
Console.WriteLine((i + 1) + ": " + line);
}
}
}
return results;
}
This code produces a csv-file that looks something like this:
0,22 | 0,44
0,32 | 0,54
0,44 | 0,65
And those values produce a curve that looks like this:
When you click on the "get Waveform" button, the curve above is generated. However, I want to display all the curves that has been generated, and a trendline as well. How would I achieve this?
void BindData(List<Tuple<double,double>> results)
{
chart.Series.Clear();
var series1 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = "curr/induc",
Color = System.Drawing.Color.Green,
IsVisibleInLegend = true,
IsXValueIndexed = true,
ChartType = SeriesChartType.Line
};
foreach (var i in results)
{
series1.Points.AddXY(i.Item2,i.Item1);
}
chart.Invalidate();
chart.Series.Add(series1);
}
private void getWaveformBtn_Click(object sender, EventArgs e)
{
Dpg10Client.Dpg10Settings settings = new Dpg10Client.Dpg10Settings();
Dpg10Instrument hej = new Dpg10Instrument(settings);
List<Tuple<double,double>> results = hej.runTest();
double current, inductance;
foreach(var i in results)
{
current = i.Item1;
inductance = i.Item2;
textBoxWaveformInput.Text += current.ToString() + inductance.ToString();
}
BindData(results);
}
TL;DR
Parse the information from the CSV files to generate curves, and create a trendline based on those files.
Marked as duplicate: That answer is regarding a straight line, these values can fluctuate in a curve.
There are many ways to solve most of the various problems in your question.
Let me tackle only the one that actually has to do with calculating an average from multiple series, i.e. will not deal with creating a Series with data from a CSV file.
There is a built-in class that can do all sorts of advanced math, both financial and statistical, but I didn't find one that will help in creating an average line/curve over more than one series.
So let's do it ourselves..
The first issue is that to calculate averages we need not just data but the data, that is their x-values, must be grouped into 'bins' from which we want to get the averages.
Unless the data already are grouped like that, e.g. because they have one value per series per day, we need to create such groups.
Let's first collect all the points from all series we want to handle; you may need to adapt the loop to include just your set of series..:
var allPoints = new List <DataPoint>();
for (int s = 0; s < 3; s++) // I know I have created these three series
{
Series ser = chartGraphic.Series["S" + (s+1)];
allPoints.AddRange(ser.Points);
}
Next we need to decide on a bin range/size, that is a value that determines which x-values shall fall into the same bin/group. I chose to have 10 bins.
So we best get the total range of the x-values and divide by the number of bins we want..:
double xmax = allPoints.Max(x => x.XValue);
double xmin = allPoints.Min(x => x.XValue);
int bins = 10;
double groupSize = (xmax - xmin) / (bins - 1);
Next we do the actual math; for this we order our points, group them and select the average for each grouped set..:
var grouped = allPoints
.OrderBy(x => x.XValue)
.GroupBy(x => groupSize * (int)(x.XValue /groupSize ))
.Select(x => new { xval = x.Key, yavg = x.Average(y => y.YValues[0]) })
.ToList();
Now we can add them to a new series to display the averages in the bins:
foreach (var kv in grouped) avgSeries.Points.AddXY(kv.xval + groupSize/2f, kv.yavg);
I center the average point in the bins.
Here is an example:
A few notes on the example:
The average line doesn't show a real 'trend' because my data are pretty much random.
I have added Markers to all series to make the DataPoints stand out from the lines. Here it is how I did it for the averages series:
avgSeries.MarkerStyle = MarkerStyle.Cross;
avgSeries.MarkerSize = 7;
avgSeries.BorderWidth = 2;
I have added a StripLine to show the bins. Here is how:
StripLine sl = new StripLine();
sl.BackColor = Color.FromArgb(44,155,155,222);
sl.StripWidth = groupSize;
sl.Interval = groupSize * 2;
sl.IntervalOffset = chartGraphic.ChartAreas[0].AxisX.IntervalOffset;
chartGraphic.ChartAreas[0].AxisX.StripLines.Add(sl);
I have also set one point to have a really low value of -300 to demonstrate how this will pull down the average in its bin. To keep the chart still nicely centered on the normal range of data I have added a ScaleBreakStyle to the y-axis:
chartGraphic.ChartAreas[0].AxisY.ScaleBreakStyle.BreakLineStyle = BreakLineStyle.Wave;
chartGraphic.ChartAreas[0].AxisY.ScaleBreakStyle.Enabled = true;
GloryDays.Add(new Songs("Shout Out to My Ex", 200));
GloryDays.Add(new Songs("Touch", 161));
GloryDays.Add(new Songs("Power", 177));
GloryDays.Add(new Songs("Your Love", 210));
GloryDays.Add(new Songs("Nobody Like You", 180));
var result = GloryDays.Sum(S => S.TrackLength);
trackLengthtextBox.Text = result.ToString();
List<Songs> GloryDays = new List<Songs>();
There is more than one in class called songs because this is the class where name of the track and its length will get stored.
I can view the info in the textbox for once record only and I would like to find a method which allows me to cycle through each songs list.
public void DisplayAlbum(int thisAlbum, int thisSong )
{
if (Albums[thisAlbum] != null)
{
albumIDtextBox.Text = Albums[thisAlbum].AlbumID.ToString();
albumNametextBox.Text = Albums[thisAlbum].AlbumName.ToString();
releaseDatetextBox.Text = Albums[thisAlbum].ReleaseDate;
albumLabeltextBox.Text = Albums[thisAlbum].Label.ToString();
trackListbox.DataSource = Albums[thisAlbum].Songs.ToList();
trackLengthtextBox.Text = GloryDays.Sum(S => S.Tracklength).ToString();
}
You cannot display on the same textbox all the song items, inorder to display you need to have different text boxes, anyway you can use for loop and display in a format you want,
sample,
foreach (var song in GloryDays) {
MessageBox.Show("song is " + song.TrackName + "length is" + song.TrackLength);
}
var productNameTags = document.DocumentNode.SelectNodes(textBox3.Text);
var priceTags = document.DocumentNode.SelectNodes(textBox2.Text);
var codeNameAndPrice = productNameTags.Zip(priceTags, (n, w) => new { productNameTags = n, priceTags = w });
int counter = 1;
if (codeNameAndPrice != null)
{
foreach (var nw in codeNameAndPrice)
{
label1.Visible = true;
label1.Text += counter + ". " + nw.productNameTags.InnerHtml + " - " + nw.priceTags.InnerHtml + "\n";
}
}
I have this code which looks at html tags and prints out a product name and a price from a website and prints out like this using .Zip:
Baseball - £5.00
Football - £10.00
Toy Car - £15.00
Is there a simple way of adding three or more variables to zip together using a different method?
e.g.
Baseball - £5.00 - 1123
Football - £10.00 - 1124
Toy Car - £15.00 - 1125
Thanks in advance!
I don't think you can do it easily with .Zip (bar multiple levels of zip?), but you could do it dynamically:
var names = new[] { "Baseball", "Football", "Toy Car" };
var prices = new[] { 5.0M, 10.0M, 15.0M };
var ids = new[] { 1123, 1124, 1125 };
var products = names
.Select((name, index) => new { Name = name, Price = prices[index], Id = ids[index] });
No.
You can chain Zip calls, or you can write (or find) extension/helper method that would walk multiple sources in parallel similar to Zip to provide functionality you are looking for.
In your particular case consider iterating over some parent nodes and selecting individual child nodes relative to parent (assuming all nodes for particular item are children of single "parent" node).