VisualStudio2019でLiveCharts2を使う

# 概要
:::note info
2023/06/21　初稿時点でLiveCharts2は8.00のベータ版です。新機能が随時追加されています。
:::

本記事はLiveCharts2を.NetFrameworkで使うための導入と、その際につまずいた点を纏めたものです。
内容は以下の通り。

| 項番 | タイトル（リンクが上手く動作できず、飛べません）|
|:----:|:-------------|
| 1 | [PC環境](#PC環境) |
| 2 | [LiveCharts2のサイトとGithub](#LiveCharts2のサイトとGithub) |
| 3 | [LiveCharts2の対象フレームワーク](#LiveCharts2の対象フレームワーク) |
| 4 | [プロジェクト作成とパッケージのインストール](#プロジェクト作成とパッケージのインストール) |
| 5 | [グラフを作ってみよう](#グラフを作ってみよう) |
| 6 | [Githubに公開されているプロジェクトについて](#Githubに公開されているプロジェクトについて) |
| 7 | [グラフラベルの日本語化](#グラフの日本語化)) |
| 8 | [おわりに](#おわりに) |


# PC環境

- Windows10 Pro 22H2
- VisualStudio 2019 16.11.23 (Professional)
VisualStudioには下記画像のチェックがついた２つがインストールされていれば良いでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/53e4db70-ec70-3214-053b-bb7ab3dd2939.png)

# LiveCharts2の各サイト
- [Github](URL "https://github.com/beto-rodriguez/LiveCharts2")
"Livecharts2/samples/ViewModelSamples"にViewModelのコードが、"Livecharts2/samples/ViewModelSamples"にViewのサンプルが入っています。ViewModel、Viewが何か分からない方は[こちら](#Githubに公開されているプロジェクトについて)を参照。

- [LiveCharts2_API](URL "https://lvcharts.com/api/2.0.0-beta.800/index")
APIが公開されています。私は見方が分からないのであまり参考になっていませんが、見たい方はどうぞ。

- [LiveCharts2_インストールやサンプルコードなど](URL "https://lvcharts.com/docs/winforms/2.0.0-beta.800/gallery")
インストール手順などはこちらのサイトを参照しました。また、どのような種類のグラフが作成できるかを概観できるため、自分が作りたいグラフについての記事は一度目を通した方が良いと思います。サンプルコードも載っています。

# LiveCharts2の対象フレームワーク
VisualStudioで扱えるフレームワークの内、**.NET5.0以上**, **.NET Core3.1以上**, **.NET Framework462以上**で使えます。
※[参考](URL:https://www.nuget.org/packages/LiveChartsCore.SkiaSharpView.WinForms#supportedframeworks-body-tab)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/335f8930-fe2c-2c1b-c410-71d3f74c82f6.png)



# プロジェクト作成とパッケージのインストール
VisualStudio 2019のインストールは他記事を参照してください。

### プロジェクト作成
VisualStudioを起動して、「<strong>新しいプロジェクトの作成</strong>」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/dc0b9dc4-057d-e241-2d66-27399167e715.png)
次に、「**Windows フォーム アプリケーション(.NET Framework)**」を選択します。
下記のように検索ワードを設定すると見つけやすいかと思います。
- 言語 ＝ <strong>C#
- プラットフォーム　＝　<strong>Windows
- プロジェクトの種類　＝　<strong>デスクトップ

ない場合は、VisualStudio Installerから必要なパッケージをインストールしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/162ba176-4737-23fa-7242-081ca15fe7cd.png)
　構成について、名前や場所は自由にしてください。フレームワークを<strong>.NET Framework 4.7.2</strong>として、作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/118a3abb-15f8-1729-fc4a-8d16b7c396d3.png)

完了です。初期表示は以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/d03f4804-9be6-3079-af76-597a688291a9.png)

### LiveCharts2のインストール
　VSのタブより「**ツール**」＞「**NuGetパッケージマネージャー**」＞「**パッケージマネージャーコンソール**」の順で選択します。
パッケージマネージャーコンソールが表示されるので、以下のコマンドを入力してください。

`NuGet\Install-Package LiveChartsCore.SkiaSharpView.WinForms -Version 2.0.0-beta.801`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/29f6e564-827a-6ee5-1c40-e1796b07d9d2.png)
ちゃんとインストールされましたね。

### CommunityToolkit.MVVMのインストール
　LiveCharts2はMVVMモデルでサンプルコードの実装を行っています。LiveCharts2の公式サイトでおすすめされていたので、取り合えず入れておきましょう。後から拡張するときにここで引っかかるのも嫌なので。
※[公式サイトの該当ページ](URL:https://lvcharts.com/docs/WinForms/2.0.0-beta.800/Overview.Mappers)

　VSのタブより「**ツール**」＞「**NuGetパッケージマネージャー**」＞「**ソリューションのNuGetパッケージの管理**」を開きます。
　検索タブに”**CommunityToolkit.MVVM**”と入力すると、青アイコンのライブラリがヒットします。これの最新版（8.2.0）をインストールしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/286e9ef0-072f-8e36-206a-1bf61da681e0.png)

# グラフを作ってみよう
### 準備 - フォルダ構成
　ソリューションエクスプローラーのプロジェクトを選択した状態で右クリックし、「**追加**」＞「**新しいフォルダ**」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/5c9ad86a-7aea-12b3-df45-8b68d2b6d74d.png)
追加したフォルダ名を今回は"**VM**"とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/49562f66-7333-cf21-6ca2-d68d2108578a.png)
次に、ファイルを２つ追加します。
先程作成したVMフォルダを選択して、「Ctrl＋Shift＋A」を入力。
「**新しい項目の追加**」画面がポップアップするので、「**クラス**」をダブルクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/dc24fc78-7e55-17d0-3d65-b1a83f92f9c9.png)
こちらのファイル名を以下のようにしましょう。
- ViewModel_Bar
- ViewModel_Label

最終的に、以下のようなフォルダ構成になれば大丈夫です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/0386015b-93b9-c2b6-e20c-a77fd1f1da49.png)

