【问题标题】:Generating a list from csv to listbox从 csv 生成列表到列表框
【发布时间】:2015-12-31 20:52:20
【问题描述】:

我正在尝试从我拥有的数据库中生成一个列表,并使用 LINQ 将其添加到 WPF C# 中的列表框中。

这是我现在拥有的 XAML:

<ListView x:Name="ListBox" Margin="16,232,22,10.4" SelectionMode="Multiple">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Nettstasjon" Width="100" DisplayMemberBinding="{Binding Path=Name}"/>
                <GridViewColumn Header="Område" Width="100" DisplayMemberBinding="{Binding Path=Area}"/>
                <GridViewColumn Header="Radial" Width="100" DisplayMemberBinding="{Binding Path=Radial}"/>
            </GridView>
        </ListView.View>
    </ListView>

为了读取数据库,我有这个代码隐藏。我创建了一个“TransformerStation”类来保存每行的数据。

public class TransformerStation : INotifyPropertyChanged
    {
        public string Name { get; set; }
        public string Radial { get; set; }
        public string Area {get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
    }

然后我做了一个类来读取数据库

public IEnumerable<TransformerStation> ReadCSV(string fileName, string radial)
    {

        string[] lines = File.ReadAllLines(System.IO.Path.ChangeExtension(fileName, ".csv"), Encoding.UTF8);


        return lines.Select(line =>
        {
            string[] data = line.Split(';');

            foreach (var value in data)
            {
                if (data = radial)
                {
                    return new TransformerStation(data[0], data[1], data[2]);
                }
            }     
        });
    }

我的数据库如下所示:

N765;TANGEN;TANGEN L98
R2351;SPIKKESTAD;SPIKKESTAD K88
S622;KRÅKSTAD;KRÅKSTAD L812
S1318;KRÅKSTAD;KRÅKSTAD L812

我需要的如下:

我有一个带有按钮的 WPF,其内容等于我的数据库中的第三列 (F.eks KRÅKSTAD L812)。当我按下一个按钮时,我希望从我的数据库中收集第一列中的所有项目,这些项目与第三列中的按钮内容相匹配,并将它们显示在我上面创建的列表框中。

所以,如果我按下内容为“Kråkstad L812”的按钮,那么我的列表将显示

S622
S1318

对不起,我对 C# 和 WPF 很陌生。所以我没有太多代码要显示。但我真的很感谢我能得到的所有帮助,如果你解释代码中发生的事情真的会有所不同:) 所以为了澄清一下,我基本上需要 SELECTradial FROM 数据库并从中生成一个列表。

编辑,我也对从 csv 数据库导入数据的其他方式持开放态度。我刚刚选择了一个对我有用的:)

【问题讨论】:

  • 您当前的实施遇到了什么问题?我唯一能看到的缺失是 if (data = Radial) 应该是 if (data[2] == Radial)
  • @wentimo,当我使用 if(data[2] == Radial) 时,我收到错误消息,“并非所有代码路径都在 typ 'Func
  • 你是如何生成按钮的?

标签: c# wpf linq mvvm


【解决方案1】:

这里有很多问题。首先,您实际上并没有正确实现 INotifyPropertyChange 支持。可能最简单的做法是将 MVVM Lite 添加到您的项目中(如果您使用 NuGet,则只需一分钟)并将您的视图模型基于 ViewModelBase:

public class TransformerStation : ViewModelBase
{
    private string _Name;
    public string Name
    {
        get { return this._Name; }
        set { this._Name = value; RaisePropertyChanged(); }
    }

    private string _Radial;
    public string Radial
    {
        get { return this._Radial; }
        set { this._Radial = value; RaisePropertyChanged(); }
    }

    private string _Area;
    public string Area
    {
        get { return this._Area; }
        set { this._Area = value; RaisePropertyChanged(); }
    }

    public TransformerStation(string name, string radial, string area)
    {
        this.Name = name;
        this.Radial = radial;
        this.Area = area;
    }

}

