1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# WPF XAMLで、CSV読み込み、グラフ(LiveCharts.Wpf)

Posted at

■画面

スクリーンショット (14).png

スクリーンショット (15).png

■ファイル構成

・MainWindow.xaml
・MainWindow.cs
・MainViewModel.cs
・Person.cs
・RelayCommand.cs

■ソースコード

MainWindow.xam
<Window x:Class="test_01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:test_01"
        xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="800" Width="1200">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="250"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- CSV読み込みボタン -->
        <Button Content="過去の国勢調査を見る"
                Command="{Binding LoadCsvCommand}"
                Margin="40"
                Padding="20"
                FontSize="20"
                Grid.Row="0"/>

        <!-- ListView -->
        <ScrollViewer VerticalScrollBarVisibility="Auto" Height="250" Grid.Row="1">
            <ListView ItemsSource="{Binding People}">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="都道府県コード" DisplayMemberBinding="{Binding PrefectureCode}" Width="100"/>
                        <GridViewColumn Header="都道府県名" DisplayMemberBinding="{Binding PrefectureName}" Width="150"/>
                        <GridViewColumn Header="元号" DisplayMemberBinding="{Binding Era}" Width="100"/>
                        <GridViewColumn Header="和暦(年)" DisplayMemberBinding="{Binding JapaneseYear}" Width="100"/>
                        <GridViewColumn Header="西暦(年)" DisplayMemberBinding="{Binding GregorianYear}" Width="100"/>
                        <GridViewColumn Header="注" DisplayMemberBinding="{Binding Note}" Width="150"/>
                        <GridViewColumn Header="人口(総数)" DisplayMemberBinding="{Binding PopulationTotal}" Width="100"/>
                        <GridViewColumn Header="人口(男)" DisplayMemberBinding="{Binding PopulationMale}" Width="100"/>
                        <GridViewColumn Header="人口(女)" DisplayMemberBinding="{Binding PopulationFemale}" Width="100"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </ScrollViewer>

        <!-- グラフ(スクロール可能) -->
        <Grid Grid.Row="2">
            <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
                <Grid>
                    <lvc:CartesianChart Margin="10" x:Name="lvChart"
                                        Series="{Binding SeriesCollection}"
                                        MinHeight="300" MinWidth="1200">
                        <lvc:CartesianChart.AxisX>
                            <lvc:Axis Title="年" Labels="{Binding YearLabels}" MinValue="0" MaxValue="10">
                                <lvc:Axis.Separator>
                                    <lvc:Separator Step="1"/>
                                </lvc:Axis.Separator>
                            </lvc:Axis>
                        </lvc:CartesianChart.AxisX>
                        <lvc:CartesianChart.AxisY>
                            <lvc:Axis Title="人口" MinValue="0" MaxValue="100000000" />
                        </lvc:CartesianChart.AxisY>
                    </lvc:CartesianChart>
                </Grid>
            </ScrollViewer>
        </Grid>
    </Grid>
</Window>

MainWindow.cs

using LiveCharts;
using System;
using System.Collections.ObjectModel;
using System.Windows;

namespace test_01
{

    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            // コンポーネント初期化
            InitializeComponent();
            // データバインド適用
            DataContext = new MainViewModel();
        }
    }
}

MainViewModel.cs

using LiveCharts;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using LiveCharts.Wpf;

