やる気駆動型エンジニアの備忘録

WPF(XAML+C#)の話題を中心に.NET/Android/CI やたまに趣味に関するブログです

Windows Forms のChart を使う~StripLine:グラフの装飾~

Windows Forms で、StripLine を使うと色々便利だったのでメモ。

StripLineってなに?

MSDN StripLineより抜粋

ストリップ ラインは、標準またはカスタムの間隔でグラフの背景に影を付ける水平または垂直の範囲です。 ストリップ ラインを使用することができます。
 ・グラフ上の個々 の値を検索するためには、読みやすさを向上します。
 ・グラフをわかりやすくときは、データ ポイントを分割します。
 ・一定間隔で実行する日付を強調表示、たとえば、週末のデータを識別するために参照します。
 ・データの特定のキー範囲を強調表示します。
 ・特定の定数値にしきい値を表す線を追加します。

StripLine クラス (System.Windows.Forms.DataVisualization.Charting)

かなり抽象的な説明ですが、"グラフを装飾するための線分を表示するもの"という認識でしょう。
おそらく、最も使用するパターンは、
 ・グラフ上の個々 の値を検索するためには、読みやすさを向上します。
 ・特定の定数値にしきい値を表す線を追加します。
このあたりでしょう。

試しに使ってみる

今回は、ランダムに設定したデータの平均値をStripLine で表示してみます。
開発環境はこちら。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Chart02Sample
{
    using System.Windows.Forms.DataVisualization.Charting;

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

        private void Form1_Load(object sender, EventArgs e)
        {
            UpdateChartData();
        }
        
        private void UpdateChartData()
        {
            chart1.Series.Clear();
            chart1.ChartAreas[0].AxisY.StripLines.Clear();

            // グラフ用のデータを構築する。
            Random r = new Random();
            PlotData[] plotData = Enumerable.Range(0, 100).Select(x => new PlotData { X = x, Y = r.Next(10, 90) }).ToArray();
            Series series = chart1.Series.Add("サンプル");
            series.ChartType = SeriesChartType.Line;
            foreach (var data in plotData)
            {
                series.Points.AddXY(data.X, data.Y);
            }

            // 平均値の水平線を表示する。
            double mean = chart1.DataManipulator.Statistics.Mean("サンプル");
            StripLine stlipLine = new StripLine
                                      {
                                          Text              = $"平均値:{mean}",
                                          TextAlignment     = StringAlignment.Near,     // テキストの水平位置(Near:左, Center:中央, Far:右)
                                          TextLineAlignment = StringAlignment.Far,      // テキストの垂直位置(Near:下, Center:中央, Far:上)
                                          Interval          = 0,                        // 線分の表示間隔 値を設定すると指定した間隔で表示される。
                                          IntervalOffset    = mean,                     // 線分の表示オフセット
                                          BorderWidth       = 1,
                                          BorderDashStyle   = ChartDashStyle.DashDot,
                                          BorderColor       = Color.Blue,
                                      };
            chart1.ChartAreas[0].AxisY.StripLines.Add(stlipLine);
        }

        private void ReloadButton_Click(object sender, EventArgs e)
        {
            UpdateChartData();
        }
    }

    /// <summary>
    /// グラフデータ
    /// </summary>
    public class PlotData
    {
        public int X { get; set; }

        public double Y { get; set; }
    }
}

平均値は chart1.DataManipulator.Statistics.Mean("サンプル") で求めています。
DataManipulator は、プロットデータの集計など、結構汎用的に使えて便利なのですが今回は割愛。

これを実行するとこのようになります。↓↓↓
f:id:iyemon018:20170413165418j:plain

これを応用すれば、最大値や最小値、データのプロット範囲などを表現することが可能です。
ちょっとした工夫でグラフが見やすくなるので覚えておくと便利そうです。

今回使用したサンプルはこちら↓↓↓
github.com

Windows Forms のChart を使う~SeriesChartType:グラフ系列種別~

最近Windows FormsのChartを使用する機会があったので、備忘録として使い方を残します。

まず今回はChartにどんな種類のグラフが使用できるのか見ていきます。
Chartの系列は"Series"プロパティでアクセスすることができます。
この系列の種類を変えるには、ChartType プロパティを変更すればOKです。
ChartTypeプロパティは、列挙体のSeriesChartType 型ですが、これは以下の種別が定義されています。
なので、ここに定義されているグラフの種類=Chartで表現できる種類 ということです。

続きを読む

Visual Studio 2017を使ってみた

いよいよリリースされたVisual Studio 2017
さっそくPCにインストールしてみました。

まぁ、ぶっちゃけどんな機能が追加されたのかは以下のページを参照したほうが手っ取り早いです。

www.atmarkit.co.jp
www.visualstudio.com

リリースノートは以下から。
www.visualstudio.com

最初インストールしたときには気が付かなかったんですが、"Live Unit Testing"の機能はEnterpriseにしかないそうで。
Communityエディションにはありませんので注意。

