I'm just a student trying to get better.
Right now I am developing an application which reads from Excel. For that I have a class, DataReader.
public class DataReader : IDataReader
{
Workbook workbook;
public DataReader()
{
workbook = Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveWorkbook);
}
public Worksheet GetWorksheetByName(string name)
{
Worksheet sheet = (Worksheet)Globals.Factory.GetVstoObject(workbook.Worksheets[name]);
return sheet;
}
}
and this class I am calling from multiple places. Here's an example of how I'm I'm instantiating it in one class
public List<ConfigModel> Instances { get; set; }
public IDataReader dr;
public Worksheet sheet;
public PopulateConfigModel()
{
Instances = new List<ConfigModel>();
dr = new DataReader();
sheet = dr.GetWorksheetByName("Your_Data_Sheet");
}
and here is another example
public class RangeCreator : IRangeCreator
{
IDataReader dr;
Worksheet sheet;
public Xcl.Range GetDestinationRange()
{
dr = new DataReader();
DataHandler dh = new DataHandler();
sheet = dr.GetWorksheetByName("Your_CSV_File");
string range = "A" + ((dh.GetLastRow(sheet) + 1) + ":A" + (dh.GetLastRow(sheet) + 3));
Xcl.Range rng = sheet.Range[range];
return rng;
}
Note that I also calls new DataHandler in that method.
I don't think this is good practice but I don't have a better solution so what's the work around for instantiating this class multiple places? I guess I could go with DI but then I'd have to DI both the DataReader and DataHandler in multiple places and I'm not sure that's a better solution.
I don't know,
UPDATE:
public void BeginProcess()
{
dh = new DataHandler1();
//Returns a List<List<int>> with all used boxsizes {{ 5,1 }, { 15, 3 }} & startCell for each of those boxes eg {{ "B6" }, { "B27" }}
ListHandler lh = dh.GetListHandlerData();
//Holds a list of instances
IPopulateConfigModel pc = new PopulateConfigModel();
//For hver boks vi har i dokumentet
for (int i = 0; i < lh.cellAddresses.Count; i++)
{
//Adds list of instances to list of models
models.Add(pc.PopulateInstances(lh.boxSizes[i], lh.cellAddresses[i]));
}
ConfigModelHandler cm = new ConfigModelHandler(models);
cm.StartModelProcessing(dh);
}
Now I am injecting the DataHandler as a parameter in StartModelProcessing
public void StartModelProcessing(IDataHandler1 dh)
{
IFileSaver fileSaver = new FileSaver();
Worksheet sheet = dr.GetWorksheetByName("Your_CSV_File");
//Each model - each with their own sheet
for (int i = 0; i < models.Count; i++)
{
//Contains all instances of an object
string[][] instances = new string[models[i].Count][];
string filePath = Directory.GetCurrentDirectory() + #"\" + models[i][0].TemplateName + ".csv";
//Clears our sheet so we're ready for a new set of instances
dh.ClearWorksheet(sheet);
//Each instance - sharing their worksheet
for (int j = 0; j < models[i].Count; j++)
{
Xcl.Range rngPopulated = GetPopulatedSourceRange(models[i][j]);
string[] rows = new string[3];
//Get our data from our populated range to our string array. It's in the form of:
/*
* one string array = one instance
* rows[0] = TEMPLATE
* rows[1] = Properties
* rows[2] = Variables
*/
for (int ti = 0; ti < rngPopulated.Rows.Count; ti++)
{
for (int tu = 1; tu < rngPopulated.Rows[ti + 1].Cells.Count; tu++)
{
rows[ti] += rngPopulated.Rows[ti + 1].Cells[tu].Value2 + ",";
}
}
instances[j] = rows;
Xcl.Range testRange = rangeGetter.GetDestinationRange(dh);
//We aint using this sheet - it's only if the user will change and save it manually but only last instance of last document is showing
di.PopulateCsvFile(rows, testRange);
}
fileSaver.SaveFile(instances, filePath);
}
}
and as you can see, injecting it as a parameter again in the rangeGetter.GetDestRange method.
and here's the method
public Xcl.Range GetDestinationRange(IDataHandler1 dh)
{
dr = new DataReader1();
sheet = dr.GetWorksheetByName("Your_CSV_File");
string range = "A" + ((dh.GetLastRow(sheet) + 1) + ":A" + (dh.GetLastRow(sheet) + 3));
Xcl.Range rng = sheet.Range[range];
return rng;
}
One way is to make DataReader class as singleton pattern like:
class DataReader
{
private static DataReader instance = null;
private DataReader()
{
//workbook goes here...
}
public static DataReader Instance
{
get
{
if (instance == null)
instance = new DataReader();
return instance;
}
}
}
And then you can call the method like:
...
sheet = DataReader.Instance.GetWorksheetByName("Your_CSV_File");
Here you not need to use the new keyword every time
please understand
this is very "sudo code" and just here to illiterate that what is more important than trying to avoid "new" is how code is consumed by other code.
your code is hard to comment on as the whole picture cannot be seen... (what belongs to what)
If you don't understand what I'm trying to show below then i will delete, but if you do then great! I have ommited a lot of code to make it easier to understand... well hopefully
class Main
{
DataReader1 _dr;
DataHandler _dh;
public Main()
{
_dr = new DataReader1();
_dh = new DataHandler();
BeginProcess();
}
public void BeginProcess()
{
//...code ommitted
ConfigModelHandler cm = new ConfigModelHandler(models);
cm.StartModelProcessing();
}
public void StartModelProcessing(IDataHandler1 dh)
{
Worksheet sheet = dr.GetWorksheetByName("Your_CSV_File");
//Each model - each with their own sheet
for (int i = 0; i < models.Count; i++)
{
for (int j = 0; j < models[i].Count; j++)
{
Worksheet sheetInner = dr.GetWorksheetByName("Your_CSV_File");
//do stuff with sheet
var range = GetDestinationRange(sheetInner);
}
fileSaver.SaveFile(instances, filePath);
}
}
public Xcl.Range GetDestinationRange(Worksheet sheet)
{
var lastRow = dh.GetLastRow(sheet);
string range = "A" + ((lastRow + 1) + ":A" + (lastRow + 3));
Xcl.Range rng = sheet.Range[range];
return rng;
}
}
Related
namespace WinFormsApp4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Load_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "Excel Files only | *.xlsx; *.xls; *.csv;" ;
ofd.Title = "Choose the file";
if (ofd.ShowDialog() == DialogResult.OK)
label1.Text = ofd.FileName;
}
}
private void Import_Click_1(object sender, EventArgs e)
{
Microsoft.Office.Interop.Excel.Application xlapp;
Microsoft.Office.Interop.Excel.Workbook xlworkbook;
Microsoft.Office.Interop.Excel.Worksheet xlworksheet;
Microsoft.Office.Interop.Excel.Range xlrange;
try
{
xlapp = new Microsoft.Office.Interop.Excel.Application();
xlworkbook = xlapp.Workbooks.Open(label1.Text);
xlworksheet = xlworkbook.Worksheets["List1"];
xlrange = xlworksheet.UsedRange;
DataMore.ColumnCount = xlrange.Columns.Count;
DataMore.ColumnCount = 6;
DataMore.Columns[0].HeaderText = "Datum";
DataMore.Columns[1].HeaderText = "Energia";
DataMore.Columns[2].HeaderText = "AC výkon";
DataMore.Columns[3].HeaderText = "napetie siete";
DataMore.Columns[4].HeaderText = "AC prud";
DataMore.Columns[5].HeaderText = "DC napetie";
foreach (DataGridViewRow row in DataMore.Rows)
{
}
// for (int xlrow = 1; xlrow <= xlrange.Rows.Count; xlrow++)
// {
// DataMore.Rows.Add(xlrange.Cells[xlrow, 1].Text, xlrange.Cells[xlrow, 2].Text, xlrange.Cells[xlrow, 3].Text,
// xlrange.Cells[xlrow, 4].Text);
// }
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
Hi, first of all im totally beginner in C# programing.
I try import numbers from excel into datagridview. All numbers are in 1 Columns and looks like this:
1,2,3,4,5,
6,7,8,9,10
11,12,13,14,15
16,17,18,19,20
All numbers are not real because original excel has numbers that he collect 24/7 past 5 years and its tons of numbers so i create own small excel...
Im just trying to create program where i import numbers from excel, then i delete Number "0" and then trying to create graphs... But im stuck at start, can you help me please?
Your question is about using Microsoft.Office.Interop.Excel to import Excel data to a DataGridView control and say there is a problem where your view stays blank. Looking at your code, I also notice that some of your Excel objects are not being disposed properly, which can lead to multiple instances of Excel running (look in Task Manager).
So, I would like to start from the beginning and look at how to go about this properly in four steps.
Record class
Make a class named Record to represent a row of data.
class Record
{
[DisplayName("Datum")]
public DateTime Datum { get; set; }
[DisplayName("Energia")]
public double Energia { get; set; }
[DisplayName("AC výkon")]
public double ACvýkon { get; set; }
[DisplayName("napetie siete")]
public double napetiesiete { get; set; }
[DisplayName("AC prud")]
public double ACprud { get; set; }
[DisplayName("DC napetie")]
public double DCnapetie { get; set; }
}
Auto-configure DataGridView
Make a BindingList<Record> and attach it to the DataSource property of the data grid view.
public partial class MainForm : Form
{
BindingList<Record> Records { get; } = new BindingList<Record>();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
dataGridView.DataSource = Records;
#region F O R M A T C O L U M N S
Records.Add(new Record()); // <- Auto-generate columns
foreach (DataGridViewColumn column in dataGridView.Columns)
{
column.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
if (column.Index > 1) column.DefaultCellStyle.Format = "F2";
}
Records.Clear();
#endregion F O R M A T C O L U M N S
}
.
.
.
}
Configure Excel Interop create and dispose
public MainForm()
{
InitializeComponent();
// Create
_xlApp = new Microsoft.Office.Interop.Excel.Application();
// When in the future the main form closes, dispose the Excel interop.
Disposed += (sender, e) =>
{
_xlBook?.Close();
_xlApp.Quit();
};
buttonImport.Click += Import_Click_1;
}
private readonly Microsoft.Office.Interop.Excel.Application _xlApp;
private Workbook _xlBook = null;
Import Data (in this case from a predetermined file location)
The pieces come together here. Because of the data source binding of Records when you add to that collection it displays in the data grid view without having to deal with the control itself.
So after opening the workbook and sheet in Excel, capture the range of all used cells and parse that information to make Record instances and add them to the Records collection.
private void Import_Click_1(object sender, EventArgs e)
{
Records.Clear();
string filePath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"Excel",
"testdata.xlsx");
_xlBook = _xlApp.Workbooks.Open(filePath);
Worksheet
xlSheet = _xlBook.Sheets[1];
Range
xlRange = xlSheet.UsedRange,
range;
List<string>
headers = new List<string>(),
line = new List<string>();
for (int i = 1; i <= xlRange.Rows.Count; i++)
{
if (i.Equals(1))
{
for (int j = 1; j <= xlRange.Columns.Count; j++)
{
range = xlRange.Cells[i, j];
headers.Add(range.Value2);
}
}
else
{
var record = new Record();
for (int j = 1; j <= xlRange.Columns.Count; j++)
{
range = xlRange.Cells[i, j];
var name = headers[j - 1];
switch(name)
{
case "Datum": record.Datum = DateTime.FromOADate(range.Value2); break;
case "Energia": record.Energia = range.Value2; break;
case "AC výkon": record.ACvýkon = range.Value2; break;
case "napetie siete": record.napetiesiete = range.Value2; break;
case "AC prud": record.ACprud = range.Value2; break;
case "DC napetie": record.DCnapetie = range.Value2; break;
default:
Debug.Assert(false, $"Not recognized: '{name}'");
break;
}
}
Records.Add(record);
}
}
}
I've been smashing my head against this problem for days and have tried a tons of different things. I've been all over the forums, tried everything I've seen with no luck. My issue could be that I don't have an override, but I can't figure out how to get that to work.
I want to check if an array of 5,000+ elements contains a user-entered word. The word gets entered character by character and combined into a string(guessString). And then I use .Contains() to see if that word is in an array.
***EDIT please see screenshots for debug logs WordArray Elements -- Debug Output -- Debug With whitespace detection -- Code that doesnt work
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Linq;
public class WordAction : MonoBehaviour
{
TMP_Text m_textComponent;
TMP_Text m_currentSquare;
public TMP_Text[] squareArray;
List<string> dupKey = new List<string>();
public string[] WordArray;
public List<string> DictionaryList = new List<string>();
public TextAsset file;
[SerializeField]
Color correctColor, wrongColor, maybeColor;
[SerializeField]
float colorFadeSpeed, colorFadeTime; // 2f, 1f
public float waitTime;
public string levelID;
public string key;
public AudioSource revealSFX;
bool guess;
string guessString;
int guessRegulator;
int guessCount = 1;
int lessGuessCount; // Starts variable at value of first current row element
int maxGuessCount;
string[] guessStringArray = new string[1];
void Start()
{
for (int i = 0; i < 5; i++) // Duplicate key
{
dupKey.Add(key[i].ToString());
}
var content = file.text;
string[] AllWords = content.Split('\n');
WordArray = AllWords;
}
public void Magic()
{
StartCoroutine(CompareKey());
}
IEnumerator CompareKey()
{
guessRegulator++;
GuessRegulatorFunction();
lessGuessCount = (guessCount * 5) - 5; // Starts variable at value of first current row element
maxGuessCount = guessCount * 5;
guessCount++; // Moves to next row
int k = 0; // Indexer for key[]
int cW = 0; // Indexer for CombineWord()
GameObject keyGO; // Keyboard GO
for (int i = lessGuessCount; i < maxGuessCount; i++)
{
if (cW < 1)
{
CombineWord(i);
cW++;
}
bool match = WordArray.Contains(guessString); // not working
Debug.Log(match);
if (match)
{
//do stuff
}
//compare stuff
string guessStr = squareArray[i].text.ToString();
string keyStr = key[k].ToString();
bool result = guessStr == keyStr;
if (!result && !dupKey.Contains(guessStr))
{
//wrong stuff
GameObject parentGO = squareArray[i].transform.parent.gameObject; // Gets parent of SquareArray element
Image parentImage = parentGO.GetComponent<Image>(); // Gets Image component of parent game object
keyGO = GameObject.Find(squareArray[i].text); // Keyboard
Image keyParentImage = keyGO.GetComponent<Image>(); // Keyboard
wrongColor.a = 255;
keyParentImage.color = wrongColor;
parentImage.color = wrongColor;
yield return null;
}
if (result)
{
//correct stuff
dupKey[k] = "";
GameObject parentGO = squareArray[i].transform.parent.gameObject; // Gets parent of SquareArray element
Image parentImage = parentGO.GetComponent<Image>(); // Gets Image component of parent game object
keyGO = GameObject.Find(squareArray[i].text); // Keyboard
Image keyParentImage = keyGO.GetComponent<Image>(); // Keyboard
correctColor.a = 255;
keyParentImage.color = correctColor;
parentImage.color = correctColor;
yield return null;
}
if (!result && dupKey.Contains(guessStr))
{
//yellow stuff
for (int x = 0; x < 5; x++) // Duplicate key
{
if (guessStr == dupKey[x])
{
dupKey[x] = "";
}
}
GameObject parentGO = squareArray[i].transform.parent.gameObject; // Gets parent of SquareArray element
Image parentImage = parentGO.GetComponent<Image>(); // Gets Image component of parent game object
keyGO = GameObject.Find(squareArray[i].text); // Keyboard
Image keyParentImage = keyGO.GetComponent<Image>(); // Keyboard
maybeColor.a = 255;
keyParentImage.color = maybeColor;
parentImage.color = maybeColor;
yield return null;
}
revealSFX.Play();
k++;
yield return new WaitForSeconds(waitTime);
}
dupKey.Clear();
for (int i = 0; i < 5; i++) // Duplicate key
{
dupKey.Add(key[i].ToString());
}
}
void GuessRegulatorFunction()
{
guessRegulator++; // Stops multiple guess attempts
for (int i = 0; i < (guessCount * 5); i++) // Checks if row is blank when guessing
{
if (squareArray[i].text == "")
{
guess = false;
guessRegulator = 0; // Resets guess regulator
break;
}
else
{
guess = true;
}
}
if (guessRegulator > 1 || guess == false) // Stops multiple guess attempts
{
return;
}
}
public void BackSpace()
{
for (int i = guessCount * 5; i > (guessCount * 5) - 6; i--)
{
if (squareArray[i].text != "")
{
squareArray[i].text = "";
break;
}
}
}
public void InputLetter()
{
guessRegulator = 0;
for (int i = 0; i < guessCount * 5; i++)
{
if (squareArray[i].text == "")
{
squareArray[i].text = EventSystem.current.currentSelectedGameObject.name.ToString();
break;
}
}
}
void CombineWord(int i)
{
var string1 = squareArray[i].text.ToString();
var string2 = squareArray[i + 1].text.ToString();
var string3 = squareArray[i + 2].text.ToString();
var string4 = squareArray[i + 3].text.ToString();
var string5 = squareArray[i + 4].text.ToString();
guessString = string1 + string2 + string3 + string4 + string5;
//Debug.Log(guessString);
}
}
I've taken your line of code that isn't working and copied it verbatim. I've then taken the data that you say is in the WordArray and guessString variables and set those up. Then I ran this:
var WordArray = new [] { "WHICH", "THERE", "THEIR", "ABOUT" };
var guessString= "THERE";
bool match = WordArray.Contains(guessString);
Console.WriteLine(match);
match comes out True.
Your variables do not contain the data you think they do.
It's likely that the content that you call .Split('\n') on actually contains Windows end of line markers, so a combination of "\r\n". Since you only split on '\n' it's likely that the '\r' remains in your strings and hence "THERE" does not match "THERE\r".
Try this instead:
.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
If your code is run on either Windows or on Linux the above line works. Just watch out for files that mix the endings.
Well, your WordArray is always empty. You put your file content into a local variable called AllWords.
Your word will never be found in an empty array.
Currently, I am able to read data from multiple CSV file and plot line graph using windows form application. However, now I need to plot a line graph based on a CSV file's section name (3rd column of csv file).
Modified/New CSV file: (Added the Section Name column)
Values,Sector,Name
5.55,1024,red
5.37,1536,red
5.73,2048,blue
5.62,2560,.blue
5.12,3072,.yellow
...
Based on the Section Name column, my line graph need to be plotted accordingly in a Single line and different sections must be plotted with different colors, including the legends shown at the side of the graph must be shown based on the different section names.
1 csv file = 1 Series. But there are same section names in a csv file (csv file example shown above, e.g. red for the 1st 20lines). Same section names = same color. If I open 2 or more csv files = 2 Series. Each Series will have different colors due to different section names in the csv file.
I am quite new with programming, and would really appreciate someone could help me by editing from my code.
Thank you.
Updated code:
GraphDemo (Form):
List<Read> rrList = new List<Read>();
void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog ff = new OpenFileDialog();
Read rr;
ff.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); //"C:\\";
ff.Filter = "csv files (*.csv)|*.csv|All files (*.*)|*.*";
ff.Multiselect = true;
ff.FilterIndex = 1;
ff.RestoreDirectory = true;
if (ff.ShowDialog() == DialogResult.OK)
{
try
{
rrList.Clear();
foreach (String file in ff.FileNames) //if ((myStream = ff.OpenFile()) != null)
{
rr = new Read(file);
rrList.Add(rr);
}
//Populate the ComboBoxes
if (rrList.Count > 0)
{
string[] header = rrList[0].header; //header of first file
xBox.DataSource = header;
yBox.DataSource = header.Clone(); //without Clone the 2 comboboxes link together!
}
if (yBox.Items.Count > 1) yBox.SelectedIndex = 1; //select second item
}
catch (Exception err)
{
//Inform the user if we can't read the file
MessageBox.Show(err.Message);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Plot.Draw(rrList, xBox, yBox, chart);
}
class Read:
public class Read
{
public int nLines { get; private set; }
public int nColumns { get; private set; }
public string[] header { get; private set; }
public float[,] data { get; private set; }
public string fileName { get; set; }
public string[] section { get; private set; }
public Read(string file)
{
string[] pieces;
fileName = Path.GetFileName(file);
string[] lines = File.ReadAllLines(file); // read all lines
if (lines == null || lines.Length < 2) return; //no data in file
header = lines[0].Split(','); //first line is header
nLines = lines.Length - 1; //first line is header
nColumns = header.Length;
//read the numerical data and section name from the file
data = new float[nLines, nColumns - 1]; // *** 1 less than nColumns as last col is sectionName
section = new string[nLines]; // ***
for (int i = 0; i < nLines; i++)
{
pieces = lines[i + 1].Split(','); // first line is header
if (pieces.Length != nColumns) { MessageBox.Show("Invalid data at line " + (i + 2) + " of file " + fileName); return; }
for (int j = 0; j < nColumns - 1; j++)
{
float.TryParse(pieces[j], out data[i, j]); //data[i, j] = float.Parse(pieces[j]);
}
section[i] = pieces[nColumns - 1]; //last item is section
}
}
}
class Plot:
public class Plot
{
//public Plot() { } //no constructor required as we use a static class to be called
public static void Draw(List<Read> rrList, ComboBox xBox, ComboBox yBox, Chart chart) //***
{
int indX = xBox.SelectedIndex;
int indY = yBox.SelectedIndex;
chart.Series.Clear(); //ensure that the chart is empty
chart.Legends.Clear();
Legend myLegend = chart.Legends.Add("myLegend");
myLegend.Title = "myTitle";
//define a set of colors to be used for sections
Color[] colors = new Color[] { Color.Black, Color.Blue, Color.Red, Color.Green, Color.Magenta, Color.DarkCyan, Color.Chocolate, Color.DarkMagenta };
//use a Dictionary to keep iColor of each section
// key=sectionName, value=iColor (color index in our colors array)
var sectionColors = new Dictionary<string, int>();
int i = 0;
int iColor = -1, maxColor = -1;
foreach (Read rr in rrList)
{
float[,] data = rr.data;
int nLines = rr.nLines;
int nColumns = rr.nColumns;
string[] header = rr.header;
chart.Series.Add("Series" + i);
chart.Series[i].ChartType = SeriesChartType.Line;
//chart.Series[i].LegendText = rr.fileName;
chart.Series[i].IsVisibleInLegend = false; //hide default item from legend
chart.ChartAreas[0].AxisX.LabelStyle.Format = "{F2}";
chart.ChartAreas[0].AxisX.Title = header[indX];
chart.ChartAreas[0].AxisY.Title = header[indY];
for (int j = 0; j < nLines; j++)
{
int k = chart.Series[i].Points.AddXY(data[j, indX], data[j, indY]);
string curSection = rr.section[j];
if (sectionColors.ContainsKey(curSection))
{
iColor = sectionColors[curSection];
}
else
{
maxColor++;
iColor = maxColor; sectionColors[curSection] = iColor;
}
chart.Series[i].Points[k].Color = colors[iColor];
}
i++; //series#
} //end foreach rr
//fill custom legends based on sections/colors
foreach (var x in sectionColors)
{
string section = x.Key;
iColor = x.Value;
myLegend.CustomItems.Add(colors[iColor], section); //new LegendItem()
}
}
}
You can separate the data by the section column and use the section names as index into the Series collection instead of using i.
Best use the section name as the Series.Name. I suggest using a data class containing the two numbers and the string and collect them in a List<Dataclass>. Then create Series for the distinct sections. Then loop over them..
Here are a few code examples:
Define a class for your data:
public class Data3
{
public int N1 { get; set;}
public double N2 { get; set;}
public string S1 { get; set;}
public Data3(double n2, int n1, string s1)
{
N1 = n1; N2 = n2; S1 = s1;
}
}
Pick your own names! Optional but always recommended: Add a nice ToString() overload!
Declare a class level varible:
List<Data3> data = new List<Data3>();
During the read collect the data there:
data.Add(new Data3(Convert.ToDouble(pieces[1]), Convert.ToInt32(pieces[0]), pieces[2]));
To plot the chart first create the Series:
var sections= data.Select(x => x.S1).Distinct<string>();
foreach (string s in sections)
chart.Series.Add(new Series(s) { ChartType = SeriesChartType.Line });
Then plot the data; the series can be indexed by their Names:
foreach (var d in data) chart.Series[d.S1].Points.AddXY(d.N1, d.N2);
I left out the nitty gritty of integrating the code into your application; if you run into issues, do show the new code by editing your question!
A few notes:
When in doubt always create a class to hold your data
When in doubt always choose classes over structures
When in doubt always choose List<T> over arrays
Always try to break your code down to small chunks with helpful names.
Example: To read all the data in a csv file create a function to do so:
public void AppendCsvToDataList(string file, List<Data3> list)
{
if (File.Exists(file))
{
var lines = File.ReadAllLines(file);
for (int l = 1; l < lines.Length; l++)
{
var pieces = lines[l].Split(',');
list.Add(new Data3(Convert.ToInt32(pieces[1]),
Convert.ToDouble(pieces[0]), pieces[2]));
}
}
}
UPDATE: So this code is collection a SQL Query into a DataSet prior to this method. This data set is then dropped into excel in the corresponding tab at a specific cell address(which is loaded from the form) but the code below is the exporting to excel method. I am getting the following error:
An unhandled exception of type 'System.AccessViolationException' occurred in SQUiRE (Sql QUery REtriever) v1.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I have been tracking this for a while and thought I fixed it, but my solution was a false positive. So I am using a try...catch block that is breaking but not returning anything. Let me know if you all see anything that I am missing. I usually break on this line (templateSheet = templateBook.Sheets[tabName];) and on the same tabName. The tab is not locked or restricted so It can be written to and works more than half of the time.
public void ExportToExcel(DataSet dataSet, Excel.Workbook templateBook, int i, int h, Excel.Application excelApp) //string filePath,
{
try
{
lock (this.GetType())
{
Excel.Worksheet templateSheet;
//check to see if the template is already open, if its not then open it,
//if it is then bind it to work with it
//if (!fileOpenTest)
//{ templateBook = excelApp.Workbooks.Open(filePath); }
//else
//{ templateBook = (Excel.Workbook)System.Runtime.InteropServices.Marshal.BindToMoniker(filePath); }
//Grabs the name of the tab to dump the data into from the "Query Dumps" Tab
string tabName = lstQueryDumpSheet.Items[i].ToString();
templateSheet = templateBook.Sheets[tabName];
// Copy DataTable
foreach (System.Data.DataTable dt in dataSet.Tables)
{
// Copy the DataTable to an object array
object[,] rawData = new object[dt.Rows.Count + 1, dt.Columns.Count];
// Copy the values to the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
for (int row = 0; row < dt.Rows.Count; row++)
{ rawData[row, col] = dt.Rows[row].ItemArray[col]; }
}
// Calculate the final column letter
string finalColLetter = string.Empty;
string colCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int colCharsetLen = 26;
if (dt.Columns.Count > colCharsetLen)
{ finalColLetter = colCharset.Substring((dt.Columns.Count - 1) / colCharsetLen - 1, 1); }
finalColLetter += colCharset.Substring((dt.Columns.Count - 1) % colCharsetLen, 1);
/*Grabs the full cell address from the "Query Dump" sheet, splits on the '=' and
*pulls out only the cell address (i.e., "address=a3" becomes "a3")*/
string dumpCellString = lstQueryDumpText.Items[i].ToString();
string dumpCell = dumpCellString.Split('=').Last();
/*Refers to the range in which we are dumping the DataSet. The upper right hand cell is
*defined by 'dumpCell'and the bottom right cell is defined by the final column letter
*and the count of rows.*/
string firstRef = "";
string baseRow = "";
//Determines if the column is one letter or two and handles them accordingly
if (char.IsLetter(dumpCell, 1))
{
char[] createCellRef = dumpCell.ToCharArray();
firstRef = createCellRef[0].ToString() + createCellRef[1].ToString();
for (int z = 2; z < createCellRef.Count(); z++)
{ baseRow = baseRow + createCellRef[z].ToString(); }
}
else
{
char[] createCellRef = dumpCell.ToCharArray();
firstRef = createCellRef[0].ToString();
for (int z = 1; z < createCellRef.Count(); z++)
{ baseRow = baseRow + createCellRef[z].ToString(); }
}
int baseRowInt = Convert.ToInt32(baseRow);
int startingCol = ColumnLetterToColumnIndex(firstRef);
int endingCol = ColumnLetterToColumnIndex(finalColLetter);
int finalCol = startingCol + endingCol;
string endCol = ColumnIndexToColumnLetter(finalCol - 1);
int endRow = (baseRowInt + (dt.Rows.Count - 1));
string cellCheck = endCol + endRow;
string excelRange;
if (dumpCell.ToUpper() == cellCheck.ToUpper())
{ excelRange = string.Format(dumpCell + ":" + dumpCell); }
else
{ excelRange = string.Format(dumpCell + ":{0}{1}", endCol, endRow); }
//Dumps the cells into the range on Excel as defined above
templateSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
/*Check to see if all the SQL queries have been run from
if (i == lstSqlAddress.Items.Count - 1)
{
//Turn Auto Calc back on
excelApp.Calculation = Excel.XlCalculation.xlCalculationAutomatic;
/*Run through the value save sheet array then grab the address from the corresponding list
*place in the address array. If the address reads "whole sheet" then save the whole page,
*else set the addresses range and value save that.
for (int y = 0; y < lstSaveSheet.Items.Count; y++)
{
MessageBox.Show("Save Sheet: " + lstSaveSheet.Items[y] + "\n" + "Save Address: " + lstSaveRange.Items[y]);
}*/
//run the macro to hide the unused columns
excelApp.Run("ReportMakerExecute");
//save excel file as hospital name and move onto the next
SaveTemplateAs(templateBook, h);
}
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
I'm trying to build Quad-tree which receive array of points and split them into 4 cells. those 4 cells(cell1...cell4) are suppose to be created recursively but it doesn't works.
What I'm doing wrong? wasted all day trying fix it.
I erased some basic things not to overload the script here.
class QuadTreeNode
{
private QuadTreeNode cell1;
private QuadTreeNode cell2;
private QuadTreeNode cell3;
private QuadTreeNode cell4;
private int maxppc;
private double leftminX, leftminY, rightmaxX, rightmaxY;
public QuadTreeNode(Point2D leftMin, Point2D rightMax, int maxPPC)
{
this.leftminX = leftMin.East;
this.leftminY = leftMin.North;
this.rightmaxX = rightMax.East;
this.rightmaxY = rightMax.North;
this.maxppc = maxPPC;
}
public QuadTreeNode(Point2D[] pointArray, Point2D leftMin, Point2D rightMax, int maxPPC)
{
for (int i=0; i <4; i++)
{
cellList[i] = new List<Point2D>();
}
allPointList = pointArray.ToList();
this.leftminX = leftMin.East;
this.leftminY = leftMin.North;
this.rightmaxX = rightMax.East;
this.rightmaxY = rightMax.North;
this.maxppc = maxPPC;
if (allPointList.Count > maxPPC)
{
Split(allPointList);
}
}
public void Split(List<Point2D> Array)
{
if (Array.Count > maxppc)
{
double centerE = (this.leftminX + this.rightmaxX) / 2;
double centerN = (this.leftminY + this.rightmaxY) / 2;
double deltaE = (this.rightmaxX - this.leftminX) / 2;
double deltaN = (this.rightmaxY - this.leftminY) / 2;
Point2D Center = new Point2D(centerE, centerN);
this.cell1 = new QuadTreeNode(cellList[0].ToArray(),new Point2D((Center.East - deltaE), (Center.North - deltaN)), Center, maxppc);
this.cell2 = new QuadTreeNode(cellList[1].ToArray(), new Point2D(Center.East, Center.North - deltaN), new Point2D((Center.East + deltaE), Center.North), maxppc);
this.cell3 = new QuadTreeNode(cellList[2].ToArray(), new Point2D((Center.East - deltaE), (Center.North)), new Point2D(Center.East, (Center.North + deltaN)), maxppc);
this.cell4 = new QuadTreeNode(cellList[3].ToArray(), Center, new Point2D((Center.East + deltaE), (Center.North + deltaN)), maxppc);
for (pntIndex = 0; pntIndex < Array.Count; pntIndex++)
{
CellIndex(Array[pntIndex]);
}
pntIndex = 0;
Array.Clear();
for (int c=0; c < 4; c++)
{
Array = cellList[c].ToList();
cellList[0].Clear();
cellList[1].Clear();
cellList[2].Clear();
cellList[3].Clear();
Split(Array);
}
return;
}
else
{
return;
}
}
public void CellIndex(Point2D point)
{
//locates points up to East,North
}
The problem may be with this part:
Array = cellList[c].ToList();
cellList[0].Clear();
cellList[1].Clear();
cellList[2].Clear();
cellList[3].Clear();
Split(Array);
Here, you're copying the list at location c but you're clearing each list at every iteration anyway, so you're likely losing some of your points.