Update all objects where create new one - c#

I have a Languages class in which i use the excellibrary. I have an .xls file in which i have three columns. The first is used to check if the key phrase is used in the document and then i have one column for each language i use. I would like to create a transactionField object for every row of the document. I try to do that but every time i create a new object all the objects that was created before take the values of the last object created. Camn you please explain me where i am wrong and how can i correct that issue?
This is where the mistake happen
TranslationField tnf = new TranslationField();
tnf.Used = false;
tnf.Strings = values;
Translations.Add(sKey, tnf);
public class Languages
{
public static bool Setup()
{
SupportedLanguages.Clear();
SupportedLanguages.Add(csDefaultLang);
try
{
Workbook book = Workbook.Load(sPath);
Worksheet sheet = book.Worksheets[0];
KeyStringHelper values = new KeyStringHelper();
TranslationNeedle tnl;
List<string> columns = new List<string>();
string sKey = "";
// traverse rows by Index
for (int rowIndex = sheet.Cells.FirstRowIndex; rowIndex <= sheet.Cells.LastRowIndex; rowIndex++)
{
Row row = sheet.Cells.GetRow(rowIndex);
row.FirstColIndex = 1;
for (int colIndex = row.FirstColIndex; colIndex <= row.LastColIndex; colIndex++)
{
Cell cell = row.GetCell(colIndex);
// the first excel row is assumed to be columns names
if (rowIndex == sheet.Cells.FirstRowIndex)
{
//Columns names correctly formatted
columns.Add(char.ToUpper(cell.StringValue[0]) + cell.StringValue.Substring(1).ToUpper());
//Register every language inside the xls
SupportedLanguages.Add(char.ToUpper(cell.StringValue[0]) + cell.StringValue.Substring(1).ToUpper());
}
else
{
if (colIndex - row.FirstColIndex == 0)
sKey = cell.StringValue.Replace("\r\n", "\n");
else
values.Add(columns[colIndex - row.FirstColIndex], cell.StringValue.Replace("\r\n", "\n"));
}
}
// add the cell values to Translations Dictionary
if (rowIndex != sheet.Cells.FirstRowIndex)
{
TranslationField tnf = new TranslationField();
tnf.Used = false;
tnf.Strings = values;
Translations.Add(sKey, tnf);
}
}
//other stuff
}
}
Here is the class TranslationField
class TranslationField
{
public bool Used = false;
public KeyStringHelper Strings = new KeyStringHelper();
}

You're reusing the same KeyStringHelper instance (values) for every TranslationField. So every TranslationField instance in your Translations collection is referencing the same KeyStringHelper instance.
It looks like you need to move the line
KeyStringHelper values = new KeyStringHelper();
inside the outer for loop.

Related

Get Merged Cell Area with EPPLus

I'm using EPPlus to read excel files.
I have a single cell that is part of merged cells. How do I get the merged range that this cell is part of?
For example:
Assume Range ("A1:C1") has been merged.
Given Range "B1" it's Merge property will be true but there isn't a way to get the merged range given a single cell.
How do you get the merged range?
I was hoping for a .MergedRange which would return Range("A1:C1")
There is no such property out of the box but the worksheet has a MergedCells property with an array of all the merged cell addresses in the worksheet and a GetMergeCellId() method which will give you the index for a given cell address.
We can therefore combine these into a little extension method you can use to get the address. Something like this:
public static string GetMergedRangeAddress(this ExcelRange #this)
{
if (#this.Merge)
{
var idx = #this.Worksheet.GetMergeCellId(#this.Start.Row, #this.Start.Column);
return #this.Worksheet.MergedCells[idx-1]; //the array is 0-indexed but the mergeId is 1-indexed...
}
else
{
return #this.Address;
}
}
which you can use as follows:
using (var excel = new ExcelPackage(new FileInfo("inputFile.xlsx")))
{
var ws = excel.Workbook.Worksheets["sheet1"];
var b3address = ws.Cells["B3"].GetMergedRangeAddress();
}
(Note that in the event that you use this method on a multi-celled range it will return the merged cell address for the first cell in the range only)
You can get all merged cells from worksheet, hence
you can find the merged range a specific cell belongs to using the following:
public string GetMergedRange(ExcelWorksheet worksheet, string cellAddress)
{
ExcelWorksheet.MergeCellsCollection mergedCells = worksheet.MergedCells;
foreach (var merged in mergedCells)
{
ExcelRange range = worksheet.Cells[merged];
ExcelCellAddress cell = new ExcelCellAddress(cellAddress);
if (range.Start.Row<=cell.Row && range.Start.Column <= cell.Column)
{
if (range.End.Row >= cell.Row && range.End.Column >= cell.Column)
{
return merged.ToString();
}
}
}
return "";
}
Update:
Turns out that there is a much easier way using EPPLUS, just do the following:
var mergedadress = worksheet.MergedCells[row, column];
For example, if B1 is in a merged range "A1:C1":
var mergedadress = worksheet.MergedCells[1, 2]; //value of mergedadress will be "A1:C1".
2 is the column number because B is the 2nd column.
This will provide you exact width of merged cells:
workSheet.Cells[workSheet.MergedCells[row, col]].Columns
Not a direct answer as Stewart's answer is perfect, but I was lead here looking for a way to get the value of a cell, whether it's part of a larger merged cell or not, so I improved on Stewart's code:
public static string GetVal(this ExcelRange #this)
{
if (#this.Merge)
{
var idx = #this.Worksheet.GetMergeCellId(#this.Start.Row, #this.Start.Column);
string mergedCellAddress = #this.Worksheet.MergedCells[idx - 1];
string firstCellAddress = #this.Worksheet.Cells[mergedCellAddress].Start.Address;
return #this.Worksheet.Cells[firstCellAddress].Value?.ToString()?.Trim() ?? "";
}
else
{
return #this.Value?.ToString()?.Trim() ?? "";
}
}
And call it like this
var worksheet = package.Workbook.Worksheets[i];
var rowCount = worksheet.Dimension.Rows;
var columnCount = worksheet.Dimension.Columns;
for (int row = 1; row <= rowCount; row++)
{
for (int col = 1; col <= columnCount; col++)
{
string val = worksheet.Cells[row, col].GetVal();
}
}

WPF datagrid fill with uneven data to look even

I have an auto generated Datagrid that shows queried data from an API. The results come in array form and i put them in a DataTable, that are shown in Datatgrid. The array length are not always the same and the columns are not "even".
|--DTColumn1---DTColumn2-----|
|---Data1----|---Data1-------|
|---Data2----|---Data3-------|
|---Data3----|---Data5-------|
|---Data4----|---emptycell---|
|---Data5----|---emptycell---|
The "DTColumn" are the arrays that contain the data, that are similar, but parts could be missing and that is not an error. I don't know the data that I will get in runtime, but i know that it will contain some similar data.
Any advice to make it look like this:
|--DTColumn1---DTColumn2-----|
|---Data1----|---Data1-------|
|---Data2----|---emptycell---|
|---Data3----|---Data3-------|
|---Data4----|---emptycell---|
|---Data5----|---Data5-------|
EDIT:
Code structure:
private void buttonget_Click(object sender, RoutedEventArgs e)
{
sngrid.ItemsSource = null;
string dtnumber = DTBox.Text;
if (dtnumber == "")
{
MessageBox.Show("NO DATA!");
}
else
{
string[] dtresvalues;// this is were the DTColumn's come from and the number a of loop's for the second apicall
int s = apicall.Check..; //<- check if input can be handled and get dtresvalues
if (s != 5)
{
//API error handling here
}
else
{
var list = dtresvalues.Where((value, index) => index % 2 == 0).ToArray();// get rid of excess/usles data
string[] predt = list.ToArray();// prepared data, actual DTColumn's in array
DataTable table = new DataTable();
for (int i = 0; i < predt.Length; i++)//loop untill DTColumn run out
{
string kerdt = predt[i];// set current DTColumn for API
int si = apicall.Get...;// if no error, it returns the resoultvalues array (data1, data2,...)
if (si != 0)
{
//API error handling here
}
else
{
table.Columns.Add(kerdt);//set column name in table
if (i == 0)//this loop down, add's resoultvalues of API to current column of table(data1,data2, ..)
{
foreach (string sd in resoultvalues)
{
table.Rows.Add(sd);
}
}
else
{
while (table.Rows.Count < resoultvalues.Length)
{
table.Rows.Add();
}
for (int j = 0; j < resoultvalues.Length; j++)
{
table.Rows[j].SetField(i, resoultvalues[j]);
}
}
}
}
// add finished table to datagrid
sngrid.ItemsSource = table.DefaultView;
}
Also resoultvalues is an array.
I don't know what type of data is inside DTColumn1 and DTColumn2, but I imagine that there is something to let you know that, for example, DTColumn1[0] is relative to DTColumn2[3].
You can create two new arrays and use it as containers for your arrays of data, while filling the new array mind the empty fields.
Example:
string[] DTColumn1 = {
"1-foo",
"2-bar",
"3-foobar"
};
string[] DTColumn2 = {
"1-foo2",
"3-foobar2"
};
//Find the longest array of the two
string[] longestArray = DTColumn1;
string[] shortestArray = DTColumn2;
if (DTColumn2.Length > longestArray.Length) {
longestArray = DTColumn2;
shortestArray = DTColumn1;
}
//Instantiating new lists to show data
List<string> col1 = new List<string>();
List<string> col2 = new List<string>();
//Filling "interface" lists with data
foreach (void value_loopVariable in longestArray) {
value = value_loopVariable;
col1.Add(value);
}
//This can be tricky, but I really have no idea of how your data is structured
foreach (void value1_loopVariable in col1) {
value1 = value1_loopVariable;
foreach (void value2_loopVariable in shortestArray) {
value2 = value2_loopVariable;
if (value1[0].Equals(value2[0])) {
col2.Add(value2);
break;
}
//When the program reaches this point means that there is no corrispondace of data, so we add an empty value to col2
col2.Add("");
}
}
//Here you'll have col1 and col2 filled with data
This will result in this:
|--DTColumn1---|--DTColumn2-----|
|---1-foo------|---1-foo2-------|
|---2-bar------|---emptycell----|
|---3-foobar---|---3-foobar2----|
You may notice that if DTColumn2 is the longer array it will be placed in the first column of the table.
I wrote down a raw example just to give you an idea, please note that this is untested code

How to use Epplus with cells containing few rows

I want to import some excel file using epplus
the problem is that some cells contains more than one row (and that cause a problem
My excel look like this (in realite their is more tests (test2,test3....)
I can only get the first column by this algorithm..but it will be more complicated to get the seconde column
//this is the list than contain applications (column 2)
ICollection<Application> applications = new List<Application>();
int i = 0;
for (int j = workSheet.Dimension.Start.Row;
j <= workSheet.Dimension.End.Row;
j=i+1)
{
//this is the object that contain the first column
//and also a list of the second column (foreach domain thei `is a list of applications (column 2)`
Domaine domaine = new Domaine();
i += 1;
//add here and not last row
while (workSheet.Cells[i, 1].Text == "" && i < workSheet.Dimension.End.Row)
{
i++;
}
if (i > workSheet.Dimension.End.Row)
break;
domaine.NomDomaine = workSheet.Cells[i, 1].Text;
domaines.Add(domaine);
}
Edit : in other words is their a way to get the number of rows in one cell , OR a way to duplicate the value of each row in the cell
(for exemple if i have a cell from row 1 to 14 and the row number 5 have value)
how can i duplicate that text to all the rows (that will help me solving the problem)
Those are known as Merged cells. Values from merged cells are stored in the .Value property of the first cell in the merged range. This means we need to do just a little bit more work in order to read the value from a merged cell using EPPlus.
EPPlus provides us with a couple of properties that help us get to the correct reference though. Firstly we can use a cell's .Merge property to find out if it is part of a merged range. Then we can use the the worksheet's .MergedCells property to find the relevant range. It's then just a matter of finding the first cell in that range and returning the value.
So, in summary:
Determine if the cell we need to read from is part of a merged range using .Merge
If so, get the index of the merged range using the worksheet's .MergedCells property
Read the value from the first cell in the merged range
Putting this together we can derive a little helper method to take a worksheet object and row/col indices in order to return the value:
static string GetCellValueFromPossiblyMergedCell(ExcelWorksheet wks, int row, int col)
{
var cell = wks.Cells[row, col];
if (cell.Merge) //(1.)
{
var mergedId = wks.MergedCells[row, col]; //(2.)
return wks.Cells[mergedId].First().Value.ToString(); //(3.)
}
else
{
return cell.Value.ToString();
}
}
Worked example
If I have a domain class like this:
class ImportedRecord
{
public string ChildName { get; set; }
public string SubGroupName { get; set; }
public string GroupName { get; set; }
}
that I wanted to read from a spreadsheet that looked like this:
Then I could use this method:
static List<ImportedRecord> ImportRecords()
{
var ret = new List<ImportedRecord>();
var fInfo = new FileInfo(#"C:\temp\book1.xlsx");
using (var excel = new ExcelPackage(fInfo))
{
var wks = excel.Workbook.Worksheets["Sheet1"];
var lastRow = wks.Dimension.End.Row;
for (int i = 2; i <= lastRow; i++)
{
var importedRecord = new ImportedRecord
{
ChildName = wks.Cells[i, 4].Value.ToString(),
SubGroupName = GetCellValueFromPossiblyMergedCell(wks,i,3),
GroupName = GetCellValueFromPossiblyMergedCell(wks, i, 2)
};
ret.Add(importedRecord);
}
}
return ret;
}
static string GetCellValueFromPossiblyMergedCell(ExcelWorksheet wks, int row, int col)
{
var cell = wks.Cells[row, col];
if (cell.Merge)
{
var mergedId = wks.MergedCells[row, col];
return wks.Cells[mergedId].First().Value.ToString();
}
else
{
return cell.Value.ToString();
}
}

Events functionality should apply on all pages in datagridview - Paging concept

I have a task to develop Windows applications where paging is involved. If I perform any event like splitting date and time, it's applied only to the current page. I would like to apply that event to all pages in the Datagridview.
If I take a datatable/dataset and work on it, the UI is taking time to read the file as it again reads the whole file to data table. So, please suggest any other alternative to apply the events to all pages in the DataGridView.
I will post the code, or upload my code in any site or here, if required.
Please let me know if my question is unclear.
VARIABLES DECLARATION:
List<String> cmbList = new List<string>();
public String Replace;
public String Find;
public String Col;
public String NewColumn;
public String NewColumnValue;
public string MyFOrmat { get; set; }
int PageCount;
int maxRec;
int pageSize = 30;
int currentPage = 1;
int recNo = 0;
string FileName;
String[] datfile;
button1 = BROWSE BUTTON (Where i read the file):
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.InitialDirectory = "Desktop";
openFileDialog1.Filter = "dat files (*.DAT)|*.DAT|All files (*.*)|*.*";
openFileDialog1.FilterIndex = 2;
openFileDialog1.RestoreDirectory = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
FileName = openFileDialog1.FileName;
string text = System.IO.File.ReadAllText(FileName);
datfile = text.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
//Added on 2015-12-02
maxRec = datfile.Length - 1;
PageCount = maxRec / pageSize;
LoadPage(MyFOrmat);
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
}
LOADPAGE Code:
public void LoadPage(string Format, bool isFindAndReplace = false)
{
int startRec;
int endRec;
if (currentPage == PageCount)
{
endRec = maxRec;
}
else
{
endRec = pageSize * currentPage;
}
dataGridView1.Rows.Clear();
if (recNo == 0)
{
dataGridView1.Columns.Clear();
}
int rowindex = 0;
startRec = recNo;
for (int RowCount = startRec; RowCount <= endRec; RowCount++)
{
if (datfile[RowCount].ToString() != "" )
{
if (RowCount == 0)
{
string[] column = datfile[RowCount].Split('þ');
for (int i = 0; i < column.Length - 1; i++)
{
if (column[i].ToString() != "" && column[i].ToString() != "\u0014")
{
DataGridViewTextBoxColumn dgvtxtcountry = new DataGridViewTextBoxColumn();
dgvtxtcountry.HeaderText = column[i].ToString();
dgvtxtcountry.Name = column[i].ToString();
dataGridView1.Columns.Add(dgvtxtcountry);
cmbList.Add(column[i]);
i += 1;
}
}
}
if (RowCount != 0)
{
dataGridView1.Rows.Add();
string[] column = datfile[RowCount].Split('þ');
int index = 0;
for (int i = 1; i < column.Length - 1; i++)
{
if (column[i].ToString() != "\u0014")
{
if (i == 3)
{
dataGridView1.Rows[rowindex].Cells[index].Value = Convert.ToDateTime(column[i]).ToString(Format);
}
else
{ dataGridView1.Rows[rowindex].Cells[index].Value = column[i].Trim('þ'); }
index += 1;
i += 1;
}
}
rowindex += 1;
}
}
recNo += 1;
}
}
FIND and REPLACE Event:
private void btnFindandReplace_Click(object sender, EventArgs e)
{
Form2 f = new Form2();
f.cmbColumnCombo.DataSource = cmbList;
f.ShowDialog();
for (int i = 0; i <= dataGridView1.Rows.Count - 1; i++)
{
//dataGridView1.Rows[rowindex].Cells[index].Value = Convert.ToDateTime(column[i]).ToString(Format);
if (dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value.ToString().ToLower().Contains(f.txtfind.Text.ToLower()))
{
//dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value = dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value.ToString().ToLower().Replace(f.txtfind.Text.ToLower(), f.txtreplace.Text);
//bulidDataRow(i);
if (!string.IsNullOrEmpty(f.txtfind.Text))
{
dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value = dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value.ToString().Replace(f.txtfind.Text, f.txtreplace.Text);
#region Commented
//dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value = dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value.ToString().Replace(f.txtfind.Text, f.txtreplace.Text);
//bulidDataRow(i);
#endregion
}
}
}
}
private void btnNext_Click(object sender, EventArgs e)
{
currentPage += 1;
if (currentPage > PageCount)
{
currentPage = PageCount;
//Check if you are already at the last page.
if (recNo == maxRec)
{
MessageBox.Show("You are at the Last Page!");
return;
}
}
LoadPage(MyFOrmat);
}
Please let me know if anything needs to be added.
To summarize your requirements:
You want to read largish data files of 10k - 500k records
You want to display them in chunks/pages in a DataGridView
You want allow the user to modify the data:
The user can merge columns
The user can use change&replace on the data
Date&time columns may be split
Possibly modified data shall be saved
The way I see it you have two approaches:
Either cache the data
Or cache the actions
Caching the actions is doable but clearly a lot more fuss, both in coding the caching and in keeping the data synchronized.
So caching the data would be my first choice.
Here is a sketch of how to break up the functionality:
A function to read in the whole data and load them into a DataTable
Functions for the initial display and for displaying a certain page
Functions for doing each of the changes on the list of rows.
After calling a changing function the current page display must be refreshed.
Keeping the total quantity of data in memory shouldn't really be a problem today; I notice that you are reading in all data as strings already in the datFile array. Reading it into a table will spare you to split it over and over..
A DataTable.DataRow also offers nice properies like HasErrors or RowState. And its Items can have a dedicated type to help with formatting..
Note however that DataRow doesn't have a (real) constructor; instead it must be created from a DataTable, so you will first have to create one from your columns!
The display code would use a pageSize and a currentFirstLine variable; it can clear and add the rows into the DGV or you could go for a binding solution with the DataTable you need anyway holding the DataRows and a filter on the table or rather on an BindingSource.
Of course you can also use a structure of your own, maybe a simple as a string[] or a List<string>to hold the row data..
If you are interested in the idea of caching the actions, you could create a ChangeAction class that holds:
the type
the parameters needed, ie, the column(s), the change&replace strings etc..
Then in a List<ChangeAction> you would store them as they happen and then apply them to each unchanged row. But here comes the first catch: You will need to know which row have been changed and maybe if a ChangeAction can be applied twice without screwing up the data.. More problems may or may not come later, depending on the details of you data and actions..
Here is an example of how to set up the binding using class level variables:
DataTable DT = new DataTable();
BindingSource BS = new BindingSource();
int pageSize = 0;
int firstLineVisible = 0;
After filling the table you can bind it and set the initial filer:
BS.DataSource = DT;
dataGridView1.DataSource = BS;
pageSize = (dataGridView1.ClientSize.Height - dataGridView1.ColumnHeadersHeight)
/ dataGridView1.Rows[0].Height;
int l1 = firstLineVisible; int l2 = firstLineVisible + pageSize;
BS.Filter = "Nr >= " + l1 + " and Nr < " + l2;
When scrolling you simply change the firstLineVisible and rest the Filter and the DataSource..
Now all your data modifications should work on the data in the DataTable using the SetField method!
Also note that you need one column in your data that holds a running number. If your data don't have one it is easy to include it by adding it to the data lines:
The column gets autogenerated in the DataGridView. For the DataTable we want to have it in the first data line; I use a separator string sep:
var lines = File.ReadAllLines(fileName).ToList();
..
string[] sep = { ";" };
var p0 = ("Nr" + sep[0] + lines[0]).Split(sep, StringSplitOptions.None );
DT.Columns.Clear();
for (int i = 0; i < p0.Length; i++) DT.Columns.Add(p0[i], typeof(string));
Adding it to the data is just as simple:
for (int l = 1; l < lines.Count; l++)
{
var p = (l + sep[0] + lines[l]).Split(sep, StringSplitOptions.None);
DT.Rows.Add(p);
}
You can hide the number column if you want to..:
dataGridView1.Columns["Nr"].Visible = false;
You should add that line right after setting the Filter.

My (Good?) Method for making DropDown List in Excel programatically (VSTO)

Here is the method I wrote for making DropDown List in an Excel cell by taking the possible values from a range in the same sheet/workbook.
private void MakeDropDownList(string strSrcSheetName, string strDestSheetName, string strSrcRange, string strDestCell)
{
var currentSheet = Application.Sheets[strDestSheetName];
var inv = Application.Sheets[strSrcSheetName];
var items = inv.Range[strSrcRange];
var list_items = new List<string>();
foreach (Excel.Range cell in items)
{
list_items.Add(cell.Value2.ToString());
}
Range xlsRange;
xlsRange = currentSheet.Range[strDestCell];
Excel.DropDowns xlDropDowns;
Excel.DropDown xlDropDown;
xlDropDowns = ((Excel.DropDowns)(currentSheet.DropDowns(Missing.Value)));
xlDropDown = xlDropDowns.Add((double)xlsRange.Left, (double)xlsRange.Top, (double)xlsRange.Width, (double)xlsRange.Height, true);
//Add item into drop down list
for (int i = 0; i < list_items.Count; i++)
{
xlDropDown.AddItem(list_items[i], i + 1);
}
}
To use it, I can invoke it like this:
MakeDropDownList("Units", "ReceiveBonds", "B1:B5", "E9:E18");
BUT, the final result is like this:
As you can see, the DropDown List is smaller than the E Column Width, and what's worse is that after I select a value from the DropDown List, the cell keep its original Value which is Choose Unit.
Am I doing something wrong?
What I want:
Either make this DropDown List look like the regular validation dropdown of Excel, Or.
Be able to trigger an event to set the selected value from the list to the corresponding cell.
Just add ColumnWidth before.
if (checkModifiers() && (checkKey(Keys.F3)))
{
Workbook wb = Globals.ThisAddIn.Application.ActiveWorkbook;
Worksheet ws = Globals.ThisAddIn.Application.ActiveSheet;
Microsoft.Office.Interop.Excel.Range rng = (Microsoft.Office.Interop.Excel.Range)Globals.ThisAddIn.Application.ActiveCell;
int row = rng.Row;
int column = rng.Column;
Range cell = ws.Cells[row, column];
cell.ColumnWidth = 50;
try
{
drd.Add("One");
drd.Add("Two");
drd.Add("Three");
Microsoft.Office.Interop.Excel.DropDown xlDropDown;
Microsoft.Office.Interop.Excel.DropDowns xlDropDowns;
xlDropDowns = ((Microsoft.Office.Interop.Excel.DropDowns)(ws.DropDowns(Missing.Value)));
xlDropDown = xlDropDowns.Add((double)cell.Left, (double)cell.Top, (double)cell.Width, (double)cell.Height, true);
for (int i = 0; i < drd.Count; i++)
{
xlDropDown.AddItem(drd[i], i+1);
}
}catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}

Categories

Resources