namespace test_01
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<Person> People { get; set; }
        public ICommand LoadCsvCommand { get; set; }


        // グラフ用のプロパティ
        
        public ObservableCollection<int> PopulationTotalValues { get; set; }
        public ObservableCollection<int> PopulationMaleValues { get; set; }
        public ObservableCollection<int> PopulationFemaleValues { get; set; }
        public ObservableCollection<string> YearLabels { get; set; }

        // LiveCharts用のプロパティ
        public SeriesCollection SeriesCollection { get; set; }
        
        // 1920年 - 2015年のデータ作成
        private static readonly int[] Years = Enumerable.Range(1920, (2015 - 1920) / 5 + 1).Select(x => x * 5).ToArray();


        public MainViewModel()
        {
            try
            {
                // 初期化
                People = new ObservableCollection<Person>();

                PopulationTotalValues = new ObservableCollection<int>();
                PopulationMaleValues = new ObservableCollection<int>();
                PopulationFemaleValues = new ObservableCollection<int>();

                YearLabels = new ObservableCollection<string>();
                SeriesCollection = new SeriesCollection
                {
                    new ColumnSeries
                    {
                        Title = "総人口",
                        Values = new ChartValues<int>()  // 初期は空の値
                    },
                    new ColumnSeries
                    {
                        Title = "男性人口",
                        Values = new ChartValues<int>()
                    },
                    new ColumnSeries
                    {
                        Title = "女性人口",
                        Values = new ChartValues<int>()
                    }
                };

                // UI スレッドで初期データを追加
                /*
                Application.Current.Dispatcher.Invoke(() =>
                {
                    YearLabels.Add("2020");
                    YearLabels.Add("2021");
                    YearLabels.Add("2022");
                    YearLabels.Add("2023");
                });
                */

                LoadCsvCommand = new RelayCommand(LoadCsv);

            }
            catch (Exception ex)
            {
                MessageBox.Show($"ViewModel 初期化エラー: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /**
         *  CSV読み込みボタンを押した、処理
         **/
        private void LoadCsv()
        {
            // string FilePath = "test_01.c01.csv";
            string FilePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "c01.csv");

            People.Clear();
            PopulationTotalValues.Clear();
            PopulationMaleValues.Clear();
            PopulationFemaleValues.Clear();
            YearLabels.Clear();

            try
            {
                if (File.Exists(FilePath))
                {
                    var lines = File.ReadAllLines(FilePath, Encoding.GetEncoding("Shift_JIS"));

                    // 最初の行(ヘッダー行)をスキップ
                    var dataLines = lines.Skip(1).ToList();

                    // 前の年を保持する変数
                    string previousYear = string.Empty; 

                    for (int i = 0; i < dataLines.Count; i++)
                    {
                        var values = dataLines[i].Split(',');
                        if (values.Length >= 9)
                        {
                            var totalPopulation = int.TryParse(values[6], out int total) ? total : 0;
                            var malePopulation = int.TryParse(values[7], out int male) ? male : 0;
                            var femalePopulation = int.TryParse(values[8], out int female) ? female : 0;

                            People.Add(new Person
                            {
                                PrefectureCode = values[0],
                                PrefectureName = values[1],
                                Era = values[2],
                                JapaneseYear = values[3],
                                GregorianYear = values[4],
                                Note = values[5],
                                PopulationTotal = totalPopulation,
                                PopulationMale = malePopulation,
                                PopulationFemale = femalePopulation
                            });

                  
                            // **2 行目(i == 0)のデータだけをグラフに反映**
                            if (previousYear != values[4])  // 年が変更された場合
                            {
                                previousYear = values[4];

                                // 年が変更された場合のみ、グラフにデータを追加
                                Application.Current.Dispatcher.Invoke(() =>
                                {
                                    YearLabels.Add(values[4]);  // 西暦(年)をラベルに追加

                                    PopulationTotalValues.Add(totalPopulation / 10000);
                                    PopulationMaleValues.Add(malePopulation / 10000);
                                    PopulationFemaleValues.Add(femalePopulation / 10000);

                                    SeriesCollection[0].Values.Add(totalPopulation); // 総人口
                                    SeriesCollection[1].Values.Add(malePopulation);  // 男性人口
                                    SeriesCollection[2].Values.Add(femalePopulation); // 女性人口
                                });
                            }
                        }
                    }

                    // 変更をビューに通知
                    OnPropertyChanged(nameof(PopulationTotalValues));
                    OnPropertyChanged(nameof(PopulationMaleValues));
                    OnPropertyChanged(nameof(PopulationFemaleValues));
                    OnPropertyChanged(nameof(YearLabels));

                    OnPropertyChanged(nameof(SeriesCollection)); // グラフの更新を通知
                }
                else
                {
                    MessageBox.Show("---ERR--- CSVファイルが見つかりません。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"LoadCsv エラー: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Person.cs
using System.ComponentModel;

namespace test_01
{
    public class Person : INotifyPropertyChanged
    {
        /**
         * プロパティ
         * */
        private string prefectureCode;
        private string prefectureName;
        private string era;
        private string japaneseYear;
        private string gregorianYear;
        private string note;
        private int populationTotal; // グラフにも使用
        private int populationMale;  // グラフにも使用
        private int populationFemale;// グラフにも使用


        /**
         * ゲッター、セッター
         * */
        public string PrefectureCode
        {
            get => prefectureCode;
            set { prefectureCode = value; OnPropertyChanged(nameof(PrefectureCode)); }
        }

        public string PrefectureName
        {
            get => prefectureName;
            set { prefectureName = value; OnPropertyChanged(nameof(PrefectureName)); }
        }

        public string Era
        {
            get => era;
            set { era = value; OnPropertyChanged(nameof(Era)); }
        }

        public string JapaneseYear
        {
            get => japaneseYear;
            set { japaneseYear = value; OnPropertyChanged(nameof(JapaneseYear)); }
        }

        public string GregorianYear
        {
            get => gregorianYear;
            set { gregorianYear = value; OnPropertyChanged(nameof(GregorianYear)); }
        }

        public string Note
        {
            get => note;
            set { note = value; OnPropertyChanged(nameof(Note)); }
        }

        public int PopulationTotal
        {
            get => populationTotal;
            set { populationTotal = value; OnPropertyChanged(nameof(PopulationTotal)); }
        }

        public int PopulationMale
        {
            get => populationMale;
            set { populationMale = value; OnPropertyChanged(nameof(PopulationMale)); }
        }

        public int PopulationFemale
        {
            get => populationFemale;
            set { populationFemale = value; OnPropertyChanged(nameof(PopulationFemale)); }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

RelayCommand.cs

using System;
using System.ComponentModel;
using System.Windows.Input;

namespace test_01
{
    public class RelayCommand : ICommand
    {
        private readonly Action execute;
        private readonly Func<bool> canExecute;

        public RelayCommand(Action execute, Func<bool> canExecute = null)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public bool CanExecute(object parameter) => canExecute == null || canExecute();
        public void Execute(object parameter) => execute();
    }
}

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?