Displaying parsed csv files - c#

Ive been working at this for hours and cant seem to figure out how to correctly display the data in a table
using (TextFieldParser csvParser = new TextFieldParser(path)) {
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.ReadLine();
int pointX = 30;
int pointY = 40;
while (!csvParser.EndOfData) {
string[] fields = csvParser.ReadFields();
int rowNums = fields.Length;
int index = 0;
for(index = 0; index < rowNums;index++) {
string Name = fields[index];
TextBox n = new TextBox();
n.Text = Name;
n.Location = new Point(pointX, pointY);
panel2.Controls.Add(n);
panel2.Show();
pointY += 20;
if(index != 0) {
pointX += 100;
}
}
}
}
Whats happening so far is im grabbing a csv file stored in the path variable and reading it the output is accessible through fields[] This works fine then I am trying to create textbox to put the data into based on rows however what i currently have comes out look like this
Program Display
I would like to display the column names and rows correctly in order here is an example image of what it looks like in notepad
Notepad Display
In notepad you will see each new line is a row and every , dictates a new entry in the row and i wanna display it this way in my program but in textbox
Also note that not all csv files that this program will be opening are short most will be large files with thousands or rows or more so theres no way that it could be simply putting fields[0] hard coded

You are much better off using a DataGridView to display this type of data in a table format.
From the toolbox add the DataGridView control to your form. You will need to build a DataTable that will bind to your DataGridview.
Below is what you can use(I commented out where you are skipping the header in your CSV file, and am using that line to get the column headers to be used in the datagrid)
var dt = new DataTable();
var lineNo = 0;
using (var csvParser = new TextFieldParser(path))
{
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
//csvParser.ReadLine();
while (!csvParser.EndOfData)
{
var fields = csvParser.ReadFields();
var rowNums = fields.Length;
var row = dt.NewRow();
lineNo += 1;
int index = 0;
for (index = 0; index < rowNums; index++)
{
if (lineNo==1)
{
dt.Columns.Add(fields[index]);
}
else
{
row[index] = fields[index];
}
}
if (lineNo == 1) continue;
dt.Rows.Add(row);
dt.AcceptChanges();
}
}
dataGridView1.DataSource = dt;

I think the controls are overlapping, so you can not see them. That they are overlapping like chairs is the problem. You are not resetting your coordinates.
Here an improvement:
using (TextFieldParser csvParser = new TextFieldParser(path)) {
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.ReadLine();
int offsetX = 30;
int offsetY = 40;
int counter;
while (!csvParser.EndOfData) {
int pointerY = ++counter * offsetY; // first counter increments by one, then counter times offsetY occurs
int pointerX;
string[] fields = csvParser.ReadFields();
int rowNums = fields.Length;
for(int index = 0; index < rowNums;index++) {
pointerX = (index + 1) * offsetX;
string name = fields[index];
TextBox n = new TextBox() { Text = name, Location = new Point(pointerX, pointerY) };
panel2.Controls.Add(n);
panel2.Show(); // should be unnecessary
}
}
}

Related

EP Plus - Error Table range collides with table

