【问题标题】:Sum Columns in ListView WPFListView WPF中的总和列
【发布时间】:2017-03-15 10:50:45
【问题描述】:

我有以下 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 的总分等。所以 listview 应该是这样的:

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

我尝试使用 Sum 选项并考虑将其打印到标签上进行测试,但它不起作用,并且我收到一条错误消息,提示“输入字符串的格式不正确”

我对 C#、WPF 和 XAML 的经验还不够丰富;特别是在读取和解析 CSV 文件时,所以我不知道该怎么做。非常感谢您对此事的任何帮助或指示!谢谢!

【问题讨论】:

    标签: c# wpf xaml csv listview


    【解决方案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

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

    Persons 是 ViewModel 中的 ObservableCollection&lt;Person&gt;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}"

    【讨论】:

    • 谢谢@Mighty Badaboom 我会试试你的答案。但是您能解释一下 FirstOrDefault 的含义吗?另外,如何将它绑定到我拥有的列表视图?再次感谢
    • FirstOrDefault 将为您提供第一个匹配项。如果没有项目匹配,您将获得空值。查看code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b 以熟悉 LINQ。它非常有用。绑定到您的 xaml 时,我将使用 ObservableCollection 而不是 List。你想如何显示你的数据?猜测 DataGrid 将是一个不错的选择。将您的 Person 绑定到 DataGrid 的 ItemsSource。如果您在发布代码时遇到问题,我可以为您提供帮助。
    • 非常感谢!目前我将 CSV 文件存储到列表中并在列表视图中显示。但我不确定我这样做是否正确,因为我有两个列表视图,每列都在不同的列表视图中。这是正确的方法吗?
    • 这不是一个好方法,因为您将依赖数据存储在两个不同的控件中。给我一分钟,我改变我的帖子给你一个例子。
    • 编辑了我的帖子作为一个简短的例子。
    【解决方案2】:

    我会使用字典:

    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,感谢您的评论。我会试试这个,但是使用字典有什么好处?我以前从未使用过它们。谢谢!
    • 我假设你的意思是在 foreach 行中: foreach (KeyValuePair entry in dict) 不是 对吗?
    • 我会使用一个类而不是带有魔术字符串的 KeyValuePairs。以我的回答为例。
    • 我也试过你的答案,它也对我有用。我从来不知道我可以在 C# 中使用字典,所以我会阅读它。非常感谢您的帮助:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多