我注意到的第二件事是,您在加载数据时似乎在过滤数据,这表明您每次用户按下按钮时都在重新加载。除非您有大量数据,否则您最好将整个数据存储在内存中,并在加载后对其进行过滤。字典不仅可以加快过滤过程,还可以为您提供数据集中所有径向线的列表,如果您需要在运行时动态创建按钮:

    public Dictionary<string, List<TransformerStation>> ReadCSV(string fileName)
    {

        string[] lines = File.ReadAllLines(System.IO.Path.ChangeExtension(fileName, ".csv"), Encoding.UTF8);
        return lines.Select(line =>
        {
            string[] data = line.Split(';');
            return new TransformerStation(data[0], data[1], data[2]);
        })
        .GroupBy(ts => ts.Radial)
        .ToDictionary(g => g.Key, g => g.ToList());
    }

回到您的 MainViewModel 中,您需要加载数据并将其存储在某处(即 AllStations),您需要一个属性,如“SelectedRadial”,您设置它以响应用户按下按钮,以及另一个(“CurrentStations ") 这是 AllStations 的过滤版本:

    private Dictionary<string, List<TransformerStation>> _AllStations;
    public Dictionary<string, List<TransformerStation>> AllStations
    {
        get { return this._AllStations; }
        private set { this._AllStations = value; RaisePropertyChanged(); }
    }


    private string _SelectedRadial;
    public string SelectedRadial
    {
        get { return this._SelectedRadial; }
        set
        {
            this._SelectedRadial = value;
            RaisePropertyChanged();
            this.CurrentStations = this.AllStations[value];
        }
    }

    private List<TransformerStation> _CurrentStations;
    public List<TransformerStation> CurrentStations
    {
        get { return this._CurrentStations; }
        private set { this._CurrentStations = value; RaisePropertyChanged(() => this.CurrentStations); }
    }

    public ICommand RadialCommand {get { return new RelayCommand<string>(OnRadialCommand); }}
    private void OnRadialCommand(string radial)
    {
        this.SelectedRadial = radial;
    }


    public MainViewModel()
    {
        this.AllStations = ReadCSV(@"data.csv");
    }

然后只需绑定一些最小的 XAML 来生成按钮(同样,这是可选的)并显示您的数据:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <ListBox ItemsSource="{Binding AllStations.Keys}">
        <ListBox.ItemTemplate>
            <ItemContainerTemplate>
                <Button Command="{Binding Path=DataContext.RadialCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" CommandParameter="{Binding}">
                    <TextBlock Text="{Binding}" />
                </Button>
            </ItemContainerTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <ListView Grid.Column="1" ItemsSource="{Binding CurrentStations}" >
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Nettstasjon" Width="100" DisplayMemberBinding="{Binding Path=Name}"/>
                <GridViewColumn Header="Område" Width="100" DisplayMemberBinding="{Binding Path=Area}"/>
                <GridViewColumn Header="Radial" Width="100" DisplayMemberBinding="{Binding Path=Radial}"/>
            </GridView>
        </ListView.View>
    </ListView>

</Grid>

结果:

【讨论】:

  • 非常感谢您的回复和时间!我尝试实现您粘贴的代码,但出现一些错误:RaisePropertyChanged(),给我一个错误“当前上下文中不存在”ColumnDefinition 在“Grid”类型以及 ViewModelBase“类型或找不到命名空间 ViewModelBase(您是否缺少 using 指令..."
  • 据我所知,我确实添加了 MVVM lite 工具包
  • 源文件顶部有using GalaSoft.MvvmLight;吗?
  • ColumnDefinitions 肯定是Grid 的一部分,你没有拼写它ColumnDefinition 是吗?
  • 我发现了 Grid 问题,但 RaisePropertyChanged() 仍然出错。是的,我正在使用 GalaSoft.MvvmLight :)
【解决方案2】:

很难给出一个完整的答案,因为没有给出你所有的代码,你可能想在以后开发完整的项目时重构其中的一些。所以我可能会提供一些建议,让你走得更远。您将需要搜索/研究其中的一些内容,但由于您是 c#/wpf 的新手,所以无论如何您都计划这样做,对吧? :)

...修复 == 就像 @wentimo 指出的...

对于返回 IEnumerable,您可以查看 yield

看起来您可能会多次读取数据库。如果它一直在变化,那可能很好。如果它更静态,请考虑将其读取一次并将其存储在字典中,其中 Name 是键,TransformerStation 是一个值。然后对于每个查找,您可以使用 .Where 子句进行过滤。

查看此post 并阅读答案以了解更多信息。也许这是相关的:

var keysForValues = dictionary.Where(pair => values.Contains(pair.Value))
                          .Select(pair => pair.Key);

没有一种最好的方法...研究DictionaryListList&lt;Tuple

一些示例代码(在控制台项目中创建)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace ConsoleApplication1
{
    [DebuggerDisplay("{Name} {Radial} {Area}")]
    public class TransformerStation
    {
        public string Name { get; set; }
        public string Radial { get; set; }
        public string Area { get; set; }

        public TransformerStation(string name, string radial, string area)
        {
            Name = name;
            Radial = radial;
            Area = area;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            CSVToDictionary();
        }

        private static void CSVToDictionary()
        {
            var dictionary = new Dictionary<string, TransformerStation>();

            // your csv
            string[] lines = new string[] {
            "N765; TANGEN; TANGEN L98",
            "R2351; SPIKKESTAD; SPIKKESTAD K88",
            "S622; KRÅKSTAD; KRÅKSTAD L812",
            "S1318; KRÅKSTAD; KRÅKSTAD L812"};

            foreach (var line in lines)
            {
                var data = line.Split(';');
                dictionary.Add(data[0], new TransformerStation(data[0].Trim(), data[1].Trim(), data[2].Trim()));
            }

            var find = "KRÅKSTAD L812";
            var search = dictionary.Values.Where(v => v.Area == find);

            foreach (var found in search)
            {
                Console.WriteLine($"Matches: {found.Name} {found.Radial} {found.Area}");
            }
        }
    }
}

你应该从中学到什么

  • DebuggerDisplay(使用调试器查看更好的数据表示)
  • 模拟读取 csv 并存储在字典中
  • 从输入数据中修剪空格(您从不信任读入的数据,始终验证/清理)
  • 使用 linq 过滤数据
  • 使用String interpolation 输出结果

祝你的项目好运。

【讨论】:

  • 感谢您的建议。我绝对有兴趣了解如何将数据库存储在字典中。你能提供一些示例代码吗? :)
  • 添加示例代码以进一步教育我的答案