I am building an export to excel functionality using EP plus and c# application. I am currently getting the error.
'Table range collides with table tblAllocations29'
In my code logic below, I am looping through a data structure that contains key and collection as a value.
I looping across each key and once again loop through each collection belonging to that key.
I basically need to print tabular information for each collection along with its totals.
In the current scenario, I am getting the error when it is trying to print
three arrays
The first array has 17 records
The second array has 29 records
The third array has 6 records
I have taken a note of the ranges it is creating while debugging
The ranges are
A1 G18
A20 G50
A51 G58
controller
[HttpGet]
[SkipTokenAuthorization]
public HttpResponseMessage DownloadFundAllocationDetails(int id, DateTime date)
{
var ms = GetStrategy(id);
DateTime d = new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
if (ms.FIRM_ID != null)
{
var firm = GetService<FIRM>().Get(ms.FIRM_ID.Value);
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationsGroup = null;
var allocationsGrouped = GetAllocationsGrouped(EntityType.Firm, firm.ID, d);
string fileName = string.Format("{0} as of {1}.xlsx", "test", date.ToString("MMM, yyyy"));
byte[] fileContents;
var newFile = new FileInfo(fileName);
using (var package = new OfficeOpenXml.ExcelPackage(newFile))
{
FundAllocationsPrinter.Print(package, allocationsGrouped);
fileContents = package.GetAsByteArray();
}
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(fileContents)
};
result.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
return result;
}
return null;
#endregion
}
I have written the following utility that will try and export. It works sometimes when there are two array collections and it failed when processing three. Could somebody tell me what the problems are
FundsAllocationsPrinter.cs
public class FundAllocationsPrinter
{
public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
{
ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
wsSheet1.Protection.IsProtected = false;
int count = 0;
int previouscount = 0;
var position = 2;
int startposition = 1;
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;
foreach (var ag in allocation)
{
allocationGroup = ag.Select(a => a);
var allocationList = allocationGroup.ToList();
count = allocationList.Count();
using (ExcelRange Rng = wsSheet1.Cells["A" + startposition + ":G" + (count + previouscount + 1)])
{
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);
//Set Columns position & name
table.Columns[0].Name = "Manager Strategy";
table.Columns[1].Name = "Fund";
table.Columns[2].Name = "Portfolio";
table.Columns[3].Name = "As Of";
table.Columns[4].Name = "EMV (USD)";
table.Columns[5].Name = "Percent";
table.Columns[6].Name = "Allocations";
wsSheet1.Column(1).Width = 45;
wsSheet1.Column(2).Width = 45;
wsSheet1.Column(3).Width = 55;
wsSheet1.Column(4).Width = 15;
wsSheet1.Column(5).Width = 25;
wsSheet1.Column(6).Width = 20;
wsSheet1.Column(7).Width = 20;
// table.ShowHeader = true;
table.ShowFilter = true;
table.ShowTotal = true;
//Add TotalsRowFormula into Excel table Columns
table.Columns[0].TotalsRowLabel = "Total Rows";
table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";
table.TableStyle = TableStyles.Dark10;
}
foreach (var ac in allocationGroup)
{
wsSheet1.Cells["A" + position].Value = ac.MANAGER_STRATEGY_NAME;
wsSheet1.Cells["B" + position].Value = ac.MANAGER_FUND_NAME;
wsSheet1.Cells["C" + position].Value = ac.PRODUCT_NAME;
wsSheet1.Cells["D" + position].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
wsSheet1.Cells["E" + position].Value = ac.UsdEmv;
wsSheet1.Cells["F" + position].Value = Math.Round(ac.GroupPercent,2);
wsSheet1.Cells["G" + position].Value = Math.Round(ac.WEIGHT_WITH_EQ,2);
position++;
}
position++;
previouscount = position;
// position = position + 1;
startposition = position;
position++;
}
}
}
This is how the data looks when it is displayed successfully
Your issue is entirely in your Print method. You've been bitten by creating a slightly over-complicated row tracking mechanism and combining that with magic numbers. This causes you to position each table after the first one row higher than it should be. The header and subtotals are not part of the table, so you have a couple rows of leeway for the error. Tables can't overlap as you've seen, so EPPlus starts barking at you after you've exhausted your leeway.
All you need to do is keep track of the current row that you are writing to, and account for the space taken by your table header and footer (the subtotals) if you use them.
You declare these:
int count = 0;
int previouscount = 0;
var position = 2;
int startposition = 1;
But to write to the correct row, all you need is this:
var rowNumber = 1;
This will properly start writing your data in row one of the Excel sheet. As you write your table rows, you'll track and increment only the rowNumber. But what about the header and footer of each table? If you start writing at the first row of your table you'll overwrite the header, and if you don't account for both the header and footer you'll start having collisions like you've seen. So lets do this:
var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);
These are pretty self explanatory, you'll use the rowAdders to hop the header or footer when needed. rowNumber will always be your current row to create your table and write your data. You use the count when defining your table, but we've made it irrelevant for anything else, so we move it:
var allocationList = allocationGroup.ToList();
//Moved here
var count = allocationList.Count();
Your using statement becomes:
using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
Next, it isn't mentioned in your post, but you are going to run into a problem with the following:
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);
Your table names have to be unique, but you could very well wind up with multiple allocations having the same count, which will cause EPPlus to throw an exception at you for duplicating a table name. So you'll want to also track the index of your current table:
var rowNumber = 1;
var tableIndex = 0;
//...
foreach (var ag in allocation)
{
tableIndex += 1;
//...
}
And use it to ensure unique table names:
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);
We use our format control variables:
// table.ShowHeader = true;
table.ShowFilter = true;
table.ShowTotal = true;
//Changes to
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;
You have a small typo here:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";
//Should be:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,[Allocations])";
After you are done defining your table, you begin writing your data with a foreach loop. In order to prevent overwriting the table header if it exists, we'll have to advance one row. We also have to advance one row for each FIRMWIDE_MANAGER_ALLOCATION. If you are using the subtotals, we have to advance one row after the loop completes in order to properly position the next table:
rowNumber += rowAdderForHeader;
foreach (var ac in allocationGroup)
{
//...
rowNumber += 1;
}
rowNumber += rowAdderForFooter;
And that's it. We now properly track our position using just one variable, and we modify the position as necessary if there is a header or footer on your table.
The following is a complete working example that can be run in LinqPad as long as you add the EPPlus package through Nuget. It creates a random number of allocation groups each with a random number of allocations, and then exports them. Change the output file path to something that works for you:
void Main()
{
var dataGenerator = new DataGenerator();
var allocations = dataGenerator.Generate();
var xlFile = new FileInfo(#"d:\so-test.xlsx");
if (xlFile.Exists)
{
xlFile.Delete();
}
using(var xl = new ExcelPackage(xlFile))
{
FundAllocationsPrinter.Print(xl, allocations);
xl.Save();
}
}
// Define other methods and classes here
public static class FundAllocationsPrinter
{
public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
{
ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
wsSheet1.Protection.IsProtected = false;
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;
var rowNumber = 1;
int tableIndex = 0;
var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);
foreach (var ag in allocation)
{
tableIndex += 1;
Console.WriteLine(tableIndex);
allocationGroup = ag.Select(a => a);
var allocationList = allocationGroup.ToList();
var count = allocationList.Count();
using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
{
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);
//Set Columns position & name
table.Columns[0].Name = "Manager Strategy";
table.Columns[1].Name = "Fund";
table.Columns[2].Name = "Portfolio";
table.Columns[3].Name = "As Of";
table.Columns[4].Name = "EMV (USD)";
table.Columns[5].Name = "Percent";
table.Columns[6].Name = "Allocations";
wsSheet1.Column(1).Width = 45;
wsSheet1.Column(2).Width = 45;
wsSheet1.Column(3).Width = 55;
wsSheet1.Column(4).Width = 15;
wsSheet1.Column(5).Width = 25;
wsSheet1.Column(6).Width = 20;
wsSheet1.Column(7).Width = 20;
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;
//Add TotalsRowFormula into Excel table Columns
table.Columns[0].TotalsRowLabel = "Total Rows";
table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109, [Allocations])";
table.TableStyle = TableStyles.Dark10;
}
//Account for the table header
rowNumber += rowAdderForHeader;
foreach (var ac in allocationGroup)
{
wsSheet1.Cells["A" + rowNumber].Value = ac.MANAGER_STRATEGY_NAME;
wsSheet1.Cells["B" + rowNumber].Value = ac.MANAGER_FUND_NAME;
wsSheet1.Cells["C" + rowNumber].Value = ac.PRODUCT_NAME;
wsSheet1.Cells["D" + rowNumber].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
wsSheet1.Cells["E" + rowNumber].Value = ac.UsdEmv;
wsSheet1.Cells["F" + rowNumber].Value = Math.Round(ac.GroupPercent, 2);
wsSheet1.Cells["G" + rowNumber].Value = Math.Round(ac.WEIGHT_WITH_EQ, 2);
rowNumber++;
}
//Account for the table footer
rowNumber += rowAdderForFooter;
}
}
}
public class FIRMWIDE_MANAGER_ALLOCATION
{
public FIRMWIDE_MANAGER_ALLOCATION(string name, Random rnd)
{
Name = name;
MANAGER_STRATEGY_NAME = "strategy name";
MANAGER_FUND_NAME = "fund name";
PRODUCT_NAME = "product name";
EVAL_DATE = DateTime.Now;
UsdEmv = (decimal)rnd.NextDouble() * 100000000;
GroupPercent = (decimal)rnd.NextDouble() * 100;
WEIGHT_WITH_EQ = 0;
}
public string Name { get; set; }
public string MANAGER_STRATEGY_NAME { get; set; }
public string MANAGER_FUND_NAME { get; set; }
public string PRODUCT_NAME { get; set; }
public DateTime EVAL_DATE { get; set; }
public decimal UsdEmv { get; set; }
public decimal GroupPercent { get; set; }
public decimal WEIGHT_WITH_EQ { get; set; }
}
public class DataGenerator
{
public static Random rnd = new Random();
public ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> Generate()
{
var data = new List<FIRMWIDE_MANAGER_ALLOCATION>();
var itemCount = rnd.Next(1, 100);
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
var name = Path.GetRandomFileName();
data.AddRange(GenerateItems(name));
}
return data.ToLookup(d => d.Name, d => d);
}
private IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> GenerateItems(string name)
{
var itemCount = rnd.Next(1,100);
var items = new List<FIRMWIDE_MANAGER_ALLOCATION>();
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
items.Add(new FIRMWIDE_MANAGER_ALLOCATION(name, rnd));
}
return items;
}
}