### コード
先ほど作成したファイル２つ（ViewModel_Label.cs、ViewModel_Bar.cs）と、既存のForm1.csに下記のコードを作成しました。そのまま張り付けてみましょう。

<details><summary>Form1.cs</summary><div>

```Form1.cs
using ForQiitaLC2.VM;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.SkiaSharpView.WinForms;
using SkiaSharp;
using System.Drawing;
using System.Windows.Forms;
using VM;

namespace ForQiitaLC2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            InitializeBar();
        }

        private void InitializeBar()
        {
            Size = new System.Drawing.Size(1454, 677);

            var viewModelBar = new ViewModel_Bar();
            var C1 = new CartesianChart
            {

                Series = viewModelBar.Series,
                // out of livecharts properties...
                Location = new Point(0, 0),
                Size = new Size(1000, 600),
                Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom,

            };

            C1.Name = "LayeredBar";

            var viewModel_Label = new ViewModel_Label();

            C1.XAxes = viewModel_Label.XAxes;
            C1.YAxes = viewModel_Label.YAxes;

            C1.LegendTextPaint = new SolidColorPaint
            {
                Color = SKColors.Black,
            };
            C1.LegendPosition = LiveChartsCore.Measure.LegendPosition.Right;

            Controls.Add(C1);
        }
    }
}

```

</div></details>

<details><summary>ViewModel_Bar.cs</summary><div>

