■画面
■ファイル構成
・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();
}
}