How to programmatically create multiple hyperlinks in a single Excel cell

I am building an Excel Spreadsheet using EPPLUS:
I have a datatable for e.g.
CM | ID < columns
VW | 23, 24, 25 < values
foreach (ExcelRangeBase cell in ws2.Cells[2, 2, ToRow, 2])
{
string new_cell;
if (cell.Value == null)
{
continue;
}
else
{
new_cell = cell.Value.ToString();
string link = string.Empty;
string hyperlink = string.Empty;
string[] ints = new_cell.Split(',');
cell.Clear();
for (int i = 0; i < ints.Length; i++)
{
string s = ints[i];
link = #"myapplication/viewCar/ID=" + s;
cell.Hyperlink = new ExcelHyperLink(link) { Display = s };
}
}
}
so what the code does it takes the string as above: 23, 24, 25 comma separates it into an array and loops through, for each it generate a link and create a new cell.hyperlink. So, the issue i'm having is this:
cell.Hyperlink = new ExcelHyperLink(link) { Display = s };
it's only outputting the last element in the array, i know why because when it loops s = last element, what i don't know is how to fix the issue, not sure if this is possible aswell.
Any help would be appreciated.

Populate TextBoxes from a List

I am trying to populate TextBoxes from a list. I have been able to populate ComboBoxes with comboList:
var comboList = new System.Windows.Forms.ComboBox[4];
comboList[0] = cmbSite1Asset;
comboList[1] = cmbSite2Asset;
comboList[2] = cmbSite3Asset;
comboList[3] = cmbSite4Asset;
List<CRCS.CAsset> assets = _rcs.Assets;
foreach (CRCS.CAsset asset in assets)
{
string id = asset.ID;
for (int i = 0; i < 4; ++i)
{
comboList[i].Items.Add(id);
}
}
But when I try and apply the same principle to TextBoxes
var aosList = new System.Windows.Forms.TextBox[8];
aosList[0] = txtAsset1;
aosList[1] = txtAsset2;
aosList[2] = txtAsset3;
aosList[3] = txtAsset4;
aosList[4] = txtAsset5;
aosList[5] = txtAsset6;
aosList[6] = txtAsset7;
aosList[7] = txtAsset8;
foreach (CRCS.CAsset asset in assets)
{
string id = asset.ID;
for (int n = 0; n < 8; ++n)
{
aosList[n].Items.Add(id);
}
}
TextBox does not like Items.Add ( aosList[n]Items.Add(id); )
I am looking fore a reference or guidance resolving this issue. Thanks!
You should use ComboBox for your problem,instead of iterating on each element,You simply use below lines to populate combobox.
comboList.DataSource=assets;
comboList.DisplayMember="ID";
comboList.ValueMember="ID";
However,if you want your values in TextBox,you can use TextBox.AppendText Method, but it will not work like ComboBox as it will contain texts+texts+texts, will not have indexes like ComboBox.
private void AppendTextBoxLine(string myStr)
{
if (textBox1.Text.Length > 0)
{
textBox1.AppendText(Environment.NewLine);
}
textBox1.AppendText(myStr);
}
private void TestMethod()
{
for (int i = 0; i < 2; i++)
{
AppendTextBoxLine("Some text");
}
}
A Combobox is a collection of items, and so has an Items property from which you can add/remove to change it's contents. A Textbox is just a control that displays some text value, so it has a Text property which you can set/get, and which denotes the string that is displayed.
System.Windows.Forms.TextBox[] aosList = new System.Windows.Forms.TextBox[8];
aosList[0] = txtAsset1;
aosList[1] = txtAsset2;
aosList[2] = txtAsset3;
aosList[3] = txtAsset4;
aosList[4] = txtAsset5;
aosList[5] = txtAsset6;
aosList[6] = txtAsset7;
aosList[7] = txtAsset8;
for (int n = 0; n < 8; ++n)
{
aosList[n].Text = assets[n].ID; // make sure you have 8 assets also!
}
int i = 1;
foreach (var asset in assets)
{
this.Controls["txtAsset" + i].Text = asset.ID;
i++;
}

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.