```ViewModel_Bar.cs
using LiveChartsCore;
using LiveChartsCore.Kernel;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Drawing.Geometries;
using SkiaSharp;

namespace ForQiitaLC2.VM
{
    public partial class ViewModel_Bar 
    {
        public ViewModel_Bar()
        {
            LiveChartsSkiaSharp.DefaultSKTypeface = SKFontManager.Default.MatchCharacter('あ');

            var series1 = new ColumnSeries<double>
            {
                Name = "目標",
                Values = new[] { 6.0, 3.0, 5.0, 7.0, 3.0, 4.0, 6.0, 3.0 },
                Stroke = null,
                MaxBarWidth = 50,
                IgnoresBarPosition = true,
            };

            var series2 = new ColumnSeries<double>
            {
                Name = "比較対象",
                Values = new[] { 2.0, 4.0, 8.0, 9.0, 5.0, 2.0, 4.0, 7.0 },
                Stroke = null,
                MaxBarWidth = 27,
                IgnoresBarPosition = true,
            };

            series1.ChartPointPointerDown += OnPointerDown; 
            series1.ChartPointPointerHover += OnPointerHover; 
            series1.ChartPointPointerHoverLost += OnPointerHoverLost; 
            series1.PointCreated += Series1_PointCreated;

            Series = new ISeries[] {series1, series2};
        }

        private void Series1_PointCreated(ChartPoint<double, RoundedRectangleGeometry, LabelGeometry> obj)
        {
            obj.Visual.Opacity = 0.5f;
        }

        public ISeries[] Series { get; set; }

        private void OnPointerDown(IChartView chart, ChartPoint<double, RoundedRectangleGeometry, LabelGeometry> point)
        {
            if (point.Visual is null) return;
            chart.Invalidate(); 
        }

        public void OnPointerHover(IChartView chart, ChartPoint<double, RoundedRectangleGeometry, LabelGeometry> point)
        {
            if (point.Visual is null) return;
            //point.Visual.Fill = new SolidColorPaint(SKColors.Yellow);
            point.Visual.Opacity = 0.5f;
            chart.Invalidate();
        }

        private void OnPointerHoverLost(IChartView chart, ChartPoint<double, RoundedRectangleGeometry, LabelGeometry> point)
        {
            if (point.Visual is null) return;
            //point.Visual.Fill = null;
            point.Visual.Opacity = 0.5f;
            chart.Invalidate();
        }
    }
}
```
</div></details>

<details><summary>ViewModel_Label.cs</summary><div>

```ViewModel_Label.cs
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.SkiaSharpView.Painting.Effects;
using SkiaSharp;

namespace VM
{
    public partial class ViewModel_Label 
    {
        public ViewModel_Label()
        {
            LiveChartsSkiaSharp.DefaultSKTypeface = SKFontManager.Default.MatchCharacter('あ');
        }


        public Axis[] XAxes { get; set; } =
        {
            new Axis
            {

                Labels = new string[] { "1", "2", "3", "4", "5","6", "7", "8", "9", "10" },

                Name = "X軸",
                NamePaint = new SolidColorPaint
                {
                    Color = SKColors.Black,
                },

                LabelsPaint = new SolidColorPaint(SKColors.Blue),
                TextSize = 10,

                SeparatorsPaint = new SolidColorPaint(SKColors.LightSlateGray) { StrokeThickness = 2 },
            }
        };

        public Axis[] YAxes { get; set; } =
        {
            new Axis
            {
                Name = "Y軸",
                NamePaint = new SolidColorPaint
                {
                    Color = SKColors.Red,
                },
                MinLimit = 0,
                LabelsPaint = new SolidColorPaint(SKColors.Green),
                TextSize = 20,

                SeparatorsPaint = new SolidColorPaint(SKColors.LightSlateGray)
                {
                    StrokeThickness = 2,
                    PathEffect = new DashEffect(new float[] { 3, 3 })
                }
            }
        };
    }
}
```
</div></details>
貼り付け後、デバッグを行うと以下の画像が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/3d5a1e9b-f9a5-00ff-6446-2cea53a11a86.png)
棒グラフ、X-Y軸、凡例、メモリが表示されています。


# Githubに公開されているプロジェクトについて
### 1. プロジェクトはVisualStudio 2022で開発されている</dt>
最初、VisualStudio 2019でもGithubから持ってきたプロジェクトを開けるかと思っていました。しかし、プロジェクトにアンドロイド用の開発環境が含まれており、この開発環境が2019だと対象外なようで、開けませんでした。もし開きたい場合は、VisualStudio 2022をインストールして、そちらから開きましょう。

　※　プロジェクトの開発環境を2022に出来るなら、そちらがベターかと思います。
　※　エラーがたくさん出てきたので、筆者はコンパイル出来ませんでした。
### 2. namespaceの宣言
Livecharts2のGithubに公開されているサンプルコードで、namespaceの宣言が以下のようにされているファイルがあります。
```c#
namespace ViewModelsSamples.Axes.ColorsAndPosition;
```
[C#10の新機能](URL:https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-10#file-scoped-namespace-declaration)を見てみると、global using　ディレクティブという機能が追加されています。これは「ほかのファイルからそのnamespace空間で宣言された全てを扱える」というもので、グローバル変数のnamespace版のようなものです。

しかしながら、.NetFramework472ではC#7.3を対象としているため、Gitのサンプルコードをそのまま移植しようとするとエラーが発生します。次に述べるnull許容参照型とともに、変更をしてください。
　※[C#のバージョンと.NET関連フレームワークの対応](URL:https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/configure-language-version)

次のように変更すると当該エラーは消えます。従来の宣言になります。
```c#
namespace ViewModelsSamples.Axes.ColorsAndPosition
{
   ...
}
```

### 3. null許容参照型</dt>
**null許容参照型**は[C#8.0で追加された機能](URL:https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/nullable-reference-types)です。
型"T"を宣言する時に、「"T"＋"?"」と記述します。
```c#
string notNull = "Hello";   // 1
string? nullable = default; // 2
```
1がnull非許容参照型であり、逆参照が可能です。2はnull許容参照型で、逆参照をする場合は先にnullかどうかをチェックする必要があります。
null許容参照型は既存の型に対する注釈（nullかもしれないよ！という宣言）です。コンパイラはこの注釈を利用して、ユーザがコード内で発生する可能性のある**null参照エラーを検出しやすく**なります。

これも移植するときにエラーが出てしまうので、サンプルコードから持ってきたときは"?"を削除してあげましょう。削除した場合でも、筆者環境では特にエラーは発生していませんでした。

# LiveCharts2インストール時の失敗
最初、LiveCharts2の公式サイトから下記のコマンドを引っ張ってきて、パッケージマネージャーコンソールに入力しました。

 ` Install-Package LiveChartsCore.SkiaSharpView.WinForms `

しかしながら、このコマンドでは失敗します。
どうやらバージョンを指定していないせいでエラーが出るようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/350029bc-8f3f-1519-5e2b-44586f670dff.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/dcd6fb85-0dab-df32-349a-978521c4f857.png)
バージョンまで指定してあげると、インストールが出来ました。
[ここ](URL:https://www.nuget.org/packages/LiveChartsCore.SkiaSharpView.WinForms#supportedframeworks-body-tab)から最新版のコマンドが取得できます。

# グラフの日本語化
ViewModel_Bar.cs, ViewModel_Label.csのコンストラクタの中に、日本語化のコードがあります。これらを一度コメントアウトして実行すると、下記の画像のようになります。
```c#
//LiveChartsSkiaSharp.DefaultSKTypeface = SKFontManager.Default.MatchCharacter('あ');
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3136613/7af33141-b6da-562f-3cb0-b6c19cb565ed.png)
コンストラクタの中で宣言すると、色々なところで宣言する必要がないので楽です。

# LayeredBarの透明度調整
ViewModel_Bar.csの中に、下記の宣言があります。
```c#
 series1.ChartPointPointerDown += OnPointerDown;  // ポインタクリック時 
 series1.ChartPointPointerHover += OnPointerHover;  // ポインタホバー時
 series1.ChartPointPointerHoverLost += OnPointerHoverLost; // ポインタホバーロスト時
 series1.PointCreated += Series1_PointCreated;  // ポインタ作成時
```
各メソッドに割り当てた関数の中では、不透明度の操作、操作内容の適用を行っています。
各メソッドに対して何も定義しない場合、LiveCharts2デフォルトの動作を行います。筆者の場合はいちいち不透明度が変わるのが邪魔だったため、不透明度を一律0.5としています。

series2には何も割り当てていないので、series1とseries2で何がデフォルトの動作か比較してみてください。

# おわりに
随時詰まったところがあれば追記していこうと思います。