I have a scrolling datagridview that dyanmically changes it's number of columns and rows. I'm looking for a way to export the datagridview as it is when the user clicks a button. I want it to export as something that cannot be edited (so not excel file). I've tried using iTextSharp to export as pdf and can't seem to come up with a dynamically changing loop that would suit it. Also, I haven't been able to find any sample code using iTextSharp that included also exporting row headers along with column headers. I've also tried one solution (that I can't seem to find at the moment) on the microsoft forums that takes advantage of a the add-on functionality of Excel to write to PDF after the datagridview contents are exported to it. The problem I have with that is it creates way to many pages as well as still shows rows that have been hidden or deleted from the datagridveiw. Any idea of how I can accomplish this feat?
I'll include two pictures to show the way in which the datagridview is dynamically populated and changes
It can have up to 66 rows and 12 columns (not including row or column headers)
You can use EPPlus to create an password-protected (locked) excel file that can be read, but not edited. I can show you how to start from a situation like this:
and get a protected file like this:
First you must download and include the EPPlus Library in your project. Don't forget to check the great EPPlusSamples Project
Then you need a method to extract your DataGridView VISIBLE data (you don't want the invisible column to be exported). I did it using a 2d array. This function accept a DataGridView parameter and returns a 2d array:
Function GetDataGridView2DArray(ByVal dataGridView As DataGridView) As String(,)
'Save list of visible columns
Dim nrVisibleColumns = (From c As DataGridViewColumn In DataGridView1.Columns
Where c.Visible
Select c).ToList()
'create 2d-array to store values, dimensions given by number of rows and visible columns
Dim dgvArray(nrVisibleColumns.Count, DataGridView1.Rows.Count) As String
'create the first row with Column Headers text
For Each col As DataGridViewColumn In nrVisibleColumns
dgvArray(col.Index + 1, 0) = col.HeaderText
Next
'create Rows, including Row Header text
For Each row As DataGridViewRow In DataGridView1.Rows
Dim rowNumber = row.Index + 1
dgvArray(0, rowNumber) = DataGridView1.Rows(row.Index).HeaderCell.Value 'save rowheader cell value
For Each col As DataGridViewColumn In nrVisibleColumns
dgvArray(col.Index + 1, rowNumber) = DataGridView1(col.Index, row.Index).Value
Next
Next
Return dgvArray
End Function
Now that you have your array you can create an Excel File and fill it with data. Plus, before saving, we'll lock it with a password to prevent user editing.
Private Sub CreateXls()
Dim fi = New FileInfo("C:\temp\output.xlsx")
Dim package As New ExcelPackage()
Dim ws = package.Workbook.Worksheets.Add("Dgv Output") 'create sheet
'get array of datagridview data
Dim dataArray = GetDataGridView2DArray(DataGridView1)
'loop my 2d array and fill my excel file
For iColumn As Integer = dataArray.GetLowerBound(0) To dataArray.GetUpperBound(0)
For iRow As Integer = dataArray.GetLowerBound(1) To dataArray.GetUpperBound(1)
ws.Cells(iRow + 1, iColumn + 1).Value = dataArray(iColumn, iRow)
ws.Cells(iRow + 1, iColumn + 1).Style.Locked = True 'lock the cell
Next
Next
ws.Cells.AutoFitColumns() 'resize columns to fit
ws.Protection.AllowFormatColumns = True 'let user resize columns if he wants
ws.Protection.SetPassword("1") 'protect the sheet from editing
package.SaveAs(fi) 'save file
End Sub
Now you should be able to easily export your dynamic DataGridView with one click.
This is My Coding Tested And Output is Perfect :
Dim ExcelApp As Object, ExcelBook As Object
Dim ExcelSheet As Object
Dim i As Integer
Dim j As Integer
'create object of excel
ExcelApp = CreateObject("Excel.Application")
ExcelBook = ExcelApp.WorkBooks.Add
ExcelSheet = ExcelBook.WorkSheets(1)
With ExcelSheet
For Each col As DataGridViewColumn In Me.DataGridView1.Columns
ExcelSheet.Cells(1, col.Index + 1) = col.HeaderText.ToString
For i = 1 To Me.DataGridView1.RowCount
ExcelSheet.cells(i + 1, 1) = Me.DataGridView1.Rows(i - 1).Cells("First Column Name").Value
For j = 1 To DataGridView1.Columns.Count - 1
ExcelSheet.cells(i + 1, j + 1) = DataGridView1.Rows(i - 1).Cells(j).Value
Next
Next
Next
End With
ExcelApp.Visible = True
ExcelSheet = Nothing
ExcelBook = Nothing
ExcelApp = Nothing
Related
i have a simple Excel/VBA problem:
What i want to create is a (single-select) ListBox where I want to show unique values of Data i have on a different worksheet.
So far I have a ListBox like this:
And a named selection of the data i want to show:
I used a formula like this and used that as the input for the ListBox.
The formula: =BEREICH.VERSCHIEBEN(TopicData!$C$1;1;0;ANZAHL2(TopicData!$C:$C)-1;1)
Now my question is: How can i get the ListBox to show only unique values? I am familiar with vba, so a solution including this would be totally fine. In fact I already tried to remove duplicate entries in vba, whenever there is a change to the ListBox, but for some reason nothing seems to work.
Here is my vba script where I tried to solve this:
unfortunatley I always get a "Error 400" when I trie to call RemoveItem on the ListBox.
' ...
' filter listbox content so only unique values remain
Dim i As Integer
' find duplicates
Dim inList As New Collection
Dim indexesToRemove As New Collection
For i = availableTopicsListBox.ListCount - 1 To 1 Step -1
If CollectionContains(inList, availableTopicsListBox.List(i)) Then
' if it is already in the list, remove it
indexesToRemove.Add i
Else
inList.Add availableTopicsListBox.List(i)
End If
Next i
' remove duplicates
Dim j As Integer
For j = indexesToRemove.count To 1 Step -1
availableTopicsListBox.RemoveItem (indexesToRemove(j))
Next j
'...
The code below will use the Dictionary to store only unique values from column C (in "TopicData" worksheet), and then populate availableTopicsListBox listbox with only the unique values inside the Dictionary.
Code
Option Explicit
Private Sub UserForm_Activate()
Dim Dict As Object
Dim Key As Variant
Dim LastRow As Long
Dim C As Range
With Sheets("TopicData") '<-- I think this is your sheet's name
' find last row with data in column "C"
LastRow = .Cells(.Rows.Count, "C").End(xlUp).Row
Set Dict = CreateObject("Scripting.Dictionary")
For Each C In .Range("C1:C" & LastRow)
If C.Value <> "" Then ' <-- skip empty cells
If Not Dict.exists(C.Value) Then
Dict.Add C.Value, 1
End If
End If
Next C
End With
' loop through all unique keys, and add them to the listbox
For Each Key In Dict.keys
availableTopicsListBox.AddItem Key
Next Key
End Sub
Using Interop.Excel I use:
CInt(oExcel.ActiveSheet.Cells(1, oExcel.ActiveSheet.Columns.Count).End(Microsoft.Office.Interop.Excel.XlDirection.xlToLeft).Column())
To get the number of active columns on row 1.
But how does one implement this in openxml?
I'm not very familiar with Interop.Excel but that code looks like you're getting the last active cell rather than the number of active cells in the sheet (although I could be way off the mark).
I think you can achieve either using OpenXML with something like this:
Public Shared Sub GetActiveColumns(filename As String, rowNumber As Integer)
'open the document in read-only mode
Using spreadsheetDocument__1 As SpreadsheetDocument = SpreadsheetDocument.Open(filename, False)
'get the workbookpart
Dim workbookPart As WorkbookPart = spreadsheetDocument__1.WorkbookPart
'get the correct sheet from the workbookview
Dim workbookView As WorkbookView = workbookPart.Workbook.Descendants(Of WorkbookView)().First()
Dim index As Integer = If(workbookView.ActiveTab IsNot Nothing AndAlso workbookView.ActiveTab.HasValue, CInt(workbookView.ActiveTab.Value), 0)
Dim sheet As Sheet = workbookPart.Workbook.Descendants(Of Sheet)().ElementAt(index)
If sheet IsNot Nothing Then
'get the corresponding worksheetpart
Dim worksheetPart As WorksheetPart = TryCast(workbookPart.GetPartById(sheet.Id), WorksheetPart)
'get the row
Dim row As Row = worksheetPart.Worksheet.Descendants(Of Row)().Where(Function(r) r.RowIndex = rowNumber).FirstOrDefault()
'get the last cell
Dim activeCells As IEnumerable(Of Cell) = row.Descendants(Of Cell)().Where(Function(c) Not String.IsNullOrEmpty(c.InnerText))
Dim cell As Cell = activeCells.LastOrDefault()
Console.WriteLine("The last cell is {0} ", cell.CellReference)
Console.WriteLine("There are {0} cells with data in them", activeCells.Count())
End If
End Using
End Sub
Given the following sheet
the output from the above code is when passing 1 as the rowNumber is:
The last cell is F1
There are 4 cells with data in them
The CellReference is the reference you would use in a formula (e.g. A1) so you might need to parse that if you want the column as a number. The code also assumes the cells are in order which I'm pretty certain Excel ensures although I don't think the OpenXML schema mandates it. If this causes an issue it could be fixed by iterating the cells and keeping track of the largest column.
Note: the VB might not be idiomatic as I used the Telerik Converter to convert it from C# to VB.
I have a program that puts a cell of data from an excel file and writes it to a datagridview cell. However since I'm only trying to write one cell of data I'm thinking it might be a better Idea to write the data from the excel file to a simple textbox.
I'm having trouble finding out how to interface a simple textbox with an excel file. If anyone has some suggestions on how I might accomplish this I would greatly appreciate it.
Below is my current code for working with my datagridview.
'This code sample here uses a for next loop to match the excel column and rows up with the datagridview's rows and columns.
Dim rowindex As Integer
Dim columnindex As Integer
For rowindex = 1 To DataGridView3.RowCount
For columnindex = 1 To DataGridView3.ColumnCount
objworksheet3.Cells(rowindex + 3, columnindex + 1) = DataGridView3(columnindex - 1, rowindex - 1).Value
Next
Next
'This code uses the oledatadapter to pull only the cells that I want from the excel file to populate the datagridview cell.
MyCommand3 = New OleDbDataAdapter("select * from [myWorksheet$B3:B4]", MyConnection)
ds3 = New System.Data.DataSet()
MyCommand3.Fill(ds3)
For rowindex = 1 To DataGridView3.RowCount
I suggest, For rowindex = 0 To DataGridView3.RowCount -1
You can get data directly from your dataset; for example to retrieve data from the first row of the first column:
Textbox1.text = ds.Tables(0).Rows(0)(0).ToString
if you want to fetch more cells you can cycle through rows and columns
I just want to mention first, I do appreciate everyone who gives of their knowledge to help others learn...
I have been able to piece together code that exports my Datgrid to an Excel file and works fine, except I am only getting the first row of the datagrid. since the datagrid could contain one record, or hundreds, I need all rows to export to Excel.
Here is what I have been able to put together (again, it works fine, file is saved with column headers, but only writes the first row of data from my datagrid):
'Export to Excel
Dim ExApp1 As Excel.Application
Dim ExWkbk1 As Excel.Workbook
Dim ExWksht1 As Excel.Worksheet
Dim MisValue As Object = System.Reflection.Missing.Value
Dim i As Integer
Dim j As Integer
ExApp1 = New Excel.Application
ExWkbk1 = ExApp1.Workbooks.Add(MisValue)
ExWksht1 = ExWkbk1.Sheets("sheet1")
For i = 0 To dg7.RowCount - 2
For j = 0 To dg7.ColumnCount - 1
ExWksht1.Cells(i + 2, j + 1) = dg7(j, i).Value.ToString()
For k As Integer = 1 To dg7.Columns.Count
ExWksht1.Cells(1, k) = dg7.Columns(k - 1).HeaderText
ExWksht1.Cells(i + 2, j + 1) = dg7(j, i).Value.ToString()
Next
Next
Next
ExWksht1.SaveAs("C:\MyExcel.xlsx")
ExWkbk1.Close()
ExApp1.Quit()
releaseObject(ExApp1)
releaseObject(ExWkbk1)
releaseObject(ExWksht1)
Again, many thsanks in advance for helping me learn
First of all it seems to me this is a DataGridView as opposed to a DataGrid since from what I know DataGrid does not have "RowCount" or "ColumnCount" properties in either WinForms or Web.UI (I could be wrong).
If it is so is there a specific reason for
dg7.RowCount - 2
If the object you are trying to get data from is indeed a DataGridView then I suppose this task is to omit new(empty) rows. If I am correct that you are trying to export data from a DataGridView this way there is a property for the DataGridViewRow that will serve this purpose:
IsNewRow
So (while I have not used Excel in VB.NET recently) your cycle could be:
For i = 0 To dg7.ColumnCount - 1
ExWksht1Cells(1 , i + 1) = dg7.Columns[i].HeaderText
Next
For i = 0 To dg7.RowCount - 1
If Not dg7.Rows(i).IsNewRow Then
For j = 0 To dg7.ColumnCount - 1
ExWksht1.Cells(i+ 2, j + 1) = dg7(i, j).Value.ToString()
Next
End If
Next
As far as the excel object calls I can't give a reasonable comment since I've not used that object too much.Let me know if that's useful and/or correct.
Is there a method available for copying a section out of a shape to another shape using VBA? I'm specifically trying to copy all the custom properties and user cells from one pagesheet to another page.
Unfortunately there isn't a simple method to do this. You will have to loop over all the rows in the source sheet and create the same rows in the destination sheet. E.g.:
Dim oPageSheet1 As Visio.Shape
Dim oPageSheet2 As Visio.Shape
Dim rowName As String
Dim i As Integer
Set oPageSheet1 = Visio.ActiveDocument.Pages.Item(1).PageSheet
Set oPageSheet2 = Visio.ActiveDocument.Pages.Item(2).PageSheet
i = visRowUser
While oPageSheet1.CellsSRCExists(visSectionUser, i, visUserValue, False)
oPageSheet2.AddNamedRow visSectionUser, oPageSheet1.Section(visSectionUser).Row(i).NameU, 0
oPageSheet2.Section(visSectionUser).Row(i).Name = oPageSheet1.Section(visSectionUser).Row(i).Name
oPageSheet2.CellsSRC(visSectionUser, i, visUserValue).FormulaU = oPageSheet1.CellsSRC(visSectionUser, i, visUserValue).FormulaU
oPageSheet2.CellsSRC(visSectionUser, i, visUserPrompt).FormulaU = oPageSheet1.CellsSRC(visSectionUser, i, visUserPrompt).FormulaU
i = i + 1
Wend
If you have to copy a large number of rows and performance is a consideration you should investigate using AddRows and SetFormulas.