C#

【WPF】Live Chartsを使ったマテリアルデザインカード

最近のWebサイトでは、カードスタイルのデザインが流行っています。TwitterやFacebookをはじめとする多くのWebサイトで採用されているデザインです。

Googleが提唱するマテリアルデザインを取り入れたカードは、オシャレで見た目がいいものが多いです。このカードスタイルを自作のアプリケーションにも取り入れたいですよね。

前回記事にしたLive Chartsの公式サイトにマテリアルデザインカードを使ったサンプルがあるので、このサンプルを元に、マテリアルデザインカードの実装を行ってみたいと思います。

そもそもカードデザインとは?

名前のとおり、カードの様な四角形をした見た目で、そのカード内にコンテンツの情報をまとめたものです。

1つのカードに対して画像やテキスト、ボタン等さまざまな要素が含まれています。カードをクリックすると詳細な情報を表示してくれたり、カード内にあるボタンをクリックするとお気に入り登録をすることが可能です。

Googleのサイトでは、ショッピングや地図、天気予報などでマテリアルデザインを使ったカードが多くあります。自作のアプリケーションのデザインを作成する際の参考になります。

カードのルール

カードのデザインにはいくつかのルールがあります。

  1. 文字がはみ出してはいけない
  2. 立体カードの上に立体ボタンはNG
  3. カードを重ねない
  4. フローティングアクションボタンは使わない
  5. 複数枚並べるときは縦スクロール
  6. カード内でスクロールは基本できない(デスクトップはOK)
  7. 全てをカードにする必要はない

このルールについては、以下のリンクに詳細な内容が記載してありますので、参考までに。

開発環境

  • OS:Windows 10 Home
  • 開発環境:Visual Studio 2019
  • 言語:C#
  • 開発プラットフォーム:WPF

プログラムの実装

さっそくXAML とコードをそれぞれ記述します。

XAML実装

カードの下地となる部分をGridで作成し、4分割にします。1行目にグラフのタイトル、2行目にグラフ、3行目にグラフの表示内容、4行目にボタンを配置します。

1行目〜4行目に背景をWhiteに設定したボーダー1を配置し、1行目〜2行目に背景をBlueVioletに設定したBorder2をBorder1の上に配置します。

<Grid Grid.Row="1" Margin="70,5,70,20">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height=".6*" />
        <RowDefinition Height=".3*" />
        <RowDefinition Height=".1*" />
    </Grid.RowDefinitions>
    <!--  4行を背景白のボーダーで結合  -->
    <Border
        x:Name="Border1"
        Grid.Row="0"
        Grid.RowSpan="4" 
        Background="White"
        CornerRadius="5" />
    <!--  2行を背景白のボーダーで結合  -->
    <Border
        x:Name="Border2"
        Grid.Row="0"
        Grid.RowSpan="2"
        Background="#007BB1" />
</Grid>

エフェクトの追加

EffectでGridに影をつけます。DropShadowEffectを使うことで、影の方向やぼかし具合、柔らかさなどを設定することができます。

DropShadowEffectの主要なプロパティは以下になります。

  • Direction : 影が付く角度を設定します。下図は、0度 〜 360度まで45度刻みのサンプルです。
  • ShadowDepth : オブジェクトと影の間の距離を設定します。下図は、0 〜 12まで4刻みのサンプルです。
  • Opacity : 影の不透明度を設定します。 有効な範囲は、0.0 ~ 1.0 です。0.0 は影が完全に透明になり、1.0 は影が完全に不透明になります。例えば、値 0.5 は影の不透明度が 50% になります。
  • BlurRadius:影のぼかしを設定します。下図は、値 0 のぼかし無しと値 10 のぼかし有りのサンプルです。

Gridに以下の様にエフェクトを適用させました。

