読者です 読者をやめる 読者になる 読者になる

SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

XAMLでズルいデザイン~その4・ズルいグラデーション・配色~

今度はグラデーションと配色についてです。

f:id:minami_SC:20140309233415p:plain:w300

ズルいグラデーション

ズルいグラデーションと説明されている、うっすらとしたグラデーションを作成するマークアップ拡張を作りました。
コードは以下の通り。

    [MarkupExtensionReturnType(typeof(LinearGradientBrush))]
    public class ZuruiGradientBrushExtension : MarkupExtension
    {
        public ZuruiGradientBrushExtension()
            : this(Colors.Transparent, 0.95f, 90)
        {
        }

        public ZuruiGradientBrushExtension(Color baseColor, float diff, double angle)
        {
            BaseColor = baseColor;
            Diff = diff;
            Angle = angle;
        }

        public Color BaseColor { get; set; }
        public float Diff { get; set; }
        public double Angle { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var grad = BaseColor.ToHSV();
            grad.V *= Diff;
            return new LinearGradientBrush(BaseColor, grad.ToRGB(), Angle);
        }
    }

グラデーションを作るときに、基準色から明度を変化させた色を作成します。
その際に、RGBではなく、HSVで色を扱いたかったので、以前↓の記事で書いたHSVColorクラスを使っています。
RGB⇔HSVの変換をするクラス - SourceChord

使い方
プロパティ 説明
BaseColor グラデーションの基準となる色
Diff グラデーションで明るさを変化させる量
Angle グラデーションの角度

↓こんな風にグラデーションを作れます。

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Rectangle Width="100"
                   Height="50"
                   Fill="{local:ZuruiGradientBrush BaseColor=Red,
                                                   Diff=0.8}" />
        <Rectangle Grid.Column="1"
                   Width="100"
                   Height="50"
                   Fill="{local:ZuruiGradientBrush BaseColor=DarkBlue,
                                                   Diff=1.8}" />
    </Grid>

f:id:minami_SC:20140309233346p:plain


ズルい配色

色相を変化させた色を設定するマークアップ拡張を作ってみました。

    [MarkupExtensionReturnType(typeof(Color))]
    public class AdjustHueExtension : MarkupExtension
    {
        public AdjustHueExtension()
            : this(Colors.Transparent, 0)
        {
        }

        public AdjustHueExtension(Color baseColor, float hueDiff)
        {
            BaseColor = baseColor;
            HueDiff = hueDiff;
        }

        public Color BaseColor { get; set; }
        public float HueDiff { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var adjust = BaseColor.ToHSV();
            adjust.H += HueDiff;
            return adjust.ToRGB();
        }
    }

このマークアップ拡張を使うと、以下のように指定した色から指定した分だけ色相を変化させた色を作成できます。

使い方
プロパティ 説明
BaseColor 基準となる色
HueDiff 色相を変化させる量
    <Window.Resources>
        <SolidColorBrush x:Key="MainColorBrush" Color="Red" />
        <SolidColorBrush x:Key="AccentColorBrush" Color="{local:AdjustHue BaseColor=Red, HueDiff=200}" />
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Rectangle Width="100"
                   Height="50"
                   Fill="{StaticResource MainColorBrush}" />
        <Rectangle Grid.Column="1"
                   Width="100"
                   Height="50"
                   Fill="{StaticResource AccentColorBrush}" />
    </Grid>

f:id:minami_SC:20140309233359p:plain

配色とグラデーションを組み合わせて使う

この二つを組み合わせて、以下のようなことをしようとすると問題にブチ当たります。。。
Resourceに定義したColorを使って、自作のマークアップ拡張でグラデーションを作ると以下のようなエラーが出ます。

    <Window.Resources>
        <Color x:Key="MainColor">#FFC13B23</Color>
        <SolidColorBrush x:Key="MainColorBrush" Color="{StaticResource MainColor}" />
        <SolidColorBrush x:Key="AccentColorBrush" Color="{local:AdjustHue BaseColor={StaticResource MainColor}, HueDiff=200}" />
    </Window.Resources>
Markup Extension の解析時に、型 'MS.Internal.Markup.MarkupExtensionParser+UnknownMarkupExtension' に対する不明なプロパティ 'BaseColor' が見つかりました。 

IDEのデザイナ画面は正しく表示されるのに、ビルドができない。。。

で、色々調べてたら、以下の記事を見つけました。
【WPF】自作したマークアップからプロパティへアクセスできない。 | 創造的プログラミングと粘土細工

どうやら、マークアップ拡張を別アセンブリで定義すればよいらしい。
てことで、自分で作ったマークアップ拡張内で、StaticResourceとかを使いたい場合は、マークアップ拡張を、別Dllのクラスライブラリなどといった形で定義しておく必要があるようです。


使ってみた

今回作ったマークアップ拡張とかを使って、適当に画面を作ってみました。

<Window x:Class="WpfBaseTemplate1.Window4"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:zurui="clr-namespace:ZuruiDesign;assembly=ZuruiDesign"
        Title="Window4"
        Width="600"
        Height="400"
        Background="{zurui:TiledImageBrush Source=Image/retina_wood.png}">
    <Window.Resources>
        <Color x:Key="MainColor">#FFC13B23</Color>
        <Color x:Key="AccentColor">#FF2274C1</Color>
        <Color x:Key="BaseColor">#FFFFFFFF</Color>
        <Color x:Key="TextColor">#FF222222</Color>
        <SolidColorBrush x:Key="MainColorBrush" Color="{StaticResource MainColor}" />
        <SolidColorBrush x:Key="TextColorBrush" Color="{StaticResource TextColor}" />
        <Style x:Key="ZuruiBlackTextBlockStyle1" TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground" Value="#FF000000" />
            <Setter Property="Effect">
                <Setter.Value>
                    <DropShadowEffect BlurRadius="0"
                                      ShadowDepth="1"
                                      Color="#FFFFFFFF" />
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Rectangle Fill="{zurui:ZuruiGradientBrush BaseColor={StaticResource MainColor}, Diff=0.9}" />
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       FontSize="46"
                       Foreground="{StaticResource MainColorBrush}"
                       Style="{StaticResource ZuruiBlackTextBlockStyle1}"
                       Text="TextBlock"
                       TextWrapping="Wrap" />
            <Button Grid.Column="1"
                    Width="80"
                    Height="30"
                    Margin="20"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Center"
                    Background="{zurui:ZuruiGradientBrush BaseColor={StaticResource AccentColor},
                                                          Diff=0.9}"
                    Content="Button"
                    Foreground="White" />
        </Grid>
        <Rectangle Grid.Row="2" Fill="{zurui:ZuruiGradientBrush BaseColor={StaticResource BaseColor}, Angle=90}" />
        <TextBlock Grid.Row="2"
                   Margin="20"
                   Foreground="{StaticResource TextColorBrush}"
                   TextAlignment="Right"
                   TextWrapping="Wrap">
            ズルいデザイン
            <LineBreak />
            abccefg
            <LineBreak />
            123
            <LineBreak />
        </TextBlock>
    </Grid>
</Window>

f:id:minami_SC:20140309233415p:plain:w400



以上のようなマークアップ拡張を作ってみましたが、マークアップ拡張だと、リソースとしてBrushやColorを作るのに向かないですね。
で、こういうテーマカラーとなる配色や、グラデーションとかはリソースに定義して使いまわすことが多いと思うので、わざわざマークアップ拡張で作るようなものではなかったかも。。。