WPFプログラミング備忘録

WPF プログレスバーの外観のカスタマイズ

WPF

はじめに

この記事ではプログレスバーの外観のカスタマイズ方法について確認します。

プロジェクトのファイル構成は下図のとおりです。

開発環境は以下のとおりです。

オペレーティングシステムWindows10 x64
Visual StudioMicrosoft Visual Studio Community 2019 Version 16.11.2
.NET Framework4.7.2

サンプルプログラムの動作仕様

カスタマイズした3つのプログレスバーとデフォルトのプログレスバーを表示します。一番下がデフォルトのプログレスバーです。

実行すると下図のとおり、それぞれのプログレスバーが進行していくようにします。

プログレスバーのカスタマイズ(XAML)

カスタマイズするプログレスバーはControlTemplateを使ってApp.xamlのリソースとして定義します。

App.xamlファイルは以下のとおりです。

<Application x:Class="WpfCustomizeControl.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfCustomizeControl"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ControlTemplate TargetType="ProgressBar" x:Key="customProgressBar1">
            <Grid>
                <Rectangle x:Name="PART_Track">
                    <Rectangle.Fill>
                        <LinearGradientBrush EndPoint="1,0">
                            <GradientStop Color="BlanchedAlmond" Offset="0"/>
                            <GradientStop Color="Beige" Offset="1"/>
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
                <Rectangle x:Name="PART_Indicator"
                           Stroke="Black" StrokeThickness="1"
                           HorizontalAlignment="Left">
                    <Rectangle.Fill>
                        <LinearGradientBrush EndPoint="1,0">
                            <GradientStop Offset="0" Color="GreenYellow"/>
                            <GradientStop Offset="1" Color="Yellow"/>
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
                <TextBlock Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, StringFormat=\{0\}%}"
                           Foreground="{TemplateBinding Foreground}" FontSize="{TemplateBinding FontSize}" FontWeight="Bold"
                           VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </Grid>
        </ControlTemplate>

        <ControlTemplate TargetType="ProgressBar" x:Key="customProgressBar2">
            <Grid>
                <Rectangle x:Name="PART_Track">
                    <Rectangle.Fill>
                        <LinearGradientBrush EndPoint="1,0">
                            <GradientStop Color="BlanchedAlmond" Offset="0"/>
                            <GradientStop Color="Beige" Offset="1"/>
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
                <Rectangle x:Name="PART_Indicator"
                           Stroke="Blue" StrokeThickness="0.5"
                           HorizontalAlignment="Left">
                    <Rectangle.Fill>
                        <LinearGradientBrush EndPoint="0.2,1" SpreadMethod="Repeat">
                            <GradientStop Offset="0" Color="LightCyan"/>
                            <GradientStop Offset="1" Color="Cyan"/>
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
                <TextBlock Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, StringFormat=\{0\}%}"
                           Foreground="{TemplateBinding Foreground}" FontSize="{TemplateBinding FontSize}" FontWeight="Bold"
                           VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </Grid>
        </ControlTemplate>

        <ControlTemplate TargetType="ProgressBar" x:Key="customProgressBar3">
            <Grid>
                <Ellipse x:Name="PART_Track">
                    <Ellipse.Fill>
                        <LinearGradientBrush EndPoint="1,0">
                            <GradientStop Color="BlanchedAlmond" Offset="0"/>
                            <GradientStop Color="Beige" Offset="1"/>
                        </LinearGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
                <Ellipse x:Name="PART_Indicator"
                           Stroke="Orange" StrokeThickness="3.0"
                           HorizontalAlignment="Left">
                    <Ellipse.Fill>
                        <LinearGradientBrush EndPoint="1,0">
                            <GradientStop Offset="0" Color="LightPink"/>
                            <GradientStop Offset="1" Color="DeepPink"/>
                        </LinearGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
                <TextBlock Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, StringFormat=\{0\}%}"
                           Foreground="{TemplateBinding Foreground}" FontSize="{TemplateBinding FontSize}" FontWeight="Bold"
                           VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </Grid>
        </ControlTemplate>
    </Application.Resources>