<!--  エフェクト追加  -->
<Grid.Effect>
    <!--  影の方向、ぼかし具合、柔らかさを設定  -->
    <DropShadowEffect
        BlurRadius="15"
        Direction="270"
        Opacity=".2"
        RenderingBias="Quality"
        ShadowDepth="1" />
</Grid.Effect>

マスクの追加

Border1はCornerRadiusプロパティで4箇所の角を丸くしています。しかし、Border1の上にBorder2を被せたことで、角の丸みが見事に消えてしまいました。Border1の角の丸みを復活させるために、OpacityMaskのVisualBrushを使います。

<!--  Border1でマスクし、マスクの対象外のビジュアルを透明にする  -->
<Grid.OpacityMask>
    <VisualBrush Visual="{Binding ElementName=Border1}" />
</Grid.OpacityMask>

VisualBrushのタブ内にVisualプロパティを追加し、Border1をバインディングすることで、マスク対象外の部分を透明にすることができます。

グラフの設定

リソースで折れ線グラフのスタイルを設定します。設定するプロパティの内容は以下です。

  • StrokeThickness:線の太さ
  • Stroke:線の色
  • Fill:領域の塗り潰し
  • PointGeometrySize:マーカー(点)の太さ
  • LineSmoothness:線の曲線。Valueの値を大きくすると、点と点の線を滑らかな曲線(スムージング)にする。

グラフのX軸のラベルとY軸のラベルを非表示にします。ShowLabelsプロパティとIsEnabledプロパティを共にFalseに設定します。

<!--  折れ線のスタイル変更  -->
<Grid.Resources>
    <Style TargetType="lvc:LineSeries">
        <Setter Property="StrokeThickness" Value="3" />
        <Setter Property="Stroke" Value="White" />
        <Setter Property="Fill" Value="#4EFFFFFF" />
        <Setter Property="PointGeometrySize" Value="0" />
        <Setter Property="LineSmoothness" Value="0" />
    </Style>
    <!--  X軸とY軸のラベル非表示  -->
    <Style TargetType="lvc:Axis">
        <Setter Property="ShowLabels" Value="False" />
        <Setter Property="IsEnabled" Value="False" />
    </Style>
</Grid.Resources>
<!--  グラフ本体  -->
<lvc:CartesianChart
        Grid.Row="1"
        Margin="0,0,4,0"
        DataTooltip="{x:Null}"
        Hoverable="False"
        Series="{Binding LastHourSeries}">
    <lvc:CartesianChart.AxisX>
        <lvc:Axis MinValue="1" />
    </lvc:CartesianChart.AxisX>
</lvc:CartesianChart>

グラフの説明文とグラフの値をTextBlockで記述します。グラフの値は、LastLectureプロパティをバインディングして反映させます。

<StackPanel
    Grid.Row="2"
    Margin="25,10"
    VerticalAlignment="Top">
    <TextBlock FontSize="13" Opacity=".4">
        one-minute chart<LineBreak />
        Stock price movementst every minute</TextBlock>
    <StackPanel Orientation="Horizontal">
        <TextBlock
            Margin="8,6"
            VerticalAlignment="Bottom"
            FontSize="26"
            Foreground="#303030">
            $
        </TextBlock>
        <TextBlock
            FontSize="32"
            Foreground="#303030"
            Text="{Binding LastLecture, StringFormat={}{0:N1}}" />
    </StackPanel>
</StackPanel>

コードの実装

まず、SeriesCollectionのインスタンスを生成します。その際、LineSeriesのValuesプロパティに適当な値を20個セットします。折れ線グラフの下部領域まで塗り潰しがされる様にAreaLimitプロパティを−10にします。

グラフ描画をする処理をTask.Runの中にまとめて非同期処理にします。非同期にまとめるのは画面がフリーズしてグラフが更新されなくなるのを防ぐためです。

グラフ描画する処理ですが、Thread.Sleep(1000) で1秒間隔でグラフの描画処理をするようにしています。

