プログラムの事とか

お約束ですが「掲載内容は私個人の見解です」

WPFでShapeをResourceにして使いまわす

WPFに限らず今時のアプリではアプリ内で使うアイコン等をベクトルデータで持っていることが多いと思います

IllustratorSVGXAML

って感じでアイコンデータがやってきたりすると思いますが、これをそのままContentに張り付けるのは悪手ですよね(面倒な時を除く)

ということでアイコンデータはResourceDictionaryに入れて、使うところはKeyを指定するようにしましょう

Resource(App.xaml)

<Application.Resources>
    <Canvas x:Key="Icon1" x:Shared="False" Width="100" Height="100">
        <Ellipse Width="100" Height="100" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}}}"/>
    </Canvas>
    <Canvas x:Key="Icon2" x:Shared="False" Width="100" Height="100">
        <Rectangle Width="100" Height="100" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}}}"/>
    </Canvas>
</Application.Resources>

MainWindow.xaml

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Button Grid.Row="0" Grid.Column="0" Content="{StaticResource Icon1}"/>
    <Button Grid.Row="0" Grid.Column="1" Content="{StaticResource Icon1}" Foreground="Blue"/>
    <Button Grid.Row="1" Grid.Column="0" Content="{StaticResource Icon2}"/>
    <Button Grid.Row="1" Grid.Column="1" Content="{StaticResource Icon2}" Foreground="Blue"/>
</Grid>

結果

f:id:puni-o:20161114160551p:plain

ポイント

Shape.Fill

Shape.Fillの指定ですが、ContentControlのForegroundを参照させたい時(Button.Foregroundとか)は上のような感じにしないと参照してくれません

TextBlockとかは何も指定しなければ親のForegroundを見てくれるのでそのつもりでいると???ってなります

上の場合ButtonBaseを継承しているコントロール内でしか効果ありませんが、まぁそれ以外の状況で色を適宜変えたいとかないよね、ね

ということでMainWindow.xamlのGrid.Column="1"にあるボタンではForegroundの指定色にShapeが変更されていることがわかります

x:Shared="False"

ResourceDictionaryに定義しているリソースはデフォルトで使いまわすようになっています(Brushとかそういうのは使いまわした方が効率いいですからね)

ですがCanvasは使いまわしちゃいけないですよね

x:Sharedを書かないとどこか一つにしか出てこなくなります

x:Shared="False"とすればそれぞれにインスタンスを作ってくれます

x:Shared Attribute

これでかつる!WPFつおい


2016/11/18 追記

Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}}}"

より

Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}"

の方がよさそうですね (ButtonBaseをContentControlに変更)