需要 StreamReader、ArrayList 和 DataGrid 协同工作方面的帮助

发布于 2024-11-26 23:31:52 字数 3161 浏览 0 评论 0原文

我正在开发一个基本上是账单提醒的程序,主要是自学 C# 和 C# 后的第一个程序。 WPF。我可以让用户输入详细信息,然后将其保存到文本文件中。我现在正在尝试编写稍后从文本加载文件的部分。我使用 StreamReader 将文本文件读入 ArrayList,然后让程序迭代 ArrayList 并填充 DataGrid。但有些东西不起作用,我最终得到一个空白的 DataGrid,但具有正确的行数和正确的标题列。我相信问题是我使用 switch-case 来确定每个 ArrayList 位置的类型,然后将该位置的内容放在正确的列中,但 StreamReader 将所有内容作为字符串拉入,从而使 switch-case无意义。

所以基本上我的问题是,如何让 StreamReader 将项目放入正确的值类型中,或者这就是问题所在?

这是我正在玩的代码。虽然它实际上不是预算计划,但我只是将其用作测试平台,这样我就不会“污染”我的好代码。 :) 不过,这些值是完全相同的值类型,因此一旦它工作并且我将其转移过来,它将执行我需要的所有操作。

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; }
    }
}

I am working on a program that is basically a bill reminder, mostly 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; }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

好久不见√ 2024-12-03 23:31:52

您在这里发布的逻辑有很多错误。首先,数组中的每一行都包含一个字符串,因为这是 StreamReader.ReadLine() 返回的内容。它们可能是可以解析为另一种数据类型的字符串,但它们不是整数或小数,它们是字符串。

另一方面,您的 switch/case 块正在为数组中的每个元素创建一个新的 PersonInfo 结构(以及网格中的一行)。 (我对您关于网格具有正确行数的断言感到惊讶;在我看来,它应该具有您期望的行数的 4 倍。)对于另一个,您将相同的四列添加到网格中每次处理一个元素时(幸运的是,这不会导致错误,但这是不必要的;数据网格只有四列,而不是每行四列)。这些列每个都有一个绑定,但它们的绑定没有来源。

实际上你只需要做很少的事情。在代码中创建绑定是一条绝望和痛苦的道路。

首先,让我们解决解析文本文件的问题。您还没有弄清楚文件的格式如何。设计文件格式的典型方法是,每一行代表一个项目(无论是对象、结构、DataRow,还是其他什么),并且各行由分隔符分隔成字段。因此,使用竖线作为分隔符的典型行可能如下所示:

Abner Stoltzfus|36|1975-02-01|12000.00

如果这样做,您可以将每一行拆分为一个数组,然后将结构中的每个字段分配给一个数组元素,例如:

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);

该代码可能有错误- 我正在把它写下来 - 所以你需要修复它,直到你运行它时,它可靠地将人员信息写入调试窗口。您应该在开始解决问题的 WPF 部分之前执行此操作。

请注意此代码执行的操作,而您的代码未执行的操作,例如创建一个集合来保存所有 PersonInfo 结构,以及将字符串解析为本机类型。它所做的最后一件事是获取 persons 列表并将其添加到资源字典中(我假设此代码在您的 Window 的代码隐藏中运行) ;如果没有,还有很多其他方法可以做到这一点)。

现在让我们看看您的 XAML。我对 ListViewDataGrid 更熟悉,所以这就是我将在本示例中使用的内容,但是如何使用 DataGrid< 执行此操作的示例/code> 比比皆是。

  <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>

这会完成您在代码中尝试执行的所有操作以及更多操作:它创建控件,告诉它将从哪里获取其项目,告诉它要显示哪些列,并定义每个列的绑定。

上面的方法可能不起作用的原因有很多种,但既然问题已经细分,至少你知道该去哪里寻找。如果“调试”窗口不包含正确的数据,则从文件读取数据的逻辑存在错误。如果“输出”窗口包含绑定错误,则 XAML 中的绑定定义中有问题(可能是字段名称),或者可能是集合未正确添加到资源字典中。

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.

攒一口袋星星 2024-12-03 23:31:52

无论如何,不​​要,2011 年不要使用 ArrayList!请改用通用 List。或者只是一个数组:

string[] lines = System.IO.File.ReadAllLines("ArrayListSource.txt");

任何个人我都会切换到类:

public class PersonInfo { }

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 { }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文