I'm using IText 7 in dotnet core to manipulate a PDF file.
Once the process has completed I'm finding myself with a 5-page PDF at 14.2MB.
When I open the generated PDF in Adobe Reader I am prompted to save when closing Reader. If i choose Yes it shrinks the pdf by 50%.
Here is the pertinent code:
// ASDLine Model
public class ASDLine
{
public long ARInvoiceHeaderKey { get; set; }
public string BillingCode { get; set; }
public string Description { get; set; }
public decimal Amount { get; set; }
public bool? MatchProfile { get; set; }
}
public AuditSummaryDocument Create()
{
// Load in the template
MemoryStream outputDoc = new MemoryStream();
// Attempt to shrink the PDF..(didn't make any difference)
var w = new PdfWriter(outputDoc);
var wb = w.IsFullCompression();
w.SetCompressionLevel(9);
w.SetSmartMode(true);
PdfDocument pdfDoc = new PdfDocument(new PdfReader("mytemplate.pdf"), w);
font = PdfFontFactory.CreateRegisteredFont("Calibri");
Document doc = new Document(pdfDoc);
PdfAcroForm pdfAcroForm = PdfAcroForm.GetAcroForm(pdfDoc, true);
var fields = pdfAcroForm.GetFormFields();
//header:
fields["generated"].SetValue($"{DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()}");
// .... lots of fields
pdfAcroForm.FlattenFields();
// Add lines
Table table = GenerateTable();
table.SetRelativePosition(0, 510, 0, 0);
doc.Add(table);
// attempt to shrink PDF
pdfDoc.SetFlushUnusedObjects(true);
pdfDoc.Close();
//outputDoc.Position = 0;
var r = new AuditSummaryDocument
{
Data = outputDoc.ToArray()
};
return r;
}
The table looks like this:
private Table GenerateTable()
{
// Example list of ASDLine
var list = new List<ASDLine>();
list.Add(new ASDLine { Amount = 20, ARInvoiceHeaderKey = 1, BillingCode = "ABC123", Description = "A billing code 1", MatchProfile = null });
list.Add(new ASDLine { Amount = 40, ARInvoiceHeaderKey = 1, BillingCode = "ABC456", Description = "A billing code 2", MatchProfile = true });
list.Add(new ASDLine { Amount = 60, ARInvoiceHeaderKey = 1, BillingCode = "ABC789", Description = "A billing code 3", MatchProfile = false });
list.Add(new ASDLine { Amount = 80, ARInvoiceHeaderKey = 1, BillingCode = "ABC987", Description = "A billing code 4", MatchProfile = true });
Table table = new Table(new UnitValue[] { new UnitValue(UnitValue.POINT, 80)
,new UnitValue(UnitValue.POINT, 370)
,new UnitValue(UnitValue.POINT, 80)
,new UnitValue(UnitValue.POINT, 80)});
table.SetWidth(new UnitValue(UnitValue.PERCENT, 102));
Style hs = new Style();
hs.SetFont(font);
hs.SetFontSize(10);
hs.SetBold();
hs.SetTextAlignment(TextAlignment.CENTER);
Style cs = new Style();
cs.SetFont(font);
cs.SetFontSize(10);
table.AddHeaderCell(new Cell().AddStyle(hs).Add(new Paragraph("Billing Code")));
table.AddHeaderCell(new Cell().AddStyle(hs).Add(new Paragraph("Description")));
table.AddHeaderCell(new Cell().AddStyle(hs).Add(new Paragraph("Amount")));
table.AddHeaderCell(new Cell().AddStyle(hs).Add(new Paragraph("Match Profile")));
list.ForEach(line =>
{
table.AddCell(new Cell().AddStyle(cs).SetTextAlignment(TextAlignment.CENTER).Add(new Paragraph(line.BillingCode)));
table.AddCell(new Cell().AddStyle(cs).SetTextAlignment(TextAlignment.LEFT).Add(new Paragraph(line.Description)));
table.AddCell(new Cell().AddStyle(cs).SetTextAlignment(TextAlignment.RIGHT).Add(new Paragraph(line.Amount.ToString("C"))));
table.AddCell(new Cell().AddStyle(cs).SetTextAlignment(TextAlignment.CENTER).Add(new Paragraph(line.MatchProfile.HasValue ? line.MatchProfile.Value ? "Yes" : "No" : "-")));
});
return table;
}
What am I doing wrong with my code that results in this PDF being twice the size it should be - and why does Adobe prompt to save changes?
Related
This is my grid in code behind:
GridPanel grid = new GridPanel
{
Height = 200,
EnableColumnHide = false,
EmptyText = "No Documents",
Store =
{
new Store
{
Model =
{
new Model
{
Fields = { new ModelField("Name") }
}
},
DataSource = data
}
},
ColumnModel =
{
Columns =
{
new Column { ID = "clmDoc", Text = "Documents Exported", DataIndex = "Name", Width = 400 }
}
}
};
How I can show a url to the file in each row so the user can download the file? Files are stored in ~/Files/Export/Documents/file1.pdf etc
Declare your column like this:
new Column()
{
Text = "MyColumn",
DataIndex = "UrlField",
Width = 200,
Renderer = new Renderer("function (value, metadata, record) { return '<a href=\"http://www.mylink.com\" target=top >'+value+'<a/>'} ")
}
I have created one dialog to evaluate our company consultant in our chatbot (C# botframework), but I can't use for or foreach inside of AdaptiveCard. I need to create one TextBlock() and one ChoiceSet() block for each item from List Pergunta, but I can't do it with foreach. Any suggestion or better idea to create this loop ?
Here is my complete dialog code in C#.
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using System.Collections.Generic;
using AdaptiveCards;
using SimpleEchoBot.Formulário;
namespace SimpleEchoBot.Dialogs
{
[Serializable]
public class RatingDialog : IDialog<EntidadeCliente>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(this.MessageReceivedAsync);
return Task.CompletedTask;
}
public async virtual Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var replyMessage = context.MakeMessage();
Attachment attachment = null;
attachment = CriarAdapativecard();
replyMessage.Attachments = new List<Attachment> { attachment };
await context.PostAsync(replyMessage);
}
public Attachment CriarAdapativecard()
{
AdaptiveCard card = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new Container()
{
Items = new List<CardElement>()
{
new ColumnSet()
{
Columns = new List<Column>()
{
new Column()
{
Size = ColumnSize.Auto,
Items = new List<CardElement>()
{
new Image()
{
Url = "D:/EvaWeb.png",
Size = ImageSize.Medium,
Style = ImageStyle.Person
}
}
},
new Column()
{
Size = ColumnSize.Auto,
Items = new List<CardElement>()
{
new TextBlock()
{
Text = "Olá, temos uma novidade!",
Weight = TextWeight.Bolder,
IsSubtle = true
},
new TextBlock()
{
Text = "Gostaria de iniciar a avaliação do consultor?",
Wrap = true
}
}
}
}
}
}
}
},
// Buttons
Actions = new List<ActionBase>()
{
new ShowCardAction()
{
Title = "Sim",
Speak = "<s>Sim</s>",
Card = AvaliarConsultor("")
},
new ShowCardAction()
{
Title = "Não",
Speak = "<s>Não</s>",
Card = PularAvaliacao()
},
}
};
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card
};
return attachment;
}
private static AdaptiveCard PularAvaliacao()
{
//does nothing on No option from user
return new AdaptiveCard()
{ };
}
private static AdaptiveCard AvaliarConsultor(List<string> Pergunta)
{
var adaptiveCard = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new TextBlock()
{
Text = "Avaliação de questionário 1!",
Weight = TextWeight.Bolder,
Size = TextSize.Large
},
new TextBlock() { Text = Pergunta[0]},
new ChoiceSet()
{
Id = "nota",
Style = ChoiceInputStyle.Compact,
IsMultiSelect = false,
Choices = new List<Choice>()
{
new Choice()
{
Title = "⭐",
Value = "1"
},
new Choice()
{
Title = "⭐⭐",
Value = "2"
},
new Choice()
{
Title = "⭐⭐⭐",
Value = "3"
},
new Choice()
{
Title = "⭐⭐⭐⭐",
Value = "4"
},
new Choice()
{
Title = "⭐⭐⭐⭐⭐",
Value = "5"
}
}
}
},
Actions = new List<ActionBase>()
{
new SubmitAction()
{
Title = "Registrar",
Speak = "<s>Registrar avaliação</s>",
DataJson = "{ \"Type\": \"Registrar\" }"
}
}
};
return adaptiveCard;
}
}
}
I need to create one TextBlock() and one ChoiceSet() block for each item from List Pergunta, but I can't do it with foreach.
To achieve your requirement, please refer to the following code snippet.
private static AdaptiveCard AvaliarConsultor(List<string> Pergunta)
{
var adaptiveCard = new AdaptiveCard()
{
Body = new List<CardElement>()
{
new TextBlock()
{
Text = "Avaliação de questionário 1!",
Weight = TextWeight.Bolder,
Size = TextSize.Large
},
},
Actions = new List<ActionBase>()
{
new SubmitAction()
{
Title = "Registrar",
Speak = "<s>Registrar avaliação</s>",
DataJson = "{ \"Type\": \"Registrar\" }"
}
}
};
//dynamically generate TextBlock and ChoiceSet
//and then add to AdaptiveCard Body
foreach (var item in Pergunta)
{
var textblock = new TextBlock() { Text = item };
var choiceset = new ChoiceSet()
{
Id = "nota",
Style = ChoiceInputStyle.Compact,
IsMultiSelect = false,
Choices = new List<Choice>()
{
new Choice()
{
Title = "⭐",
Value = "1"
},
new Choice()
{
Title = "⭐⭐",
Value = "2"
},
new Choice()
{
Title = "⭐⭐⭐",
Value = "3"
},
new Choice()
{
Title = "⭐⭐⭐⭐",
Value = "4"
},
new Choice()
{
Title = "⭐⭐⭐⭐⭐",
Value = "5"
}
}
};
adaptiveCard.Body.Add(textblock);
adaptiveCard.Body.Add(choiceset);
}
return adaptiveCard;
}
Test result:
I need to create a Table Of Contents with page numbers, but I don't know how. Next format:
heading1 ----------------page number
subHeading1---------------page number
subHeading2---------------page number
heading2-----------------page number
I read a few articles and didn't understand. In particular, I mean this article, where "Named destinations" and "GoTo actions" I think it is useful for me, but I don't know how to use it in iTextSharp.
In my code, I have got a few "Chapter" and "Section", and I want to take it and create a TOC. I've understood that I need to use PdfPageEventHelper and OnChapter.
You've probably implemented this yourself by name, but I made a small example myself for the sake of completeness.
Please take a look at the CreateTOC example. It creates a PDF with some random text:
You can clearly see the titles and the content under the titles. After we have added all our content, we start a new page, and we add a table of contents:
The table of contents is composed by a series of key-value pairs, where the key is the title and the value is the page number. We create this list in a page event:
public class TOCEvent extends PdfPageEventHelper {
protected List<SimpleEntry<String, Integer>> toc = new ArrayList<>();
#Override
public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) {
toc.add(new SimpleEntry(text, writer.getPageNumber()));
}
public List getTOC() {
return toc;
}
}
We use this page event like this:
public void createPdf(String dest) throws IOException, DocumentException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
TOCEvent event = new TOCEvent();
writer.setPageEvent(event);
document.open();
for (int i = 0; i < 10; i++) {
String title = "This is title " + i;
Chunk c = new Chunk(title, titleFont);
c.setGenericTag(title);
document.add(new Paragraph(c));
for (int j = 0; j < 50; j++) {
document.add(new Paragraph("Line " + j + " of title " + i));
}
}
document.newPage();
document.add(new Paragraph("Table of Contents", titleFont));
Chunk dottedLine = new Chunk(new DottedLineSeparator());
List<SimpleEntry<String, Integer>> entries = event.getTOC();
Paragraph p;
for (SimpleEntry<String, Integer> entry : entries) {
p = new Paragraph(entry.getKey());
p.add(dottedLine);
p.add(String.valueOf(entry.getValue()));
document.add(p);
}
document.close();
}
First we create an instance of the event and we declare it to the writer:
TOCEvent event = new TOCEvent();
writer.setPageEvent(event);
We mark the titles using setGenericTag():
String title = "This is title " + i;
Chunk c = new Chunk(title, titleFont);
c.setGenericTag(title);
document.add(new Paragraph(c));
Once we've finished adding the content, we get all the entries:
List<SimpleEntry<String, Integer>> entries = event.getTOC();
We loop over this list and compose a Paragraph for every entry:
for (SimpleEntry<String, Integer> entry : entries) {
p = new Paragraph(entry.getKey());
p.add(dottedLine);
p.add(String.valueOf(entry.getValue()));
document.add(p);
}
No one can argue that this was difficult. The event class takes less than 10 lines of code. Adding support for subheadings will add a handful of lines, but that shouldn't be difficult too. It's a matter of building a tree structure, and introducing some indentation where necessary.
Thanks for the example, i needed this in C# and with multicolumn, so i rewrote this example as below:
namespace GerarPDF
{
public class GerarPDF
{
public const String DEST = "results/example.pdf";
public GerarPDF()
{
FileInfo file = new FileInfo(String.Concat(AppDomain.CurrentDomain.BaseDirectory, #"/", DEST));
file.Directory.Create();
this.createPdf(file.FullName);
}
public void createPdf(String dest)
{
FileStream fs = new FileStream(dest, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
Document document = new Document(PageSize.LETTER);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
TOCEvent evento = new TOCEvent();
writer.PageEvent = evento;
for (int i = 0; i < 10; i++)
{
String title = "This is title " + i;
Chunk c = new Chunk(title, new Font());
c.SetGenericTag(title);
document.Add(new Paragraph(c));
for (int j = 0; j < 50; j++)
{
document.Add(new Paragraph("Line " + j + " of title " + i + " page: " + writer.PageNumber));
}
}
document.NewPage();
document.Add(new Paragraph("Table of Contents", new Font()));
Chunk dottedLine = new Chunk(new DottedLineSeparator());
List<PageIndex> entries = evento.getTOC();
MultiColumnText columns = new MultiColumnText();
columns.AddRegularColumns(72, 72 * 7.5f, 24, 2);
Paragraph p;
for (int i = 0; i < 10; i++)
{
foreach (PageIndex pageIndex in entries)
{
Chunk chunk = new Chunk(pageIndex.Text);
chunk.SetAction(PdfAction.GotoLocalPage(pageIndex.Name, false));
p = new Paragraph(chunk);
p.Add(dottedLine);
chunk = new Chunk(pageIndex.Page.ToString());
chunk.SetAction(PdfAction.GotoLocalPage(pageIndex.Name, false));
p.Add(chunk);
columns.AddElement(p);
}
}
document.Add(columns);
document.Close();
}
public class TOCEvent : PdfPageEventHelper
{
protected int counter = 0;
protected List<PageIndex> toc = new List<PageIndex>();
public override void OnGenericTag(PdfWriter writer, Document document, Rectangle rect, string text)
{
String name = "dest" + (counter++);
int page = writer.PageNumber;
toc.Add(new PageIndex() { Text = text, Name = name, Page = page });
writer.DirectContent.LocalDestination(name, new PdfDestination(PdfDestination.FITH, rect.GetTop(0)));
}
public List<PageIndex> getTOC()
{
return toc;
}
}
}
public class PageIndex
{
public string Text { get; set; }
public string Name { get; set; }
public int Page { get; set; }
}
}
this snipplet can it be recursive, the basic concept is:
List<PdfPCell> celdas = new List<PdfPCell>();
string urlSection=String.empty;
var estatus = new Phrase();
estatus.Leading = 25;
if (streams != null && streams.Any())
primero = streams.FirstOrDefault(x => x.Id == enlace.Id);
if (primero != null)
urlSection = primero.UrlSection;
//For the code and generate hyperlink:
Chunk espacioTab = new Chunk(" " + enlace.Name, baseFontBig );
espacioTab = Visor.Servicios.GeneracionPDF.PDFUtils.GenerarVinculo(" " + enlace.Name, urlSection, baseFontBig);
estatus.Add(espacioTab);
if (incluirPaginado)
{
if (primero != null)
actualPage = primero.TotalPages;
else
actualPage = 0;
///This is important, generate dots like "...." to chunk end
estatus.Add(new Chunk(new iTextSharp.text.pdf.draw.DottedLineSeparator()));
var linkPagina = new Chunk(actualPage.ToString());
linkPagina = Visor.Servicios.GeneracionPDF.PDFUtils.GenerarVinculo(actualPage.ToString(), urlSection, baseFontBig );
estatus.Add(linkPagina);
resultado.paginaFinal = actualPage;
}
//This is for add to your cell or table
PdfPCell rightCell = new PdfPCell()
{
Border = PdfPCell.NO_BORDER,
Colspan = 3,
PaddingLeft = espacioInicial.Length,
ExtraParagraphSpace = 10,
};
rightCell.AddElement(estatus);
celdas.Add(rightCell);
And create a new method, this create a hyperlinkand you can invoque when you want
/*Generar Vinculo (create hyperlink)**/
public static Chunk GenerarVinculo(String tituloMostrar, string urlDirecion, iTextSharp.text.Font fuente)
{
Chunk espacioTab = new Chunk();
try
{
if (String.IsNullOrEmpty(urlDirecion))
urlDirecion = "Indice de Contenido";
espacioTab = new Chunk(tituloMostrar, fuente);
var accion = PdfAction.GotoLocalPage(urlDirecion, false);
espacioTab.SetAction(accion);
}
catch (Exception error) { }
return espacioTab;
}
Hope helps someone
I was able to successfully add a document to a template via the API and now I would like to add multiple signature tabs and initial tabs to the document that I just added via the API using the .net c# library. I created the tabs below and created the envelope and added the document. Do I have to add the tabs to the document or the template or something else?
//Create tabs to be added to do the document.
Tabs tabList = new Tabs()
{
signHereTabs = new Tab[]
{
new Tab()
{
documentId = 5,
pageNumber = 1,
xPosition = 100,
yPosition = 150
}
},
initialHereTabs = new Tab[]
{
new Tab(){
documentId = 5,
pageNumber = 1,
xPosition = 100,
yPosition = 150
}
}
};
//Create template envolope and its template role
byte[] ips = GetIPS("");
RestSettings.Instance.DocuSignAddress = "https://demo.docusign.net";
RestSettings.Instance.WebServiceUrl = RestSettings.Instance.DocuSignAddress + "/restapi/v2";
RestSettings.Instance.IntegratorKey = integratorKey;
DocuSign.Integrations.Client.Account account = new DocuSign.Integrations.Client.Account();
account.Email = username;
account.Password = password;
var loginResult = account.Login();
Template template = new Template();
template.TemplateId = templateId;
template.Login = account;
template.EmailSubject = emailSubject;
template.EmailBlurb = emailMessage;
var documents = template.GetDocuments();
var roles = new List<TemplateRole>();
//Handle Primary Client
roles.Add(new TemplateRole
{
roleName = "Primary Client",
name = primaryClientName,
email = primaryClientEmail,
tabs = new RoleTabs
{
textTabs = new RoleTextTab[] {
new RoleTextTab {
tabLabel = "FeeEffectiveDate",
value = effectiveDate
},
new RoleTextTab {
tabLabel = "FeePercentage",
value = fee
}
},
},
});
if (secondaryClientName.Trim().Length != 0)
{
roles.Add(new TemplateRole
{
roleName = "Secondary Client",
name = secondaryClientName,
email = secondaryClientEmail,
});
}
roles.Add(new TemplateRole
{
roleName = "President",
name = presidentName,
email = presidentEmail
});
roles.Add(new TemplateRole
{
roleName = "Css",
name = cssName,
email = cssEmail
});
template.TemplateRoles = roles.ToArray<TemplateRole>();
template.Status = "created";
var result = template.Create();
//Add the document to the template
bool status = template.AddDocument(ips, "IPS.pdf", 5);
//-------------------------------------------------------------------------
//Now I need to add tabslist created previously to the document. How do I do this?
//--------------------------------------------------------------------------
template.Status = "sent";
result = template.UpdateStatus();
return result;
I was able to do this after several trial and error. The Tag/Tabs needs to be added to the template using its AddTabs function.
TabCollection ipsTabs = new TabCollection();
ipsTabs.signHereTabs = new List<Tab>();
ipsTabs.signHereTabs.Add(new Tab()
{
documentId = 5,
anchorString = "soc1",
recipientId = "1",
xPosition = 100,
yPosition = 150
});
ipsTabs.signHereTabs.Add(new Tab()
{
documentId = 5,
anchorString = "soc2",
recipientId = "2",
xPosition = 100,
yPosition = 150
});
ipsTabs.initialHereTabs = new List<Tab>();
ipsTabs.initialHereTabs.Add(new Tab()
{
documentId = 5,
anchorString = "ci1",
recipientId = "1",
xPosition = -100,
yPosition = 150
});
ipsTabs.initialHereTabs.Add(new Tab()
{
documentId = 5,
anchorString = "ci2",
recipientId = "2",
xPosition = -100,
yPosition = 150
});
var tabstatus = template.AddTabs(ipsTabs);
I have a winform with a button and a datagridview.
The scenario is:
I have a winform:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CreateItems();
}
private List<Store> stores = new List<Store>();
public void CreateItems()
{
Store newStore1 = new Store();
newStore1.StoreName = "My Store 1";
newStore1.City = "My City 1";
newStore1.PlannedSales = 10;
newStore1.RealizedSales = 5;
newStore1.SalesDate = new DateTime(2012, 01, 12, 12, 30, 54, DateTimeKind.Unspecified);
Store newStore2 = new Store();
newStore2.StoreName = "My Store 2";
newStore2.City = "My City 2";
newStore2.PlannedSales = 200000;
newStore2.RealizedSales = 250000;
newStore2.SalesDate = new DateTime(2012, 04, 12, 12, 30, 54, DateTimeKind.Unspecified);
Store newStore3 = new Store();
newStore3.StoreName = "My Store 3";
newStore3.City = "My City 3";
newStore3.PlannedSales = 100000;
newStore3.RealizedSales = 10000;
newStore3.SalesDate = new DateTime(2012, 05, 12, 12, 30, 54, DateTimeKind.Unspecified);
Store newStore4 = new Store();
newStore4.StoreName = "My Store 1";
newStore4.City = "My City 1";
newStore4.PlannedSales = 20;
newStore4.RealizedSales = 10;
newStore4.SalesDate = new DateTime(2012, 02, 12, 12, 30, 54, DateTimeKind.Unspecified);
Store newStore5 = new Store();
newStore5.StoreName = "My Store 1";
newStore5.City = "My City 1";
newStore5.PlannedSales = 30;
newStore5.RealizedSales = 20;
newStore5.SalesDate = new DateTime(2012, 03, 12, 12, 30, 54, DateTimeKind.Unspecified);
stores.Add(newStore1);
stores.Add(newStore2);
stores.Add(newStore3);
stores.Add(newStore4);
stores.Add(newStore5);
}
private void btnQuery_Click(object sender, EventArgs e)
{
var query1 = stores.GroupBy(x => new
{
x.City,
x.StoreName,
x.SalesDate.Year,
x.SalesDate.Month
}).Select(group => new
{
Planned = group.Sum(x => x.PlannedSales),
Realised = group.Sum(x => x.RealizedSales),
Count = group.Count(),
City = group.Key.City,
StoreName = group.Key.StoreName,
Year = group.Key.Year,
Month = group.Key.Month
}).OrderBy(x => x.Year).ThenBy(x => x.Month).Where(x => x.StoreName.Equals("My Store 1"));
List<Store> total = new List<Store>();
foreach (var value in query1)
{
Store newStore = new Store()
{
MonthYear = value.Month.ToString() + " - " + value.Year.ToString(),
RealizedSales = value.Realised,
PlannedSales = value.Planned
};
total.Add(newStore);
};
var query2 = total.Select((s, i) => new
{
MonthYear = s.MonthYear,
RealizedSales = s.RealizedSales + total.Take(i).Sum(sa => sa.RealizedSales),
PlannedSales = s.PlannedSales + total.Take(i).Sum(sa => sa.PlannedSales)
});
List<Store> totalFinal = new List<Store>();
foreach (var value in query2)
{
Store newStore = new Store()
{
MonthYear = value.MonthYear,
RealizedSales = value.RealizedSales,
PlannedSales = value.PlannedSales
};
totalFinal.Add(newStore);
};
dataGridView1.DataSource = totalFinal;
}
}
public class Store
{
public string StoreName { get; set; }
public string City { get; set; }
public int PlannedSales { get; set; }
public int RealizedSales { get; set; }
public DateTime SalesDate { get; set; }
public string MonthYear { get; set; }
}
}
When the form is instantiated, the method CreateItems is called. This method will populate a List with some demo Store data.
When the button from the form is pressed, than the List stores is queried with Linq in a way that will return all the PlannedSales and RealizedSales per MonthYear from my List stores WHERE the StoreName = My Store 1
An example of the query result is here: http://i49.tinypic.com/vz1c6.jpg
Any idea how to optimize this query to make it more simpler but get the same result?
Basically I need to return all planned and realized sales per month-year for a specific store name only!
Thanks!
This should be as easy as:
var totalPlanned = 0;
var totalRealized = 0;
var result = stores.Where(s => s.StoreName.Equals("My Store 1"))
.Select(s => {
totalPlanned += s.PlannedSales;
totalRealized += s.RealizedSales;
return new Store(){
MonthYear = s.SalesDate.ToString("MM - yyyy"),
PlannedSales = totalPlanned,
RealizedSales = totalRealized
};
});
Live example showing this new code alongside your original to show same result: http://rextester.com/PAF27531
from s in stores
where s.StoreName == "My Store 1"
group s by new { s.StoreName, s.SalesDate.Year, s.SalesDate.Month } into g
select new
{
g.Key.StoreName,
MonthYear = g.Key.Year.ToString() + " - " + g.Key.Month.ToString(),
Planned = g.Sum(st => st.PlannedSales),
Realized = g.Sum(st => st.RealizedSales)
}