在WPF中对ListView的列进行求和

3

我有以下csv文件:

Name,Score
Kelly,5
James,5
Sara,1
Kelly,4
James,1
John,3

我正在尝试读取CSV文件,根据姓名获取分数总和并将其加载到列表视图中。

首先,我能够读取CSV文件并将其加载到两个列表视图中的每一列。以下是我的代码:

   public MainWindow()
    {
        InitializeComponent();      
        using (var fs = File.OpenRead(filepath))
        using (var reader = new StreamReader(fs))
        {
            List<string> list0 = new List<string>();
            List<string> list1 = new List<string>();
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
                var values = line.Split(',');

                list0.Add(values[0]);
                list1.Add(values[1]);
                 //sum = list1.Sum(x => Convert.ToInt32(x));
            }

            List0.ItemsSource = list0;
            List1.ItemsSource = list1; 

         }
  }

这是我使用的 XAML 代码:

    <ListView Name="List0" HorizontalAlignment="Left" Height="258" Margin="0,33,0,0" VerticalAlignment="Top" Width="267" >
            <ListView.View>
                <GridView>
                    <GridViewColumn DisplayMemberBinding="{Binding}"/>
                </GridView>
            </ListView.View>
        </ListView>
        <ListView Name="List1" HorizontalAlignment="Left" Height="258"   Margin="272,33,0,0" VerticalAlignment="Top" Width="172" >
            <ListView.View>
                <GridView>
                    <GridViewColumn DisplayMemberBinding="{Binding}"/>
                </GridView>
            </ListView.View>
        </ListView>

这对我来说很好用。但是我在这里卡住了,想知道是否可以根据列表视图中的姓名列求出分数列的总和。

例如,获取Kelly的总分,获取James的总分等等。因此,列表视图将如下所示:

Name,Score
Kelly,9 
James,6 
Sara,1
John,3

我尝试使用Sum选项并想将其打印到标签上进行测试,但它没有工作,并出现错误提示“输入字符串的格式不正确”。

我在C#,WPF和XAML方面仍然缺乏经验,特别是在读取和解析CSV文件方面,因此我不确定如何处理这个问题。如果有关于此事的任何帮助或指针,将不胜感激!谢谢!

2个回答

1
我会使用一个字典:

public MainWindow()
{
    InitializeComponent();      
    using (var fs = File.OpenRead(filepath))
    using (var reader = new StreamReader(fs))
    {
        var dict = new Dictionary<string, int>();
        while (!reader.EndOfStream)
        {
            var line = reader.ReadLine();
            var values = line.Split(',');

            // Discard header line
            if (("Name" == values[0]) && ("Score" == values[1]))
                continue;

            if (dict.ContainsKey(values[0]))
                dict[values[0]] += Convert.ToInt32(values[1]);
            else
                dict[values[0]] = Convert.ToInt32(values[1]);
        }

        // Now you can create some lists to display
        var list0 = new List<string>();
        var list1 = new List<string>();
        foreach (KeyValuePair<string, string> entry in dict)
        {
            list0.Add(entry.Key);
            list1.Add(entry.Value.ToString());
        }

        List0.ItemsSource = list0;
        List1.ItemsSource = list1; 
     }
}

你好dumetrulo,感谢您的评论。我会尝试这个方法,但使用字典有什么好处呢?我以前从未使用过。谢谢! - Dr.Bake
我假设在 foreach 行中你的意思是:foreach (KeyValuePair<string, int> entry in dict),而不是 <string, string>,对吗? - Dr.Bake
我会使用类而不是带有魔术字符串的KeyValuePairs。请查看我的答案以获取示例。 - Mighty Badaboom
我也尝试了你的答案,它也对我起作用了。我从来不知道我可以在C#中使用字典,所以我会去了解一下。非常感谢你的帮助 :) - Dr.Bake

1
我会创建一个新的类来存储人员信息,包括他们的分数。大概是这样:
public class Person
{
    public string Name { get; private set; }
    public int Score { get; set; }

    public Person(string name, int score)
    {
        Name = name;
        Score = Score;
    }
}

然后,您可以将逻辑更改为以下内容。
var lines = File.ReadAllLines(filepath);
var persons = new List<Person>();

foreach(var line in lines)
{
    var values = line.Split(',');
    var name = values[0];
    var score = int.Parse(line[1].ToString());

    var person = persons.FirstOrDefault(x => x.Name == name);
    if(person == null)
    {
        var newPerson = new Person(name, score);
        persons.Add(newPerson);
    }
    else
    {
        person.Score += score;
    }
}

Person类的优点是代码更加清晰易懂;没有任何神秘的字符串,让人不知道它们的含义。

在您的逻辑中,当您已经有一个人的条目在列表中时,您可以提高这个人的分数。

//编辑绑定到xaml 例如,您可以使用。

<DataGrid ItemsSource="{Binding Persons}" />

人员是您的ViewModel中的ObservableCollection<Person>。 DataGrid将自动为Person类中的每个属性创建一列。 您可以自定义DataGrid以获得所需的外观。 互联网上有很多示例。
//编辑绑定 人员集合必须是公共的。 否则,您无法从xaml访问它。
您可以删除

var persons = new List<Person>();

将其替换为MainWindow类中的属性。
public ObservableCollection<Person> Persons {get; set;}

你的类应该长这样:
public ObservableCollection<Person> Persons {get; set;}

public MainWindow()
    {
        InitializeComponent();

        var lines = File.ReadAllLines(filepath);
        Persons = new ObservableCollection<Person>();

在您的 DataGrid 中将绑定更改为 ItemsSource="{Binding Persons}"


非常感谢!目前我将CSV文件存储到列表中,并在列表视图中显示它。但我不确定我是否做得正确,因为我有两个列表视图,每列都在不同的列表视图中。这是正确的方法吗? - Dr.Bake
那不是一个好的方法,因为你正在两个不同的控件中存储依赖数据。给我一分钟,我会改变我的帖子,给你一个例子。 - Mighty Badaboom
我编辑了我的帖子,加入了一个简短的例子。 - Mighty Badaboom
非常感谢您提供的简短示例和解释。我现在会尝试一下。只有一个问题,我需要将Person类绑定到我的MainWindow()作为datagrid的itemsource吗?还是因为已经在XAML中完成了,所以不需要?再次感谢! - Dr.Bake
ObservableCollection是因为它实现了INotifyPropertyChanged接口,可以在发生更改时更新视图。Person类是用于表示一个人的。如果你有x个人,那么你需要一个集合来存储他们。在引用的代码行中,您检查是否已经存在具有相同名称的Person。如果是,则可以增加她的分数。如果没有,则创建一个新的Person并将其放入列表中。 - Mighty Badaboom
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接