Need help with StreamReader, ArrayList, and DataGrid working together
I am working on a program that is basically a bill reminder, m开发者_JS百科ostly for a first program after having taught myself C# & WPF. I am able to have the user enter details, then save it to a text file. I am now trying to code the part which later loads a file from text. I used StreamReader to read the text file into an ArrayList, then have the program iterate through the ArrayList and populate the DataGrid. Something isn't working though, and I'm ending up with a blank DataGrid, but with the correct number of rows and properly headed columns. I believe the problem is that I'm using a switch-case to determine the type of each ArrayList location, then place the contents of that location in the correct column, but StreamReader pulls everything in as a string, thus making the switch-case pointless.
So basically my question is, how do I make StreamReader place the items in the correct value type, or is that even the problem?
This is the code I'm playing with. Although it's not actually the budget program, I've just been using this as a test bed so I don't "contaminate" my good code. :) These values are exactly the same value types though, so it will do everything I need it to once it works and I transfer it over.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ArrayList array = new ArrayList();
DataGridTextColumn nameOfPerson = new DataGridTextColumn();
nameOfPerson.Binding = new Binding("name");
DataGridTextColumn ageOfPerson = new DataGridTextColumn();
ageOfPerson.Binding = new Binding("age");
DataGridTextColumn birthdayOfPerson = new DataGridTextColumn();
birthdayOfPerson.Binding = new Binding("birthday");
DataGridTextColumn netWorth = new DataGridTextColumn();
netWorth.Binding = new Binding("netWorth");
using (StreamReader reader = new StreamReader("ArrayListSource.txt"))
{
while (!reader.EndOfStream)
{
array.Add(reader.ReadLine());
}
}
//Array is now populated with contents of text file.
string name;
int age;
DateTime birthday;
decimal value;
//Checks the type of each entry in the array.
for (int i = 0; i < array.Count; i++)
{
System.Type idType = array[i].GetType();
string currentItem = idType.Name;
switch (currentItem)
{
case "String":
name = (string)array[i];
dataGrid1.Items.Add(new PersonInfo() { nameOfPerson = name });
break;
case "Int32":
age = (int)array[i];
dataGrid1.Items.Add(new PersonInfo() { ageOfPerson = age });
break;
case "DateTime":
birthday = (DateTime)array[i];
dataGrid1.Items.Add(new PersonInfo() { birthdayOfPerson = birthday });
break;
case "Decimal":
value = (decimal)array[i];
dataGrid1.Items.Add(new PersonInfo() { netWorth = value });
break;
}
dataGrid1.Columns.Add(nameOfPerson);
dataGrid1.Columns.Add(ageOfPerson);
dataGrid1.Columns.Add(birthdayOfPerson);
dataGrid1.Columns.Add(netWorth);
nameOfPerson.Header = "Name";
ageOfPerson.Header = "Age";
birthdayOfPerson.Header = "Birthdate";
netWorth.Header = "Net Worth";
}
public struct PersonInfo
{
public string nameOfPerson { get; set; }
public int ageOfPerson { get; set; }
public DateTime birthdayOfPerson { get; set; }
public decimal netWorth { get; set; }
}
}
There's a bunch that's wrong with the logic you've posted here. For one, each line in your array contains a string, because that's what StreamReader.ReadLine()
returns. They may be strings that could conceivably be parsed into another data type, but they're not integers or decimals, they're strings.
For another, your switch/case block is creating a new PersonInfo
struct (and thus a row in the grid) for every element in the array. (I'm surprised by your assertion that the grid has the right number of rows; it looks to me like it should have 4x the number of rows you expect.) For yet another, you're adding the same four columns to your grid every time you process an element (fortunately, this doesn't cause an error, but it's unnecessary; the data grid only has four columns, not four columns for every row). And those columns each have a binding, but their binding has no source.
You need to actually do very little of this. Creating bindings in code is the path of despair and misery.
First, let's address parsing the text file. You haven't made clear how the file is formatted. A typical way to design a file format is so that each line represents an item (whether an object, a struct, a DataRow
, or what have you), and the lines are separated into fields by delimiters. So a typical line that used vertical bars as delimiters might look like:
Abner Stoltzfus|36|1975-02-01|12000.00
If you do this, you can split each line into an array, and then assign each field in your struct to an array element, e.g.:
List<PersonInfo> persons = new List<personInfo>();
using (StreamReader sr = new StreamReader(filename))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
string[] fields = line.Split(new char[] { '|' });
PersonInfo p = new PersonInfo
{
Name = fields[0],
Age = int.Parse(fields[1]),
DateOfBirth = DateTime.Parse(fields[2]),
NetWorth = decimal.Parse(fields[3])
});
Debug.WriteLine(string.Format("Name={0}, Age={1}, DateOfBirth={2}, NetWorth={3}",
p.Name, p.Age, p.DateOfBirth, p.NetWorth);
persons.Add(p);
}
}
AddResource("Persons", persons);
That code probably has bugs in it - I'm writing it off the top of my head - so you'll need to fix it until when you run it, it reliably writes the person info to the Debug window. You should do this before you even begin screwing around with the WPF part of the problem.
Pay attention to the things this code is doing that your code doesn't, like creating a collection to hold all of the PersonInfo
structs, and parsing the strings into their native types. The last thing it does is take the persons
list and adds it to the resource dictionary (I'm assuming here that this code is running in the code-behind of your Window
; if not, there are plenty of other ways to do this).
Now let's go to your XAML. I'm more familiar with ListView
than DataGrid
, so that's what I'm going to use in this example, but examples of how to do this with DataGrid
abound.
<ListView ItemsSource="{Binding {DynamicResource Persons}}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}"/>
<GridViewColumn Header="Date of birth" DisplayMemberBinding="{Binding DateOfBirth}"/>
<GridViewColumn Header="Net worth" DisplayMemberBinding="{Binding NetWorth}"/>
</GridView>
</ListView.View>
</ListView>
</ListView>
This does everything you were trying to do in your code and more: it creates the control, tells it where it will get its items from, tells it what columns to display, and defines the bindings for each columns.
There are all kinds of reasons the above might not work, but now that the problem's segmented, at least you know where to look. If the Debug window doesn't contain the right data, then the logic that reads the data from the file has bugs in it. If the Output window contains binding errors, then there's something wrong (probably the field names) in the binding definitions in the XAML, or possibly the collection's not being added to the resource dictionary correctly.
Anyway, don't, don't use ArrayList in 2011! Use generic List<T>
instead. Or just an array:
string[] lines = System.IO.File.ReadAllLines("ArrayListSource.txt");
Any personally I would switch to class:
public class PersonInfo { }
精彩评论