先日、Windows のタスクバーに小さいスタートメニューみたいなサブメニューを追加するランチャーアプリケーションを WPF で作りまして。
で、作っている最中に、そのものズバリの日本語情報がネット検索で「簡単に」見つからなかったケースがいくつかありましたので、それらを4~5回に渡って記事化して放流しておこうと思います。
ということで、今回はスライダーのカスタマイズです。
読み込み中です。少々お待ち下さい
まえおき
スライダーのデザインを、レトロなデフォルトスタイルから、今風のものに変えたい場合も多いかと思います。
WPF なら、かなり自由に外観を変えられますので、その方法についてサンプルコードで見ていきましょう。
サンプルコード
以下のコードは、Visual Studio (2015) の「ファイル」メニュー>「新規作成」>「WPF アプリケーション」から「WpfCustomSlider」という名前で新しいプロジェクトを作成し、自動的に生成される MainWindow に上書きすれば、そのまま動きます。
今回は「MainWindow.xaml.cs」の方は一切いじっていないので、「MainWindow.xaml」だけ貼り付けておきます。
あまり凝るとパッと見で分かり難くなってしまうので、あくまでサンプルとして、できるだけ簡潔に記述しています。
そのため、見た目がやや地味ですが、各パーツ毎に色を個別に指定したり、つまみに画像を使ったりと、後はお好きなようにカスタマイズしていただければ。
MainWindow.xaml
<Window x:Class="WpfCustomSlider.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:WpfCustomSlider"
        mc:Ignorable="d"
        Title="MainWindow" Height="247" Width="494">
    <Window.Resources>
        <SolidColorBrush x:Key="CyanBrush" Color="#00BCD4" />
        <SolidColorBrush x:Key="PinkBrush" Color="#DB167C" />
        <SolidColorBrush x:Key="LimeBrush" Color="#0BE182" />
        <!-- つまみのデザイン。ここでは円(Ellipse)を描いていますが、他の図形や画像など、お好きにデザインしてください -->
        <Style x:Key="SliderThumbStyle" TargetType="{x:Type Thumb}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Thumb}">
                        <Ellipse Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Slider}, Mode=FindAncestor}}" Width="25" Height="25"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!-- このサンプルでは、簡潔に記述する為に全て Foreground を基準に色を決定しています。個別に指定する場合は、適宜書き換えてください -->
        <Style x:Key="CustomSliderStyle" TargetType="{x:Type Slider}">
            <Style.Triggers>
                <Trigger Property="Orientation" Value="Horizontal">
                    <!-- 横置きの場合 -->
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate>
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="25" />
                                        <RowDefinition Height="Auto" />
                                    </Grid.RowDefinitions>
                                    <!-- 上の目盛り -->
                                    <TickBar Grid.Row="0" x:Name="TopTick" Placement="Top" Visibility="Collapsed" Fill="{TemplateBinding Foreground}" Height="8" Opacity="0.64" Margin="0,0,0,2" />
                                    <!-- 下の目盛り -->
                                    <TickBar Grid.Row="2" x:Name="BottomTick" Placement="Bottom" Visibility="Collapsed" Fill="{TemplateBinding Foreground}" Height="8" Opacity="0.64" Margin="0,2,0,0" />
                                    <Track Grid.Row="1" x:Name="PART_Track">
                                        <!-- 減少側のトラック(レール) -->
                                        <Track.DecreaseRepeatButton>
                                            <RepeatButton Command="Slider.DecreaseLarge" Background="{TemplateBinding Foreground}" Height="5" BorderBrush="{x:Null}" Opacity="0.2"/>
                                        </Track.DecreaseRepeatButton>
                                        <!-- 増加側のトラック(レール) -->
                                        <Track.IncreaseRepeatButton>
                                            <RepeatButton Command="Slider.IncreaseLarge" Background="{TemplateBinding Foreground}" Height="5" BorderBrush="{x:Null}" Opacity="0.5"/>
                                        </Track.IncreaseRepeatButton>
                                        <!-- つまみ -->
                                        <Track.Thumb>
                                            <Thumb Style="{StaticResource SliderThumbStyle}"/>
                                        </Track.Thumb>
                                    </Track>
                                </Grid>
                                <!-- TickPlacement の設定によって目盛りの表示を切り替え -->
                                <ControlTemplate.Triggers>
                                    <Trigger Property="Slider.TickPlacement" Value="TopLeft">
                                        <Setter TargetName="TopTick" Property="Visibility" Value="Visible" />
                                    </Trigger>
                                    <Trigger Property="Slider.TickPlacement" Value="BottomRight">
                                        <Setter TargetName="BottomTick" Property="Visibility" Value="Visible" />
                                    </Trigger>
                                    <Trigger Property="Slider.TickPlacement" Value="Both">
                                        <Setter TargetName="TopTick" Property="Visibility" Value="Visible" />
                                        <Setter TargetName="BottomTick" Property="Visibility" Value="Visible" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
                <Trigger Property="Orientation" Value="Vertical">
                    <!-- 縦置きの場合 -->
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate>
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="25" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <!-- 左の目盛り -->
                                    <TickBar Grid.Column="0" x:Name="LeftTick" Placement="Left" Visibility="Collapsed" Fill="{TemplateBinding Foreground}" Width="8" Opacity="0.64" Margin="0,0,2,0" />
                                    <!-- 右の目盛り -->
                                    <TickBar Grid.Column="2" x:Name="RightTick" Placement="Right" Visibility="Collapsed" Fill="{TemplateBinding Foreground}" Width="8" Opacity="0.64" Margin="2,0,0,0" />
                                    <Track Grid.Column="1" x:Name="PART_Track" IsDirectionReversed="true">
                                        <!-- 減少側のトラック(レール) -->
                                        <Track.DecreaseRepeatButton>
                                            <RepeatButton Command="Slider.DecreaseLarge" Background="{TemplateBinding Foreground}" Width="5" BorderBrush="{x:Null}" Opacity="0.2"/>
                                        </Track.DecreaseRepeatButton>
                                        <!-- 増加側のトラック(レール) -->
                                        <Track.IncreaseRepeatButton>
                                            <RepeatButton Command="Slider.IncreaseLarge" Background="{TemplateBinding Foreground}" Width="5" BorderBrush="{x:Null}" Opacity="0.5"/>
                                        </Track.IncreaseRepeatButton>
                                        <!-- つまみ -->
                                        <Track.Thumb>
                                            <Thumb Style="{StaticResource SliderThumbStyle}"/>
                                        </Track.Thumb>
                                    </Track>
                                </Grid>
                                <!-- TickPlacement の設定によって目盛りの表示を切り替え -->
                                <ControlTemplate.Triggers>
                                    <Trigger Property="Slider.TickPlacement" Value="TopLeft">
                                        <Setter TargetName="LeftTick" Property="Visibility" Value="Visible" />
                                    </Trigger>
                                    <Trigger Property="Slider.TickPlacement" Value="BottomRight">
                                        <Setter TargetName="RightTick" Property="Visibility" Value="Visible" />
                                    </Trigger>
                                    <Trigger Property="Slider.TickPlacement" Value="Both">
                                        <Setter TargetName="LeftTick" Property="Visibility" Value="Visible" />
                                        <Setter TargetName="RightTick" Property="Visibility" Value="Visible" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="40"/>
        </Grid.ColumnDefinitions>
        <Grid Grid.Column="0">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <WrapPanel Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">
                <TextBlock TextWrapping="Wrap" Text="通常のスライダー" FontSize="14" Width="100" VerticalAlignment="Center"/>
                <Slider x:Name="NormalSlider" Width="240" Margin="10,0" Maximum="100" SmallChange="1" LargeChange="10" TickPlacement="TopLeft" TickFrequency="10"/>
                <TextBox x:Name="textBox_NormalSlider" Height="23" Width="50" Text="{Binding Value, ElementName=NormalSlider}" HorizontalContentAlignment="Center"/>
            </WrapPanel>
            <WrapPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
                <TextBlock TextWrapping="Wrap" Text="カスタムスライダー" FontSize="14" Width="100" VerticalAlignment="Center" Foreground="{DynamicResource CyanBrush}"/>
                <Slider x:Name="CustomSlider" Width="240" Margin="10,0" Maximum="100" SmallChange="1" LargeChange="10" Style="{StaticResource CustomSliderStyle}" Foreground="{DynamicResource CyanBrush}"/>
                <TextBox x:Name="textBox_CumstomSlider" Height="23" Width="50" Text="{Binding Value, ElementName=CustomSlider}" HorizontalContentAlignment="Center" Foreground="{DynamicResource CyanBrush}" BorderBrush="{DynamicResource CyanBrush}"/>
            </WrapPanel>
            <WrapPanel Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center">
                <TextBlock TextWrapping="Wrap" Text="目盛りアリ" FontSize="14" Width="100" VerticalAlignment="Center" Foreground="{DynamicResource PinkBrush}"/>
                <Slider x:Name="CustomSliderWithTick" Width="240" Margin="10,0" Maximum="100" SmallChange="1" LargeChange="10" TickPlacement="BottomRight" TickFrequency="10" Style="{StaticResource CustomSliderStyle}" Foreground="{DynamicResource PinkBrush}"/>
                <TextBox x:Name="textBox_CumstomSliderWithTick" Height="23" Width="50" Text="{Binding Value, ElementName=CustomSliderWithTick}" HorizontalContentAlignment="Center" Foreground="{DynamicResource PinkBrush}" BorderBrush="{DynamicResource PinkBrush}"/>
            </WrapPanel>
        </Grid>
        <Grid Grid.Column="1">
            <Slider x:Name="CustomSliderVertical" Height="Auto" Margin="0,10" Maximum="100" SmallChange="1" LargeChange="10" TickPlacement="BottomRight" TickFrequency="10" Style="{StaticResource CustomSliderStyle}" Foreground="{DynamicResource LimeBrush}" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Value}" Orientation="Vertical" HorizontalAlignment="Right"/>
        </Grid>
    </Grid>
