我能夠將完整的gridview匯出為pdf,但我不明白如何定位特定行并在單擊按鈕時使用itextsharp將其匯出為pdf
這是我下面用于匯出到 pdf 的代碼,我可以在其中匯出完整的 gridview
private void gvSamplereports_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == gvSamplereports.Columns["btnPDFsingle"].Index)
{
DateTime PrintTime = DateTime.Now;
if (gvSamplereports.Rows.Count > 0)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "PDF (*.pdf)|*.pdf";
sfd.FileName = "SampleDataReports_" PrintTime.ToShortDateString() ".pdf";
bool fileError = false;
if (sfd.ShowDialog() == DialogResult.OK)
{
if (File.Exists(sfd.FileName))
{
try
{
File.Delete(sfd.FileName);
}
catch (IOException ex)
{
fileError = true;
MessageBox.Show("It wasn't possible to write the data to the disk." ex.Message);
}
}
if (!fileError)
{
try
{
PdfPTable pdfTable = new PdfPTable(gvSamplereports.Columns.Count);
pdfTable.DefaultCell.Padding = 3;
pdfTable.WidthPercentage = 100;
pdfTable.HorizontalAlignment = Element.ALIGN_CENTER;
//Below line is to add the header column name on each page of pdf
pdfTable.HeaderRows = 1;
foreach (DataGridViewColumn column in gvSamplereports.Columns)
{
Font fon = FontFactory.GetFont("ARIAL", 6);
fon.SetStyle(1);
PdfPCell cell = new PdfPCell(new Phrase(column.HeaderText, fon));
cell.HorizontalAlignment = Element.ALIGN_CENTER;
pdfTable.AddCell(cell);
}
foreach (DataGridViewRow row in gvSamplereports.Rows)
{
foreach (DataGridViewCell cell in row.Cells)
{
Font fon = FontFactory.GetFont("ARIAL", 6);
PdfPCell cell2 = new PdfPCell(new Phrase(cell.Value?.ToString(), fon));
cell2.HorizontalAlignment = Element.ALIGN_CENTER;
pdfTable.AddCell(cell2);
//pdfTable.AddCell(cell.Value.ToString());
}
}
using (FileStream stream = new FileStream(sfd.FileName, FileMode.Create))
{
Document pdfDoc = new Document(PageSize.A4, 30f, 30f, 100f, 50f);
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, stream);
//PDFFooter is class created for adding header and footer in the pdf
writer.PageEvent = new PDFFooter();
pdfDoc.Open();
pdfDoc.Add(pdfTable);
pdfDoc.Close();
stream.Close();
}
MessageBox.Show("Data Exported Successfully !!!", "Info");
}
catch (Exception ex)
{
MessageBox.Show("Error :" ex.Message);
}
}
}
}
else
{
MessageBox.Show("No Record To Export !!!", "Info");
}
}
}
我添加了圖片以供參考,單擊按鈕后,我想在 c# winform 中使用 Itextsharp 在 pdf 中匯出帶有標題列名稱的單行,以 pdf 格式匯出的資料應如下圖所示
uj5u.com熱心網友回復:
將資料與顯示方式分開
在現代編程中,傾向于將資料(=模型)與與操作員通信的方式(=視圖)分開。這樣做的好處是,如果您決定以不同的方式顯示模型,則可以重用模型,例如,如果您想將資料顯示為圖形而不是表格。
要將模型與視圖匹配,需要一個配接器類。這個配接器類通常稱為 Viewmodel。這三個類一起縮寫為 MVVM。考慮閱讀一些關于此的背景資訊。
在使用 Winforms 和 DataGridView 時,人們傾向于直接使用 Rows 和 Cells,而不是將資料與其顯示方式分開。很多時候,這會導致很多問題。除此之外,你不能在沒有表單的情況下對資料進行單元測驗,你不能在其他表單中重用資料,也不能在不更改 DataGridView 的情況下更改資料。
Winforms 使用屬性支持 MVVM DataGridView.DataSource。
如何輕松高效地訪問 DataGridView 的資料?
唉,你忘了告訴我們你的 DataGridView 中有什么,而且我無法從你的代碼中提取顯示的內容。因此,對于示例,假設您顯示 的集合的幾個屬性Products:
class Product
{
public int Id {get; set;}
public string ProductCode {get; set;}
public string Description {get; set;}
public ProductType ProductType {get; set;} // some enumeration: food / non-food / etc
public decimal Price {get; set;}
public int LocationId {get; set;} // foreign key to Location table
...
}
您可能不想顯示所有屬性。
所以當然你有一個程式來獲取你最初想要展示的產品:
IEnumerable<Product> FetchProductsToShow() {...}
實施超出了問題的范圍。
使用 Visual Studio 設計器,您添加了一個DataGridView, 和一個DataGridViewColumn要顯示的每個 Product 屬性。您必須定義哪個 DataGridViewColumn 將顯示哪個屬性的值。這可以使用設計器來完成。我通常在建構式中使用nameof.
public MyForm : Form
{
InitializeComponents();
// Define which column shows which Property:
coilumnProductId.DataPropertyName = nameof(Product.Id);
columnProductCode.DataPropertyName = nameof(Product.ProductCode);
columnProductPrice.DataPropertyName = nameof(Product.Price);
...
使用 , 的好處nameof是,如果以后您決定更改屬性的名稱,它會在此處自動更改。編譯器檢測到鍵入錯誤。
現在要顯示所有產品,您只需將 ProductsToDisplay 分配給`dataGridView1.DataSource:
this.dataGridView1.DataSource = this.FetchProductsToShow().ToList();
并且您的資料很快就會顯示出來。
但是,如果操作員編輯表,則資料不會更新。如果需要,您必須將必須顯示的產品放入實作IBindingList. 幸好已經有這么一個類了,不出意外的名字BindingList<T>
將以下屬性添加到您的表單中:
public BindingList<Product> DisplayedProducts
{
get => (BindingList<Product>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
現在,操作員所做的所有更改都會在 BindingList 中自動更新:對單元格的更改,以及添加和洗掉的行。
private void ShowInitialProducts()
{
this.DisplayedProducts = new BindingList<Product>(this.FetchProductsToDisplay().ToList());
}
要訪問已編輯的表格,例如在操作員按下 OK 按鈕后:
public void OnButtonOk_Clicked(object sender, ...)
{
BindingList<Product> editedProducts = this.DisplayedProducts;
// find out which products are changed, and process them:
this.ProcessEditedProducts(editedProducts);
}
回到你的問題
但我不明白如何定位特定行
BindingList<T>不執行IList<T>。設計人員發現直接訪問沒有用this.DisplayedProducts[4]。畢竟:如果操作員可以重新排列行,您不知道索引行中有什么[4]。
但是,您可能希望以序列的形式訪問產品。因此ICollection<T>得以實施。
如果您想訪問該current行或selected rows,請考慮將以下屬性添加到您的表單中:
public Product CurrentProduct => this.dataGridView1.CurrentRow?.DataBoundItem as Product;
這將回傳當前產品,如果未選擇任何內容,則回傳 null
public IEnumerable<Product> SelectedProducts = this.dataGridView1.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<Product>();
因此,要在操作員按下 Ok 按鈕后訪問所選產品:
public void OnButtonOk_Clicked(object sender, ...)
{
IEnumerable<Product> selectedProducts = this.SelectedProducts;
// process the selected products:
this.ProcessProducts(selectedProducts);
}
有改進的余地
如果我查看您的代碼,在我看來,如果操作員單擊具有名稱的列中的單元格btnPDFsingle(為什么不使用解釋列顯示內容的名稱?),那么您會做幾件事:
- 您要求操作員提供檔案名,
- 如果檔案存在,則將其洗掉(如果無法洗掉則解決問題)
- 然后你創建一個
PdfPTable并用 DataGridView 的內容填充它 - 最后,您將 PdfPTable 寫入檔案。
而您決定在一個程式中完成所有這些。這使得單元測驗變得困難。您不能重用此代碼的任何部分,并且如果您更改其中的一部分,則很難檢測出您的代碼的哪些部分也必須更改。
private void gvSamplereports_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == gvSamplereports.Columns["btnPDFsingle"].Index)
{
this.SaveProducts();
}
else
... // report problem to operator
}
private void SaveProducts()
{
string fileName = this.AskOperatorForFileName();
if (String.IsNullOrEmpty(fileName))
{
... // report operator that no filename is given
return;
}
// fetch the products that must be in the Pdf
IEnumerable<Product> productsToSave = this.FetchProductsToSave();
this.SaveProducts(fileName, productsToSave);
}
ICollection<Product> FetchProductsToSave()
{
// Do you want to save all Products or Only the selected ones?
return this.DisplayedProducts;
}
注意:如果您決定保存不同的東西,只保存選定的產品,或者可能只保存前 10 個產品,或者只保存非食品產品,您只需更改此方法即可。其他方法不知道,也不必知道保存了哪些產品。
順便說一句,您是否注意到,直到現在我還沒有提到產品被保存為 PDF?如果稍后您決定將它們保存為 XML、CSV 或純文本,則無需更改這些程序。
private void SaveProducts(string fileName, IEnumerable<Product> productsToSave)
{
PdfPTable tableToSave = this.CreatePdfPTable(productsToSave);
this.SavePdfPTable (fileName, tableToSave);
}
private PdfPTable CreatePdfPTable(IEnumerable<Product> products)
{
...
foreach (Product product in products)
{
...
}
}
你看到了嗎,為了創建 PdfPTable,我不再需要訪問 DataGridViewRows 或 Cells 了嗎?如果您決定更改 PdfPTable 的布局,則僅更改此程序。沒有任何外部方法對表格的內部格式一無所知。易于單元測驗,易于重用,易于更改。
private void SavePdfPTable (string fileName, PdfPTable pdfPTable)
{
// if the file already exists, if will be overwritten (FileMode.Create)
// so no need to delete it first
using (FileStream stream = new FileStream(sfd.FileName, FileMode.Create))
{
... etc, write the PdfTable in the stream.
}
}
你看到了嗎,因為所有的小程式,每個程式只有一個特定的任務。對這個任務進行單元測驗要容易得多,如果你想要一個小的改變(例如保存為 XML),用一個類似的任務替換這個任務。您可以以不同的形式重復使用這些程序中的每一個。因為您有適當的單元測驗,所以您不必擔心這些程序會出現意外行為。
結論:
不要讓你的程式太大。每個程式都應該有一個特定的明確任務。這是所謂的關注點分離的一部分。考慮閱讀一些關于此的背景資訊。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/524684.html
標籤:C#表格pdf文本