AvalonEdit: Getting Syntax Highlighted Text

I'm registering syntax highlighting with AvalonEdit with:
PythonPrompt.SyntaxHighlighting = pythonHighlighting;
Text can then be input by the user throughout the course of the program. Is there a way to take the formatted text and move it to a TextBlock without loosing the formatting?
As this formatted text will not be edited again I presume it is more efficient to create a TextBlock rather than creating a TextEditor on the fly.
I managed to get something that works. Its based off the latest code for AvalonEdit (HighlightedLine and RichTextModel)
TextBlock Item = new TextBlock();
Code = Code.Replace("\t", new String(' ', Editor.Options.IndentationSize));
TextDocument Document = new TextDocument(Code);
IHighlightingDefinition HighlightDefinition = Editor.SyntaxHighlighting;
IHighlighter Highlighter = new DocumentHighlighter(Document, HighlightDefinition.MainRuleSet);
int LineCount = Document.LineCount;
for (int LineNumber = 1; LineNumber <= Document.LineCount; LineNumber++)
{
HighlightedLine Line = Highlighter.HighlightLine(LineNumber);
string LineText = Document.GetText(Line.DocumentLine);
int Offset = Line.DocumentLine.Offset;
int SectionCount = Line.Sections.Count;
for (int SectionNumber = 0; SectionNumber < SectionCount; SectionNumber++)
{
HighlightedSection Section = Line.Sections[SectionNumber];
//Deal with previous text
if (Section.Offset > Offset)
{
Item.Inlines.Add(
new Run(Document.GetText(Offset, Section.Offset - Offset))
);
}
Run RunItem = new Run(Document.GetText(Section));
if (RunItem.Foreground != null)
{
RunItem.Foreground = Section.Color.Foreground.GetBrush(null);
}
if (Section.Color.FontWeight != null)
{
RunItem.FontWeight = Section.Color.FontWeight.Value;
}
Item.Inlines.Add(RunItem);
Offset = Section.Offset + Section.Length;
}
//Deal with stuff at end of line
int LineEnd = Line.DocumentLine.Offset + LineText.Length;
if (LineEnd > Offset)
{
Item.Inlines.Add(
new Run(Document.GetText(Offset, LineEnd-Offset))
);
}
//If not last line add a new line
if (LineNumber < LineCount)
{
Item.Inlines.Add(new Run("\n"));
}
}

Categories

Resources