もう一つ気になったのですが…
f:id:iyemon018:20170309215654j:plain
"XML をクラスとして貼り付ける"のメニュー無くね?
あれか、XMLはもはや時代遅れということか…

もはや定番拡張機能となったProductivity Power Toolsの2017向けツールもリリースされています。
blogs.msdn.microsoft.com

Team Foundation Server 2017

また、TFSも次期バージョンがリリースされています。
www.visualstudio.com

WPFでCarouselPanelを作る

前回、PathListBoxを使って遊んでみましたが、今回もPathListBoxを使ってみます。
iyemon018.hatenablog.com

ルーセルパネル(Carousel Panel)は、左右にコンテンツをスライドするあれです。
まずはサンプルプログラムの動作を見てみます。

f:id:iyemon018:20170216154556g:plain

このように複数のコンテンツを無限にスライドし続けることができます。
以下、サンプルソースです。
必要なアセンブリ名前空間は、前回の記事を参照してください。

続きを読む

WPFでPathListBoxを使う

WPFのコントロールにPathListBoxというものがあります。
これは、通常のItemsControlとは少し異なる動作をします。
百聞は一見にしかず、まずは以下のイメージを御覧ください。

f:id:iyemon018:20170212225314g:plain

単純な動作ではありますが他のList系コントロールとは明らかに動作が異なります。
このような一風変わったPathListBoxですが、かなり簡単に実装することができます。

PathListBoxのプロパティ

PathListBoxには以下のプロパティがあります。

  • StartItemIndex
  • WrapItems
  • LayoutPaths

StartItemIndex

ItemsSourceに設定したコレクション要素の開始インデックスのプロパティです。
例えば"StartItemIndex=2"に設定すると以下の様になります。

f:id:iyemon018:20170212231943p:plain

WrapItems

このプロパティをtrueにすると、表示範囲外にあるコレクション要素をラップします。
falseにすると、表示範囲外のコレクション要素はラップされずに見切れてしまいます。

f:id:iyemon018:20170215152657j:plain

LayoutPaths

ItemsSourceにバインドしたコレクションを配置するためのレイアウト要素を設定することができます。
上記のサンプルでは、赤いPathを作成し、そのPathをLayoutPathsに設定しています。

以下、サンプルのソースです。

事前準備

PathListBoxを使用するには、以下のアセンブリを参照する必要があります。
Blendを使用すればドラッグアンドドロップで自動的に参照が追加されます。

PathListBoxを参照するには、予め以下の名前空間を追加しておきましょう。

xmlns:ec="http://schemas.microsoft.com/expression/2010/controls"
<Window x:Class="PathListBoxSample.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:ec="http://schemas.microsoft.com/expression/2010/controls"
        xmlns:local="clr-namespace:PathListBoxSample"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="240"
        Height="240"
        mc:Ignorable="d">
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <ec:PathListBox ItemsSource="{Binding Values,
                                              Mode=OneWay}"
                        StartItemIndex="2"
                        WrapItems="True">
            <ec:PathListBox.LayoutPaths>
                <ec:LayoutPath SourceElement="{Binding ElementName=path}" />
            </ec:PathListBox.LayoutPaths>
        </ec:PathListBox>
        <Border>
            <Path x:Name="path"
                  Margin="47.958,56.296,51.484,48.649"
                  Data="M167.9651,297.05052 C167.1821,300.96551 298.95333,358.22029 298.95333,358.22029 L336.53545,481.93313 501.22995,268.87785 584.51969,339.3325"
                  Stretch="Fill"
                  Stroke="#FFE40000" />
        </Border>
        <Button Width="32"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Command="{Binding AddCommand,
                                  Mode=OneWay}"
                Content="+" />
        <Button Width="32"
                Margin="36,0,0,0"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Command="{Binding SubCommand,
                                  Mode=OneWay}"
                Content="-" />
    </Grid>
</Window>
  • MainViewModel.cs
namespace PathListBoxSample
{
    using System.Collections.ObjectModel;
    using System.Linq;
    using Microsoft.Practices.Prism.Commands;
    using Microsoft.Practices.Prism.Mvvm;

    public class MainViewModel : BindableBase
    {
        public ObservableCollection<int> Values { get; private set; }

        public DelegateCommand AddCommand { get; private set; }

        public DelegateCommand SubCommand { get; private set; }

        public MainViewModel()
        {
            Values = new ObservableCollection<int>(Enumerable.Range(1, 5));
            AddCommand = new DelegateCommand(() => Values.Add(Values.Max() + 1));
            SubCommand = new DelegateCommand(() =>
                                             {
                                                 if (Values.Any()) Values.Remove(Values.Max());
                                             });
        }
    }
}

このようにPathListBoxはWPFの中でもかなり特殊なコントロールです。
以下のページにもう少し詳しい使い方が記述されています。

パスに沿ったアイテムのレイアウト

今回作成したサンプルはこちら↓↓↓↓↓
github.com