</Application>

MainWindow.xamlファイルは以下のとおりです。

<Window x:Class="WpfCustomizeControl.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:WpfCustomizeControl"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>
        <ProgressBar x:Name="progressBar1" Height="30" Margin="20"
                     Foreground="Blue" FontSize="16"
                     Template="{StaticResource customProgressBar1}"/>

        <ProgressBar x:Name="progressBar2" Height="30" Margin="20"
                     Foreground="Black" FontSize="18"
                     Template="{StaticResource customProgressBar2}"/>

        <ProgressBar x:Name="progressBar3" Height="30" Margin="20"
                     Foreground="Black" FontSize="20"
                     Template="{StaticResource customProgressBar3}"/>

        <ProgressBar x:Name="progressBarDefault" Height="30" Margin="20"/>
    </StackPanel>
</Window>

プログレスバーの構成

プログレスバーの構成部品はWPFで決められています。

プログレスバーの定義は以下のとおりです。

[System.Windows.TemplatePart(Name="PART_Track", Type=typeof(System.Windows.FrameworkElement))]
[System.Windows.TemplatePart(Name="PART_Indicator", Type=typeof(System.Windows.FrameworkElement))]
[System.Windows.TemplatePart(Name="PART_GlowRect", Type=typeof(System.Windows.FrameworkElement))]
public class ProgressBar : System.Windows.Controls.Primitives.RangeBase
ProgressBar Class (System.Windows.Controls)
Indicates the progress of an operation.

PART_TrackやPART_Indicatorのデータ型はFrameworkElementとなっているため、この派生クラスであるRectangleやEllipseが使用できます。

TemplateBindingマークアップ拡張

App.xamlの各ControlTemplate要素にはTextBlock要素を配置しています。これはプログレスバーの進み具合をパーセント表示したいためです。

TextBlock要素にはTemplateBindingマークアップ拡張によってForegroundプロパティとFontSizeプロパティがバインドされています。

TemplateBindingマークアップ拡張はテンプレートが施されたコントロール(今回の場合はMainWindow.xamlの上から3つのプログレスバー)で指定されたプロパティにバインドするために使用するものです。

一番上のプログレスバーで言うとForegroundプロパティにBlue、FontSizeプロパティに16が指定されていますが、これらの値がそれぞれTextBlockのForegroundプロパティとFontSizeプロパティの値になります。

詳細は以下をご確認ください。

TemplateBinding のマークアップ拡張機能 - WPF .NET Framework

RelativeSourceマークアップ拡張のTemplatedParentモード

同じくTextBlock要素のTextプロパティには、Bindingマークアップ拡張の中でRelativeSourceマークアップ拡張のTemplatedParentモードが指定されています。

TemplatedParentモードは、あるコントロールテンプレート内から、そのコントロールのプロパティをバインドするために使用します。

今回の場合は、コントロールテンプレートのTargetTypeプロパティにProgressBarが指定されているため、Bindingマークアップ拡張のValueプロパティはProgressBarのValueプロパティを指しています。

プログレスバーのカスタマイズ(コードビハインド)

コードビハインドは以下のとおりです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace WpfCustomizeControl
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(200);
            timer.Tick += OnTimerTick;
            timer.Start();
        }

        private DispatcherTimer timer = null;

        private double d = 0.0;

        private void OnTimerTick(object sender, EventArgs e)
        {
            if(d <= progressBar1.Maximum)
            {
                progressBar1.Value = d;
            }

            if (d <= progressBar2.Maximum)
            {
                progressBar2.Value = d;
            }

            if (d <= progressBar3.Maximum)
            {
                progressBar3.Value = d;
            }

            if (d <= progressBarDefault.Maximum)
            {
                progressBarDefault.Value = d;
            }
            d++;
        }
    }
}

タイマーを使って疑似的にプログレスバーを進めています。

今回はプログレスバーの外観をカスタマイズする方法について確認しました。

以上です。

コメント