【解决方案3】:

好像你的 if 语句

if (data = radial)

应该是:

if (data == radial)

因为= 是一个赋值,而== 是一个等于操作。

除此之外,我猜你想要类似的东西:如果用户输入存在于数据库中的某处......返回匹配项。

如果是这样,请使用 IndexOf 而不是 ==,如下所示:

if (data[0].IndexOf(radial, StringComparison.OrdinalIgnoreCase) >= 0)
{
    // return new ..
}

虽然上面只检查数据[0]。您可以像现在一样使用 foreach 循环来比较行中的所有数据。或使用:

if (data.Any(dataPart => dataPart.IndexOf(radial, StringComparison.OrdinalIgnoreCase) >= 0)
{
    // return new ..
}

【讨论】:

  • 我想指出,在data是字符串数组,radial是字符串的问题中,答案的前半部分不会编译
  • @Measuring 这里还有很多问题。数据是string[],径向是stringTransformerStation没有那个构造函数,不是Select的所有路径都返回结果,INotifyPropertyChanged没有正确实现,ListBox.ItemsSource没有设置跨度>
  • @dkozl 是的,我知道这不是工作代码。但似乎他的工作大部分时间都在工作,所以我没有打扰。
  • @dkozl,就像我说的。我对 C# 很陌生,所以我确信有很多错误和更好的做事方法。我只是希望人们给我指点并解释事情,以便我可以改进它们。感谢您的帮助:)
猜你喜欢
  • 2018-01-20
  • 2012-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多