0.0 〜 1.0 未満の乱数が 0.3 以上なら 1、それ以外の場合は −1とし、 0 〜 5 未満の乱数の結果と乗算をします。この乱数の結果をLastHourSeriesのValuesプロパティに追加します。Valuesプロパティの一番古い値を削除し、常に20個になる様にします。

LastHourSeriesのValuesプロパティに追加された最新の値を取得し、LastLectureプロパティにセットします。セットされた値は、TextBlockとバインドしているので、値が画面に反映されます。

public partial class UserControlMaterialCards : UserControl, INotifyPropertyChanged
{
    public SeriesCollection LastHourSeries { get; set; }

    public UserControlMaterialCards()
    {
        InitializeComponent();

        double trend = 20;

        //折れ線グラフのインスタンス生成
        LastHourSeries = new SeriesCollection
            {
                new LineSeries
                {
                    AreaLimit = -10,
                    Values = new ChartValues<ObservableValue>
                    {
                        new ObservableValue(3),
                        new ObservableValue(5),
                        new ObservableValue(6),
                        new ObservableValue(7),
                        new ObservableValue(10),
                        new ObservableValue(13),
                        new ObservableValue(20),
                        new ObservableValue(19),
                        new ObservableValue(15),
                        new ObservableValue(10),
                        new ObservableValue(6),
                        new ObservableValue(5),
                        new ObservableValue(6),
                        new ObservableValue(7),
                        new ObservableValue(3),
                        new ObservableValue(4),
                        new ObservableValue(9),
                        new ObservableValue(2),
                        new ObservableValue(5),
                        new ObservableValue(8)
                    }
                }
            };

        //グラフ描画処理
        Task.Run(() =>
        {
            var r = new Random();
            while (true)
            {
                Thread.Sleep(1000);
                trend += (r.NextDouble() > 0.3 ? 1 : -1) * r.Next(0, 5);
                Application.Current.Dispatcher.Invoke(() =>
                {
                    LastHourSeries[0].Values.Add(new ObservableValue(trend));
                    LastHourSeries[0].Values.RemoveAt(0);
                    LastLecture = ((ChartValues<ObservableValue>)LastHourSeries[0].Values).Last().Value;
                });
            }
        });

        DataContext = this;
    }


    //ViewModelをバインド
    private double _lastLecture;
    public double LastLecture
    {
        get { return _lastLecture; }
        set
        {
            _lastLecture = value;
            OnPropertyChanged("LastLecture");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

アプリケーションの起動

さっそくアプリケーションを起動してみましょう。どこかのサイトで見たことある様なカードデザインになっています。こんなにちゃんと作れてしまうと感動しますね。(サンプルコードをほとんど引用していますが。)

グラフも1秒おきに更新されていていい感じです。株価チャートイメージしてるんですが、上昇トレンド継続してます。こんな株があれば購入したいところです。

最後に

Live Chartsの公式サイトにあるマテリアルデザインカードを使ったサンプルを参考にして、マテリアルデザインカードの作り方を紹介しました。オシャレなアプリケーションを作成する上で、抑えておきたい内容だと思います。

デザインカードの作り方、LiveChartの折れ線の作り方の参考になれば幸いです。

以上、最後まで読んでいただきありがとうございました。

プログラミングを学習したいなら…

プログラミングスキルを身に付けるなら、プログラミングを効率良く学べる
プログラミングスクール」がオススメです。

特にこんな方にオススメ!!
これからエンジニアを目指したい
プログラミングの専門性を高めたい
プログラミングを学んで副業をしたい
エンジニアに転職して年収をアップさせたい

プログラミングを触ったことがない未経験からでも、プログラミングスクールで学習すれば、エンジニアへ就職・転職することも可能です。

あなたの「行動力」と「やる気」で、あなたの人生を大きく変えるチャンスになることでしょう。

プログラミングスクールに興味がある方は是非チェックしてみてください。

> プログラミングを学ぶ <

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA