WPF でグラフのイメージを作成・表示(2)

前回の「WPF でグラフのイメージを作成・表示」の続きです。前回は関数グラフの作成だったので、今回は「グラフの塗りつぶし」を使う例として棒グラフを作成します。

実際に表示を行った画面は次のようになります。

棒グラフの表示例
棒グラフ

棒グラフ表示のデータは、Wikipedia のヒストグラムの項目から引用しています。

それでは、プログラムです。
「WPF アプリケーション」なプロジェクトを作成します(プロジェクト名は Histogram01 としています)。
最初にグラフィック描画関係から。
プロジェクトに「Utility」フォルダを追加します。
「DeviceHelper」クラス、「ChartData」クラス、「ChartBase」クラスは、前回の「WPF でグラフのイメージを作成・表示」のものをそのまま使います。
次にビンの情報を保持する「Bin」クラスを作成します。

namespace Histogram01.Utility
{
    class Bin
    {
        public Bin(int interval, int quantity)
        {
            Interval = interval;
            Quantity = quantity;
        }

        /// <summary>
        /// ビンの間隔
        /// </summary>
        public int Interval { get; private set; }

        /// <summary>
        /// 該当数
        /// </summary>
        public int Quantity { get; private set; }
    }
}

次に「Histogram」クラスを作成します。Histogram クラスは、ChartBase クラスを継承し、getSeriesGeometry メソッドを実装します。

using System;
using System.Windows;
using System.Windows.Media;

namespace Histogram01.Utility
{
    class Histogram : ChartBase
    {
        public Histogram(int canvasWidth, int canvasHeight) : base(canvasWidth, canvasHeight) {}

        protected override Geometry getSeriesGeometry(object[] bins)
        {
            if (bins.GetType() != typeof(Bin[]))
            {
                throw new ArgumentException("引数の型が Bin[] ではありません。");
            }

            var group = new GeometryGroup();
            RectangleGeometry rectGeo;
            var x = 0;
            for (var i = 0; i < bins.Length; ++i)
            {
                rectGeo = new RectangleGeometry();
                var item = (Bin)bins[i];
                rectGeo.Rect = new Rect(x, 0, (int)(item.Interval * ZoomRatioX) - x, item.Quantity * ZoomRatioY);
                group.Children.Add(rectGeo);
                x = (int)(item.Interval * ZoomRatioX);
            }

            return group;
        }
    }
}

getSeriesGeometry メソッドはビンの配列を受け取り、グラフの図形情報を返すようにしています。

ここまででグラフィック描画関係ができたので、次はビューモデルです。
MVVM パターンなので、お決まりのコマンド処理とプロパティチェンジイベントの発火を提供する ViewModelBase クラスは別記事の ViewModelBase で書いたものを使っています。別プロジェクトにして作成した DLL を参照設定で追加するなり、プロジェクト内にソースを取り入れるなりしてください(次のコードは DLL を参照する前提で書いています)。
プロジェクトに「ViewModels」フォルダを追加し、ViewModels フォルダに「MainViewModel」クラスを作成します。

using System.Windows.Input;
using System.Windows.Media;

using MakCraft.ViewModels;

using Histogram01.Utility;

namespace Histogram01.ViewModels
{
    class MainViewModel : ViewModelBase
    {
        private const int IMAGE_WIDTH = 500;
        private const int IMAGE_HEIGHT = 350;
        private Histogram _chart;

        public MainViewModel()
        {
            _chart = new Histogram(IMAGE_WIDTH, IMAGE_HEIGHT);
            ImageSource = _chart.Canvas;
            ImageWidth = IMAGE_WIDTH;
            ImageHeight = IMAGE_HEIGHT;

            _chart.ZoomRatioX = 0.4;
            _chart.ZoomRatioY = 15;
            _chart.TranslateX = 60;
            _chart.TranslateY = 30;
            _chart.AxisXHeader = "閲覧回数";
            _chart.AxisYHeader = "日数";
            _chart.MarginX = 20;
            _chart.MarginY = 2;
            _chart.IsAxisXNickVal = true;
            _chart.AxisXNicValStart = 0;
            _chart.AxisXNickValIncrements = 100;
            _chart.IsAxisYNickVal = true;
            _chart.AxisYNickValStart = 0;
            _chart.AxisYNickValIncrements = 1;
            _chart.AxisXScaleStart = 100;
            _chart.AxisXScaleIncrements = 100;
            _chart.IsAxisYScale = true;
            _chart.AxisYScaleStart = 1;
            _chart.AxisYScaleIncrements = 1;
        }

        private int _imageWidth;
        public int ImageWidth
        {
            get { return _imageWidth; }
            set
            {
                _imageWidth = value;
                base.RaisePropertyChanged(() => ImageWidth);
            }
        }

        private int _imageHeight;
        public int ImageHeight
        {
            get { return _imageHeight; }
            set
            {
                _imageHeight = value;
                base.RaisePropertyChanged(() => ImageHeight);
            }
        }

        private ImageSource _imageSource;
        public ImageSource ImageSource
        {
            get { return _imageSource; }
            set
            {
                _imageSource = value;
                base.RaisePropertyChanged(() => ImageSource);
            }
        }

        private void drawChartExecute()
        {
            var chartData = new ChartData[] {
                new ChartData { Datas = getViews(), BorderColor = Colors.Black,
                    IsFill = true, FillColor = Colors.BurlyWood },
            };
            _chart.DrawGraph(chartData);
        }
        private ICommand _drawChartCommand;
        public ICommand DrawChartCommand
        {
            get
            {
                if (_drawChartCommand == null)
                {
                    _drawChartCommand = new RelayCommand(drawChartExecute);
                }
                return _drawChartCommand;
            }
        }

        private Bin[] getViews()
        {
            return new Bin[] {
                new Bin(99, 1),
                new Bin(199, 2),
                new Bin(299, 4),
                new Bin(399, 5),
                new Bin(499, 4),
                new Bin(599, 7),
                new Bin(699, 7),
                new Bin(799, 1)
            };
        }
    }
}

getViews メソッドで統計データの配列を作成します。統計データの配列は、drawChartExecute メソッドで ChartData クラスのインスタンスに棒の枠の色、棒の塗りつぶし指示、塗りつぶしの色と共にセットしています。

最後にビューの MainWindow.xaml です。

<Window x:Class="Histogram01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:Histogram01.ViewModels"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <vm:MainViewModel />
    </Window.DataContext>
    
    <StackPanel>
        <TextBlock Text="ヒストグラムのテスト" FontSize="18" Margin="8" HorizontalAlignment="Center" />
        
        <Button
            Command="{Binding DrawChartCommand}"
            Content="表示" Margin="8" Padding="8 4" MinWidth="100" HorizontalAlignment="Right" />
        
        <Border BorderBrush="Azure" BorderThickness="1">
            <Image Width="{Binding ImageWidth}" Height="{Binding ImageHeight}" Source="{Binding ImageSource}" />
        </Border>
    </StackPanel>
</Window>

以上で「表示」ボタンをクリックすると棒グラフが表示されます。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です