</Window>
※横置きでも縦置きでも、どちらでも対応できるように修正しました(2016/06/22)
気になる点
さて、実行すると直ぐに気がつくのですが、上のコードだとスライダーをドラッグで動かした時に、テキストボックスの値が小数になってしまいます。
Slider の Value が double なので当たり前といえば当たり前なのですが、これを常に整数に保ちたい場合も多いですよね。
今回は焦点をデザインに絞っていますので、そちらについては次回を参照して下さい。


    
    
    
    ステレオの左右等、チャンネル毎のボリュームをお手軽に調節できるWindowsアプリを作ってみました
    
    
    
    Windowsタスクバーにアイコンをまとめて登録できるメニューアプリを作ってみました
    
    
    
    数ヶ月越しで我が家にお越しいただいた Alexa と戯れる(Amazon Echo Dot の感想)
    
    
    
    コルタナさん、もう少し静かに喋っていただけますか(アプリ毎にボリュームを設定する方法)
    
    
    
    iPhone X で手軽にホーム画面の1枚目に戻ったりアプリの削除画面を完了する方法
    
    
    
    iPhone X の Face ID が全然認識しないと思ったら、まるきり自分のせいだった話
    
    
    
    macOS High Sierra にアップグレードしたら、CocoaPods が動かなくなった
    
    
    
    Swift 4 で substring 的にインデックスを指定して部分文字列を取り出す方法
    
    
    
    iOS 11以降は、Apple IDの2ステップ確認には対応せず、2ファクタ認証に一本化される模様