Evening all,
To the point: Within my WPF application I would like to display data from an access database within a listbox in the format of a ToString method created within another class. -- I can display the data, but it does not contain formatting.
Context of my question:
I am creating an application for my graded unit at college which adds, deletes and displays data from an access database. I am having no trouble with adding or deleting data to the database, however, I am struggling to display the data in a particular format.
Due to specific requirements, I have had to create an abstract Games class, with the subclasses Platform and Mobile (games).
I would like to know how to display data from an access database in a listbox (though this is flexible to change), whilst formatting the content to a previously created ToString() method in both the Platform and Mobile class. I understand that I may have to create two separate methods to display platform and mobile games, as they each have an additional variable.
Currently, I am storing my listPlatform() method within my Catalogue class, which is accessed from a separate window (EmployeeWindow, which contains the list view box, then accessing this method and calling it via a button_click event.
Catalogue class --
public List<string> listPlatform()
{
List<string> data = new List<string>();
string queryString = "SELECT ID, Game_Name, Developer, Publisher, Genre, Age_Rating, Price, Quantity, Description, Platform FROM GameDetails";
using (OleDbConnection connection = new OleDbConnection(ConnString))
{
OleDbCommand command = new OleDbCommand(queryString, connection);
connection.Open();
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int id = reader.GetInt32(0);
string gName = reader.GetString(1);
string gDeveloper = reader.GetString(2);
string gPublisher = reader.GetString(3);
string gGenre = reader.GetString(4);
int gAgeRating = reader.GetInt32(5);
var gPrice = reader.GetValue(6);
var gQuantity = reader.GetValue(7);
var gDescription = reader.GetValue(8);
var gPlatform = reader.GetValue(9);
data.Add(id + gName + gDeveloper + gPublisher + gGenre + gAgeRating + gPrice
+ gQuantity + gDescription + gPlatform);
}
reader.Close();
}
return data;
}
EmployeeWindow --
private void btnDisplay_Click(object sender, RoutedEventArgs e)
{
List<string> data = theCatalogue.listPlatform();
lstvwGames.Items.Clear();
foreach (string s in data)
{
lstvwGames.Items.Add(s);
}
}
Platform class --
/// <summary>
/// Returns a string representation of a Platform game
/// </summary>
/// <returns></returns>
public override string ToString()
{
string strout = string.Format(base.ToString() + "Platform:{0}", platform);
return strout;
}
I hope that my question makes sense and that I have provided enough information to give you some understanding of what it is that I am trying to do.
I think that, in this scenario, you need to add another class to your code.
The Game class that you could model looking at the fields present in your database table
public class Game
{
public int ID {get;set;}
public string GameName {get;set;}
public string Developer {get;set;}
public string Publisher {get;set;}
public string Genre {get;set;}
public string Age_Rating {get;set;}
public decimal Price {get;set;}
public int Quantity {get;set;}
public string Description {get;set;}
public string Platform {get;set;}
public override string ToString()
{
return this.GameName + " - " + this.Genre;
}
}
Now in the Catalogue class, when you read your database you build a List<Game> not a List<String>
public List<Game> listPlatform()
{
.....
List<Game> games = new List<Game>();
while (reader.Read())
{
Game g = new Game();
g.ID = reader.GetInt32(0);
g.GameName = reader.GetString(1);
... and so on for the rest of fields
games.Add(g);
}
...
return games;
}
Finally, when you need to add the games to your ListBox, you could write
private void btnDisplay_Click(object sender, RoutedEventArgs e)
{
List<Game> data = theCatalogue.listPlatform();
lstvwGames.Items.Clear();
foreach (Game g in data)
{
lstvwGames.Items.Add(g.ToString());
}
}
And you have a list filled with GameName and Genre.
EDIT to complete this answer,
Finally, you could directly set the DataSource, DisplayMember and ValueMember properties of the ListBox with your List<Game> and some of its properties removing the need to have a loop to fill items
private void btnDisplay_Click(object sender, RoutedEventArgs e)
{
lstvwGames.ValueMember = "ID";
lstvwGames.DisplayMember = "GameData";
lstvwGames.DataSource = theCatalogue.listPlatform();
}
In this example the DisplayMember property is assigned to a new GameData field that you should define inside your Game class. This new readonly property could be the actual return value of the ToString method of the same class or another value of your choice
public class Game
{
.....
public string GameData
{
// Only a getter, thus readonly
get
{
return this.ToString();
}
}
}
Of course you could change the ToString method or the GameData property inside the Game class to return the info you really want to display in the listbox.
I do not see you using the tostring override method. Maybe you meant to use it here?
lstvwGames.Items.Add(s.ToString());
Related
I am developing a C# Android that takes the user input and add it to a database, then in another Activity, it displays the user input with an option to edit his input again.
So I have 2 activities and 1 public class which links them together. I am using SQLLite to save the user input into a database (in the MainActivity.cs) then (in the secondActivity) it retrieves the saved value (which is the user input) from the public database (located in the public class called Class1) and displays it in a Textview.
Class1.cs
namespace App
{
public class Class1
{
public static string dpPath= Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "user.db3");
public void Insert(string Quantity, string name)
{
var db = new SQLiteConnection(Class1.dpPath);
var logintable = new LoginTable();
logintable.quantity = Quantity;
logintable.name = name;
db.Insert(logintable);
}
public void edit(string Quantity)
{
var db = new SQLiteConnection(Class1.dpPath);
var logintable = new LoginTable();
logintable.quantity = Quantity;
db.Update(logintable);
}
public void delete(int id)
{
var db = new SQLiteConnection(Class1.dpPath);
var logintable = new LoginTable();
logintable.id = id;
db.Delete(logintable);
}
public Class1()
{
//Creating database, if it doesn't already exist
if (!File.Exists(Class1.dpPath))
{
var db = new SQLiteConnection(Class1.dpPath);
db.CreateTable<LoginTable>();
}
}
public class LoginTable
{
[PrimaryKey, AutoIncrement, Column("_Id")]
public int id { get; set; } // AutoIncrement and set primarykey
[MaxLength(25)]
public string quantity { get; set; }
[MaxLength(15)]
public string name { get; set; }
public LoginTable()
{
}
}
}
MainActivity.cs
Class1 cl = new Class1():
cl.Insert(input.ToString(), name.ToLower());
SecondActivity.cs
Class1 cl = new Class1():
cl.edit(input.ToString());
var db = new SQLiteConnection(Class1.dpPath);
var table = db.Table<Class1.LoginTable>();
foreach( var item in table) {
textView.Text += item.name + " " + item.quantity;
}
Well, I am getting in the textview in the secondActivity, the input that was entered the first time (in the MainActivity) and not the one which was edited later in the SecondActivity. I thought that maybe because I have created two different instance of the Class1 and each one is working with a different Table. In addition, I have tried to initialise a public static instance of the Class1 inside the Class1 itself like that:
public class Class1 { public static Class1 cl = new Class1(); }
but did not work either, the textview is still displaying the original input and not the edited one. I need to be able to edit the database from each activitie.. Please help me to find a solution.
UPDATE
I have created a new class Class2 and I have initialise inside it a new instance of Class1 like that:
public class Class2
{
public static Class1 cl = new Class1();
}
And then i have tried to access this instance of the class1 in the Main and second activity, so in the Main my code are:
Class2.cl.Insert(input.ToString(), name.ToLower());
and in the second activity my code are:
Class2.cl.edit(input.ToString());
var db = new SQLiteConnection(Class1.dpPath);
var table = db.Table<Class1.LoginTable>(); // The issue is in this line
foreach( var item in table) {
textView.Text += item.name + " " + item.quantity;
}
So now the issue I think is in the secondActivity, the var table is getting only one table which is the one where the original input in the Main activity is stored, and when updating the value in the second activity, it is not considering the second table which stores the edited input. But still i don't know how to solve this.
On first glance it appears that your Edit() method is not correct. You have this:
public void edit(string Quantity)
{
var db = new SQLiteConnection(Class1.dpPath);
var logintable = new LoginTable();
logintable.quantity = Quantity;
db.Update(logintable);
}
But you have no way to quantify which item in the database that you wish to update; you're just creating a new item and then trying to update the table with that item. You should instead add some way to select the item you wish to edit, like this example:
public void Edit(int id, string quantity)
{
var db = new SQLiteConnection(Class1.dbPath);
var table = db.Table<Class1.LoginTable>();
var itemToEdit = table.First(f=>f.Id == id);
itemToEdit.quantity = quantity;
db.Update(itemToEdit);
}
As you can see, the example above gets the item from your table, edits the item and updates your table with the data you gave it.
Your Delete() method should follow a similar path, instead of instantiating a new object just to delete it.
Also about your update, you should consider researching the Singleton pattern. Here is a better way to handle a static object than your Class1/Class2 implementation. In your Class1 class, add the field:
public static readonly Class1 Instance = new Class1()
And then reference your instance from other classes like this:
Class1.Instance.Insert(input.ToString(), name.ToLower());
This way there is no way for you to accidentally create a new instance of Class1.
I hope someone can help me out with an obscure problem that has me absolutely flummoxed.
I wish to set up a DataGridView, which allows a user to select an option from a DataGridViewComboBoxColumn, and for the object which is a datasource for the DataGridView, to be updated with the object that the user pick in the ComboBox.
[NB I wish the DataGridViewComboBoxColumn to show more than a single property in the dropdown, hence why I am using a DataTable as the DataGridViewComboBoxColumn datasource. In other words, I want them to see a description which is a combination of other properties concatenated together.]
My code works, but when I click outside the ComboBox cell, the value gets automatically set (it gets set back to the first item in BindingList). I cannot get it to stick to the value that the user selected. Ultimately the wrong object instance gets assigned to the DataGridView's dataSource.
I would post a pic, but I don't have enough rep.
So my question is, why does the DataGridViewComboBoxColumn switch to the first item in its datasource (a DataTable) when I click outside the cell. How can I get it to keep the option I selected, in the cell. I am absolutely BAFFLED.
I hope it's okay to post this much code up to the StackOverflow website without it annoying everyone. I've tried to create suitably generic example to help any other souls trying to find out how to select and assign an object to the property of another object, using a DataGridViewComboBoxColumn. So hopefully this is of use to someone else. I've also tried to make it relatively easy to recreate, should someone need to solve this kind of problem.
I've tried all manner of things, including using a List as a datasource of the DataGridViewComboBoxColumn, doing things with the CurrentCellDirtyStateChanged event - all to no avail.
Here's my 2 classes:
public class CarPartChoice
{
public string Name { get; set; }
public int Value { get; set; }
public string Comment {get; set;}
public CarPartChoice(string name, int value, string comment)
{
Name = name;
Value = value;
Comment = comment;
}
}
public class Car
{
public string Manufacturer { get; set; }
public string Model { get; set; }
public CarPartChoice WheelChoice {get; set;}
public Car(string maker, string model, CarPartChoice wheel)
{
Manufacturer = maker;
Model = model;
WheelChoice = wheel;
}
public Car(string maker, string model)
{
Manufacturer = maker;
Model = model;
}
}
public static class GlobalVariables
{
public static BindingList<CarPartChoice> GlobalChoiceList { get; set; }
public static BindingList<Car> GlobalCarsList { get; set; }
}
And here's me creating my dataGridView:
private void Form1_Load(object sender, EventArgs e)
{
//Setup the wheel choices to be selected from the DataGridViewComboBoxColumn.
CarPartChoice myWheelChoice = new CarPartChoice("Chrome", 19, "This is the chromes wheels option.");
CarPartChoice myWheelChoice2 = new CarPartChoice("HubCaps", 16, "This is the nasty plastic hubcaps option.");
BindingList<CarPartChoice> tempBLChoice = new BindingList<CarPartChoice>();
tempBLChoice.Add(myWheelChoice);
tempBLChoice.Add(myWheelChoice2);
GlobalVariables.GlobalChoiceList = tempBLChoice;
//Setup the cars to populate the datagridview.
Car car1 = new Car("Vauxhall", "Astra");
Car car2 = new Car("Mercedes", "S-class");
BindingList<Car> tempListCars = new BindingList<Car>();
tempListCars.Add(car1);
tempListCars.Add(car2);
GlobalVariables.GlobalCarsList = tempListCars;
dataGridView1.AutoGenerateColumns = false;
dataGridView1.CurrentCellDirtyStateChanged += new EventHandler(dataGridView1_CurrentCellDirtyStateChanged);
// Set up 2 DataGridViewTextBox columns, one to show the manufacturer and the other to show the model.
DataGridViewTextBoxColumn manufacturer_col = new DataGridViewTextBoxColumn();
manufacturer_col.DataPropertyName = "Manufacturer";
manufacturer_col.Name = "Manufacturer";
manufacturer_col.HeaderText = "Manufacturer";
DataGridViewTextBoxColumn model_col = new DataGridViewTextBoxColumn();
model_col.DataPropertyName = "Model";
model_col.Name = "Model";
model_col.HeaderText = "Model";
// Create a DataTable to hold the Wheel options available for the user to choose from. This DT will be the DataSource for the
// ...combobox column
DataTable wheelChoices = new DataTable();
DataColumn choice = new DataColumn("Choice", typeof(CarPartChoice));
DataColumn choiceDescription = new DataColumn("Description", typeof(String));
wheelChoices.Columns.Add(choice);
wheelChoices.Columns.Add(choiceDescription);
foreach (CarPartChoice wheelchoice in GlobalVariables.GlobalChoiceList)
{
wheelChoices.Rows.Add(wheelchoice, wheelchoice.Name + " - " + wheelchoice.Value.ToString() + " - " + wheelchoice.Comment);
}
// Create the Combobox column, populated with the wheel options so that user can pick one.
DataGridViewComboBoxColumn wheelOption_col = new DataGridViewComboBoxColumn();
wheelOption_col.DataPropertyName = "WheelChoice";
wheelOption_col.Name = "WheelChoice";
wheelOption_col.DataSource = wheelChoices;
wheelOption_col.ValueMember = "Choice";
wheelOption_col.DisplayMember = "Description";
wheelOption_col.ValueType = typeof(CarPartChoice);
// Add the columns and set the datasource for the DGV.
dataGridView1.Columns.Add(manufacturer_col);
dataGridView1.Columns.Add(model_col);
dataGridView1.Columns.Add(wheelOption_col);
dataGridView1.DataSource = GlobalVariables.GlobalCarsList;
}
UPDATE:
I have tried to use a "BindingSource" object and setting the datasource of the BindingSource to the DataTable. That made no difference. I've also tried getting the Car to implement the "INotifyPropertyChanged" interface and that didn't make any difference.
The one thing if I've noticed is that the Car item in the datagridview DOES get its WheelChoice property updated. The Car objects IS INDEED updated with the value I've selected from the ComboBox. I think this is a just a display issue and the DataGridViewComboBoxColumn just isn't populated with the correct value. Still baffled.
Thanks to those who left comments. I found the answer in the end.
The answer is for me simply to define an overridden "ToString()" method for my "CarPartChoice" class (i.e. the class which is used to provide the items to be looked-up by clicking on the ComboBoxColumn). I guess it was having trouble formatting my cell with a nice string when I moved control away from it.
So, for anyone who ever wants to do this in future, here's a full example of how you might use a datagridview to update a list of objects of a certain class, and using a DataGridViewComboBoxColumn to provide the user with a choice of objects, and for their selected object (which they chose from the dropdown), to be populated into the list (to be precise: in the field of an object in the list which is of the type of object which gets selected by the user in the dropdown). Yes, I know that's a very horrible sentence.
Here's the complete code which would accomplish this task (I would have thought it was something that people would often want to do).
Kindest regards to all.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//Setup the wheel choices to be selected from the DataGridViewComboBoxColumn.
CarPartChoice myWheelChoice = new CarPartChoice("Chrome", 19, "This is the chromes wheels option.");
CarPartChoice myWheelChoice2 = new CarPartChoice("HubCaps", 16, "This is the nasty plastic hubcaps option.");
CarPartChoice myWheelChoice3 = new CarPartChoice("Iron", 15, "These are metal wheels.");
CarPartChoice myWheelChoice4 = new CarPartChoice("Spoked", 15, "This is the fancy classic hubcaps option.");
CarPartChoice myWheelChoice5 = new CarPartChoice("solid", 13, "This wheels has no spokes or holes.");
CarPartChoice myWheelChoice6 = new CarPartChoice("SpaceHubCaps", 17, "Newly developed space hubcaps.");
BindingList<CarPartChoice> tempBLChoice = new BindingList<CarPartChoice>();
tempBLChoice.Add(myWheelChoice);
tempBLChoice.Add(myWheelChoice2);
tempBLChoice.Add(myWheelChoice3);
tempBLChoice.Add(myWheelChoice4);
tempBLChoice.Add(myWheelChoice5);
tempBLChoice.Add(myWheelChoice6);
GlobalVariables.GlobalChoiceList = tempBLChoice;
//Setup the cars to populate the datagridview.
Car car1 = new Car("Vauxhall", "Astra");
Car car2 = new Car("Mercedes", "S-class");
BindingList<Car> tempListCars = new BindingList<Car>();
tempListCars.Add(car1);
tempListCars.Add(car2);
GlobalVariables.GlobalCarsList = tempListCars;
dataGridView1.AutoGenerateColumns = false;
dataGridView1.CurrentCellDirtyStateChanged += new EventHandler(dataGridView1_CurrentCellDirtyStateChanged);
// Set up 2 DataGridViewTextBox columns, one to show the manufacturer and the other to show the model.
DataGridViewTextBoxColumn manufacturer_col = new DataGridViewTextBoxColumn();
manufacturer_col.DataPropertyName = "Manufacturer";
manufacturer_col.Name = "Manufacturer";
manufacturer_col.HeaderText = "Manufacturer";
DataGridViewTextBoxColumn model_col = new DataGridViewTextBoxColumn();
model_col.DataPropertyName = "Model";
model_col.Name = "Model";
model_col.HeaderText = "Model";
// Create a DataTable to hold the Wheel options available for the user to choose from. This DT will be the DataSource for the
// ...combobox column
DataTable wheelChoices = new DataTable();
DataColumn choice = new DataColumn("Choice", typeof(CarPartChoice));
DataColumn choiceDescription = new DataColumn("Description", typeof(String));
wheelChoices.Columns.Add(choice);
wheelChoices.Columns.Add(choiceDescription);
foreach (CarPartChoice wheelchoice in GlobalVariables.GlobalChoiceList)
{
wheelChoices.Rows.Add(wheelchoice, wheelchoice.Name + " - " + wheelchoice.Value.ToString() + " - " + wheelchoice.Comment);
}
// Create the Combobox column, populated with the wheel options so that user can pick one.
DataGridViewComboBoxColumn wheelOption_col = new DataGridViewComboBoxColumn();
wheelOption_col.DataPropertyName = "WheelChoice";
wheelOption_col.DataSource = wheelChoices;
wheelOption_col.ValueMember = "Choice";
wheelOption_col.DisplayMember = "Description";
wheelOption_col.ValueType = typeof(CarPartChoice);
// Add the columns and set the datasource for the DGV.
dataGridView1.Columns.Add(manufacturer_col);
dataGridView1.Columns.Add(model_col);
dataGridView1.Columns.Add(wheelOption_col);
dataGridView1.DataSource = GlobalVariables.GlobalCarsList;
}
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
var grid = sender as DataGridView;
if (grid.IsCurrentCellDirty)
grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
public class CarPartChoice
{
public string Name { get; set; }
public int Value { get; set; }
public string Comment { get; set; }
public CarPartChoice(string name, int value, string comment)
{
Name = name;
Value = value;
Comment = comment;
}
public override string ToString()
{
return Name.ToString() + " - " + Value.ToString() + " - " + Comment.ToString();
}
}
public class Car
{
public string Manufacturer { get; set; }
public string Model {get; set; }
public CarPartChoice WheelChoice { get; set; }
public Car(string maker, string model, CarPartChoice wheel)
{
Manufacturer = maker;
Model = model;
WheelChoice = wheel;
}
public Car(string maker, string model)
{
Manufacturer = maker;
Model = model;
}
}
public static class GlobalVariables
{
public static BindingList<CarPartChoice> GlobalChoiceList { get; set; }
public static BindingList<Car> GlobalCarsList { get; set; }
}
I too had the same problem but with a different dataset. I was trying to add a keyvalue pair to the combobox and everytime I click outside, it got reset to the first item. What I was missing was that I hadn't set displayMember property of the combobox.
(Hope this would help if any others are here for the same problem)
Basically i have the user open a text document that is formatted like this currently.
Burger.jpg,Double Down KFC,Food,30/06/95,This is a burger
it then splits the info into an array then into variables and then into text boxes.
obviously if i wanted multiple records i may have to format it differently, (thats what i need help with)
But if i had it like this what would be the most efficient way of taking these records from the text file and storing them separately so i can flick through them. For example with a combo box on my form. When the record is selected the form populates with that records data.
multiple records:
Burger.jpg,Double Down KFC,Food,30/06/95,This is a burger
Person.jpg,Smile,People,23/06/95,This is a Person
Here is my code currently for this part.
private void LoadFile()
{
StreamReader reader = new StreamReader(fileName);
content = reader.ReadLine();
doc = content.Split(',');
filename = Convert.ToString(doc[0]);
fileNameTextBox.Text = doc[0];
description = doc[1];
descriptionTextBox.Text = doc[1];
category = doc[2];
categoryComboBox.Text = doc[2];
//dateTaken = Convert.ToDouble(doc[3]);
dateTakenTextBox.Text = doc[3];
comments = doc[4];
commentsTextBox.Text = doc[4];
}
This code currently works but only for the first record as it is using one array, and i obviously will need multiple ways of storing the other lines.
I Think the best option if i was going to give it a guess would be to use a List of some sort with a Class that generates Records, but that is where i am stuck and need help.
(usually my questions on here get downvoted as i am not concise enough if that is the case comment and i will try to alter my question.
Thanks everyone.
I would create a class that holds the information of a record
public class ImageInfo
{
public string FileName { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public DateTime Date { get; set; }
public string Comments { get; set; }
public override string ToString()
{
return FileName;
}
}
Now you can write a method that returns the image infos
public List<ImageInfo> ReadImageInfos(string fileName)
{
string[] records = File.ReadAllLines(fileName);
var images = new List<ImageInfo>(records.Length);
foreach (string record in records) {
string[] columns = record.Split(',');
if (columns.Length >= 5) {
var imageInfo = new ImageInfo();
imageInfo.FileName = columns[0];
imageInfo.Description = columns[1];
imageInfo.Category = columns[2];
DateTime d;
if (DateTime.TryParseExact(columns[3], "dd/MM/yy",
CultureInfo.InvariantCulture, DateTimeStyles.None, out d))
{
imageInfo.Date = d;
}
imageInfo.Comments = columns[4];
images.Add(imageInfo);
}
}
return images;
}
Now you can fill the textboxes with one of these records like this
List<ImageInfo> images = ReadImageInfos(fileName);
if (images.Count > 0) {
ImageInfo image = images[0];
fileNameTextBox.Text = image.FileName;
descriptionTextBox.Text = image.Description;
categoryComboBox.Text = image.Category;
dateTakenTextBox.Text = image.Date.ToShortDateString();
commentsTextBox.Text = image.Comments;
}
The advantage of this approach is that the two operations of reading and displaying the records are separate. This makes it easier to understand and modify the code.
You can add ImageInfo objects to a ComboBox or ListBox directly instead of adding file names if you override the ToString method in the ImageInfo class.
public override string ToString()
{
return FileName;
}
Add the items to a combo box like this:
myComboBox.Items.Add(image); // Where image is of type ImageInfo.
You can retrieve the currently selected item with:
ImageInfo image = (ImageInfo)myComboBox.SelectedItem;
Most likely you will be doing this in the SelectedIndexChanged event handler.
void myComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
ImageInfo image = (ImageInfo)myComboBox.SelectedItem;
myTextBox.Text = image.FileName;
}
Create a class that resembles a row of your data, then iterate over the file, making your split and constructing a new class instance with your split data. Store this in a List<> (or some other appropriate structure) ensure you store it such that it can be referenced later. Don't change your UI as you are loading and parsing the file (as Mike has suggested), also as mike suggests you need to read until the EOF is reached (plenty of examples of this on the web) MSDN example.
Also, streamreader implements IDisposable, so you need to dispose of it, or wrap it in a using statement to clean up.
Example class, you could even pass the row in as a constructor argument:
public class LineItem
{
public string FileName { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public DateTime DateTaken { get; set; }
public string Comments { get; set; }
public LineItem(string textRow)
{
if (!string.IsNullOrEmpty(textRow) && textRow.Contains(','))
{
string[] parts = textRow.Split(',');
if (parts.Length == 5)
{
// correct length
FileName = parts[0];
Description = parts[1];
Category = parts[2];
Comments = parts[4];
// this needs some work
DateTime dateTaken = new DateTime();
if (DateTime.TryParse(parts[3], out dateTaken))
{
DateTaken = dateTaken;
}
}
}
}
}
Your code is not iterating through the records within the file.
You want to continue reading until the end of the file.
while (content != eof)
{
// split content
// populate text boxes
}
But this will overwrite your text boxes with each pass of the loop.
Also, you want to separate your code - do not mix I/O process with code that updates the UI.
The name of the method implies you are loading a file, but the method is doing far more than that. I would suggest changing the method to read the file, split each record into a class object which then gets stored into an array - and return that array.
A separate method will take that array and populate your table or grid or whatever is in the UI. Ideally, you have the gridview bind to the array.
If you keep all your entries the same:
name,food,type,blah blah
name,food,type,blah blah
you can add another split into your code:
line = content.Split('\n');
foreach (line in filename)
{
doc = line.Split(',');
//do stuff...
As for the option for string multiple entries, a method I have used is implementing a list of Models:
class ModelName
{
string Name { get; set; }
string foodType { get; set; }
//etc...
public void ModelName()
{
Name = null;
foodType = null;
//etc...
}
}
List<Model> ModelList;
foreach (line in filename)
{
doc = line.Split(',');
Model.Name = doc[1];
//etc...
And have a different list, and a different Model for each type of entry (person or food)
First, I'm going to apologize if this is a stupid question. I've been using C# for 16 hours after having not programmed anything since VB6. I'm just trying to hack together a small program for personal use that reads from an old access database and spits out a formatted report in Excel. I apologize for the messy/inefficient code.
Overview: I have two class types, "Zone" and "Device". Each "Zone" has a List of Devices in it. The main program has a List of Zones. Each database has a varying number of "zones" in it, and each "zone" has a varying number of devices assigned to it. I need to parse, sequentially, the zone list and the devices on each zone. I started with structs and arrays and popular opinion seems to be that those are both bad ways to do it, and I wasn't having much luck anyway, so I moved to lists and classes, and it was going well.
I can pull all the "zones" from the database, add them to the list, assign them their labels and IDs. The problem is when I go to read the "devices" from the database, I can't add them to the list within the Zone.
This is the error I get: "Object reference not set to an instance of an object." Which I gather means the object is null?
Here's the relevant code:
Device Class:
public class Device
{
public string Label;
public string Address;
public string Type;
public Device(string Label, string Address, string Type)
{
this.Address = Address;
this.Label = Label;
this.Type = Type;
}
}
Zone Class:
public class Zone
{
public string Label;
public short ID;
public List<Device> Devices;
public Zone(string Label, short ID) {
this.Label = Label;
this.ID = ID;
// ADDED AS PER SUGGESTIONS BELOW
this.Devices = new List<Device>();
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type) {
Devices.Add(new Device(Label, Address, Type));
}
}
Initializing and populating Zone List (on button click) (completes successfully)
List<Classes.Zone> Zones = new List<Classes.Zone>();
dbZoneReader = myZoneSelect.ExecuteReader();
while (dbZoneReader.Read())
{
Classes.dbItem dbRow = new Classes.dbItem();
dbRow.Address = Convert.ToInt16(dbZoneReader["DeviceAddress"].ToString());
dbRow.DeviceType = Convert.ToInt16(dbZoneReader["DeviceType"].ToString());
dbRow.Label = dbZoneReader["DeviceLabel"].ToString();
if (dbRow.Label != "" && dbRow.Address > 0)
{
Zones.Add(new Classes.Zone(dbRow.Label,dbRow.Address));
}
}
Adding Devices to their respective Zones:
while (dbReader.Read()) {
Classes.dbItem dbRow = new Classes.dbItem();
string tempZones;
// Acquire/convert device information
dbRow.Node = Convert.ToInt16(dbReader["NodeAddress"].ToString());
dbRow.Loop = Convert.ToInt16(dbReader["LoopSelection"].ToString());
dbRow.Address = Convert.ToInt16(dbReader["DeviceAddress"].ToString());
dbRow.TypeID = Convert.ToInt16(dbReader["TypeID"].ToString());
dbRow.FlashScanID = Convert.ToInt16(dbReader["FlashScanID"].ToString());
dbRow.DeviceType = Convert.ToInt16(dbReader["DeviceType"].ToString());
dbRow.Label = dbReader["DeviceLabel"].ToString();
// Find "proper" zone ID (some zones have multiple IDs, only one is relevant)
tempZones = dbReader["DevicePointMappingList"].ToString();
tempZones = tempZones.Replace("Z", "");
var elements = tempZones.Split(new[] { ',' }, System.StringSplitOptions.RemoveEmptyEntries);
if (elements.Length >= 2) {
ZoneCheck z = new ZoneCheck();
foreach (string items in elements) { if (z.Check(items)) { dbRow.Zone = Convert.ToInt16(items); } }
} else {
if (elements.Length == 1) { dbRow.Zone = Convert.ToInt16(elements[0]); }
else { dbRow.Zone = 0; }
}
// Only add devices that aren't assigned to zone 0, which is non-existent
if (dbRow.Zone > 0) {
// Add new device to zone's device list [THIS IS WHERE IT FAILS]
Zones.Find(z => z.ID == dbRow.Zone).Devices.Add(new Classes.Device("Test", "test", "Test"));
}
}
I've gone through and found out exactly where it fails, and it's the last line where it tries to add the device. Searching here and on google has lead me to believe that I need to initialize the object list... which I believe I've done? I've tried initializing it within the Zone class constructor, and when the Zone is added (which is what it's set too now).
I've confirmed that the Zone object exists, and that the Detectors list within that Zone object isn't null. Kinda stumped, figure I'm doing something that I shouldn't be doing and just don't know better, or I'm missing something really obvious.
The problem is in your Zone class. You need to initialize the List<Device> as follows.
public class Zone
{
public string Label;
public short ID;
public List<Device> Devices;
public Zone(string Label, short ID) {
this.Label = Label;
this.ID = ID;
this.Devices = new List<Device>();
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type) {
Devices.Add(new Device(Label, Address, Type));
}
}
The reason is that when you write public List<Device> Devices;, you're not actually creating an object. You're creating a variable that can hold an instance of the specified object. It's only when you pair the variable declaration up with object initialization ( = new List<Device>();) that you get a usable instance of the object.
Thinking of the same issue in terms of a simpler object may help:
public class Foo
{
public string bar; // bar isn't an actual instance of an object, it's just a spot that can hold a string
public void ManipulateBarWithRuntimeError()
{
bar.Substring(0, 1); // "bar" isn't actually set to anything, so how can we take a substring of it? This is going to fail at runtime.
}
public void ManipulateBarWithoutRuntimeError()
{
bar = "Hello, world!";
bar.Substring(0, 1); // bar is actually set to a string object containing some text, so now the Substring method will succeed
}
}
I think the problem is in your Zone class.
Here is my version of your Zone class:
public class Zone
{
public string Label;
public short ID;
public List<Device> Devices;
public Zone(string Label, short ID) {
this.Label = Label;
this.ID = ID;
this.Devices = new List<Device>();
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type) {
Devices.Add(new Device(Label, Address, Type));
}
}
This is an only change that I made to your class;
this.Devices = new List<Device>();
Now it might work...
You can also initialize the list inside a getter
public class Zone
{
public string Label;
public short ID;
private List<Device> _devices;
public List<Device> Devices
{
get
{
return this._devices ?? (this._devices = new List<Device>());
}
}
public Zone(string Label, short ID)
{
this.Label = Label;
this.ID = ID;
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type)
{
Devices.Add(new Device(Label, Address, Type));
}
}
I have in a SearchFlyClass an Arraylist GetFly()
...
public ArrayList GetFly(int tip, string country)
{
...
var list = new ArrayList();
var reader = command.ExecuteReader();
if (reader.HasRows)
{
...
while (reader.Read())
{
decimal nr_zbor = reader.GetDecimal(cod_zbor);
string aeroport = reader.GetString(nume_aeroport);
string companie = reader.GetString(nume_companie);
list.Add(nr_zbor);
list.Add(companie);
list.Add(aeroport);
}
}
...
and I wish to put in Form1.cs the list in listview by columns[zbor(colZbor),airport(colAirport),company(colCompany)], but I don't now how
private SearchFlyClass searchFly = new SearchFlyClass();
private ArrayList fly = new ArrayList();
...
private void ShowResultFlySearch(int direction, string country)
{
fly = searchFly.GetFly(direction, country);
for (int count = 0; count < fly.Count; count++)
{
string zbor = fly[0].ToString();
string companie = fly[1].ToString();
string aeroport = fly[2].ToString();
ListViewItem searchlist = new ListViewItem();
searchlist.Items.Add(new ListViewItem(elem));
}
}
can someone help me, please?
First you have to put ListView to View mode details, which you do with following code (it is also possible with setting View property in designer):
ListView listView = new ListView();
listView.View = View.Details;
Then you have to assign columns to the listView (can also be done in designer):
listView.Columns.Add("zbor");
listView.Columns.Add("airport");
listView.Columns.Add("company");
After this you have to assign other columns to ListViewItem subitems, by modifying your function:
private void ShowResultFlySearch(int direction, string country)
{
fly = searchFly.GetFly(direction, country);
for (int count = 0; count < fly.Count; count++)
{
string zbor = fly[0].ToString();
string companie = fly[1].ToString();
string aeroport = fly[2].ToString();
ListViewItem listViewItem = new ListViewItem(zbor);
listViewItem.SubItems.Add(airport);
listViewItem.SubItems.Add(companie);
listView.Items.Add (listViewItem);
}
}
The function assumes that it is in Form1.cs and that you have listView variable instantized as class variable of type ListView. Basics of C# and object oriented programming.
There are lots of issues with this code. Firstly, is there any reason that you're using ArrayList instead of the generic collection types? E.g. List<T>
Secondly, I would create a type to store all of the related data for one instance of your entity, rather than putting the column values for the entity into an untyped collection.
Thirdly, your aren't referencing count anywhere in your for loop - presumably because the query is returning a single entity, and therefore the for loop is redundant because you know the number of items returned for a single entity. You are also using a variable elem which doesn't seem to have been defined.
Updated
Define a type that describes your entity:
public class Flight
{
public decimal Code { get; set; }
public string Company { get; set; }
public string Airport { get; set; }
}
Change your method to return an instance of your entity:
public Flight GetFlight(int tip, string country)
Create a new instance to return from the method and populate it from your database query result:
var flight = new Flight();
flight.Code = reader.GetDecimal(cod_zbor);
flight.Airport = reader.GetString(nume_aeroport);
flight.Company = reader.GetString(nume_companie);
return flight;
Now your other method can use the updated method:
var flight = searchFly.GetFlight(...);
// access flight properties here
This assumes that your query returns a single entity. If it returns a collection, then you can use List<Flight> or IEnumerable<Flight> as your return type as appropriate.