none
ベクターアイコンの管理方法と、表示の問題について RRS feed

  • 質問

  • 現在VS2013 C# でWPFアプリケーションを作成しています。

    アプリケーション内で使用するアイコンをベクターデータで扱おうと思うのですが、どのように扱うのが正しいのかがいまいちわかりません。

    現在はResourceDictionaryにDrawingImageを利用してパスデータを記入し、表示する場合にはImageのSourceに

    Source="{StaticResource ResourceKey=(DrawingImageで指定したキー)}"のようにしてアイコンを表示しています。

    上記の方法で一応表示はできているのですが、いくつか問題と疑問があります。

    1.VSでアイコンを確認できない。

    2.アプリケーションで表示時に、長方形のベクターアイコンが自動で正方形にされて表示されてしまう。(切り取られている)

    3.そもそもベクターアイコンの管理方法はあっているのか。

    どこか参考になるページや、皆様が行っている管理方法をご教示いただけますでしょうか。

    宜しくお願いいたします。

    2015年12月16日 10:07

回答

  • 「パスデータ」というのは何でしょうか? パスのマークアップ構文で使われるミニ言語によって記述されたデータですか?

    自分はResourceDictionaryにStreamGeometryのリソースを作成し、Path要素のDataプロパティにStaticResource経由で指定するようにしています。

    方法 : StreamGeometry を使用して図形を作成する

    最近は単色のModernスタイルアイコンのみを作成&使用するようにしているので、PathのFillプロパティをバインディングによってButtonなどの上位要素の状態に連動させることで、Enabled/Disabledに応じてアイコンの色を変えるようにしています。アイコンを多色成型したい場合は様々なアプローチがあると思いますが。

    Image系は主にラスター画像をホストするための要素のはずなので、特にベクトルデータ用に使うメリットはないかと。

    • 回答としてマーク れいじ 2015年12月21日 1:35
    2015年12月16日 13:50
  • 試してみた

    >1
    表示しますが…ウィンドウのアイコンですか?
    どうにも表示しないのであれば、作っている間は適当なコントロールを置いて仮表示させるとか。Blendで作ってデータだけコピペするとか。

    >2
    初期状態のImageに設定しても長方形を維持してます。
    ImageのStretchかStretchDirectionが変更されていたりしませんか?
    あるいはImageより外で正方形に制限しているとか。

    <!-- Dictionary1.xaml-->
    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StreamGeometry x:Key="triangle" >M2,0 L2,20 30,10z</StreamGeometry>
        <StreamGeometry x:Key="square" >M0,0 L0,20 20,20 20,0z</StreamGeometry>
        <EllipseGeometry x:Key="ellipse" RadiusX="5" RadiusY="5" Center="10,10" />
        <CombinedGeometry x:Key="comb1" Geometry1="{StaticResource  triangle}" Geometry2="{StaticResource ellipse}" GeometryCombineMode="Xor" />
        <CombinedGeometry x:Key="comb2" Geometry1="{StaticResource square}" Geometry2="{StaticResource comb1}" GeometryCombineMode="Xor"  />
    
        <Path x:Key="path1" Data="{StaticResource comb2}" Stroke="Blue" StrokeThickness="1"  />
    
        <DrawingImage x:Key="draw1"  >
            <DrawingImage.Drawing>
                <DrawingGroup>
                    <GeometryDrawing Brush="LightSkyBlue" Geometry="{StaticResource comb1}" >
                        <GeometryDrawing.Pen>
                            <Pen Brush="Green" />
                        </GeometryDrawing.Pen>
                    </GeometryDrawing>
                    <GeometryDrawing Brush="{x:Null}" Geometry="{StaticResource square}" >
                        <GeometryDrawing.Pen>
                            <Pen Brush="Red" />
                        </GeometryDrawing.Pen>
                    </GeometryDrawing>
                </DrawingGroup>
            </DrawingImage.Drawing>
        </DrawingImage>
    </ResourceDictionary>
    
    <!-- App.xaml -->
    <Application x:Class="WpfApplication1.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 StartupUri="MainWindow.xaml">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Dictionary1.xaml" />
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    
    <!-- MainWindow.xaml-->
    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="400" Width="400"
            Icon="{StaticResource draw1}">
        <StackPanel>
            <StackPanel.LayoutTransform>
                <ScaleTransform ScaleX="3" ScaleY="3" />
            </StackPanel.LayoutTransform>
            <WrapPanel>
                <Button Margin="2"><Path Data="{StaticResource triangle}" Stroke="Red" StrokeThickness="1" StrokeLineJoin="Round" StrokeDashArray="2,1" /></Button>
                <Button Margin="2"><Path Data="{StaticResource comb1}" Stroke="Red" StrokeThickness="1" Fill="Black"/></Button>
                <Button Margin="2"><Path Data="{StaticResource comb2}" Stroke="Red" StrokeThickness="1" Fill="Black" /></Button>
                <Button Margin="2" Content="{StaticResource path1}" />
                <Button Margin="2" ><Image Source="{StaticResource draw1}" Width="16" Height="16" /></Button> 
                <Button Margin="2" ><Image Source="{StaticResource draw1}" Width="16" Height="16" StretchDirection="UpOnly" /></Button>
                <Button Margin="2" ><Image Source="{StaticResource draw1}" Width="16" Height="16" Stretch="None" /></Button>
                <Button Margin="2" >
                    <Grid Width="16" Height="16" >
                        <Image Source="{StaticResource draw1}" Stretch="None" />
                    </Grid>
                </Button>
                <Button Margin="2" >
                    <Grid Width="16" Height="16" >
                        <Viewbox>
                            <Image Source="{StaticResource draw1}" Stretch="None" />
                        </Viewbox>
                    </Grid>
                </Button>
            </WrapPanel>
        </StackPanel>
    </Window>
    >3
    私はほぼ単色Pathでアイコン作ってそのままContentControlに入れてるだけなので、複数の色を使うアイコンの場合は判らないです。

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2015年12月16日 13:55
    • 回答の候補に設定 星 睦美 2015年12月18日 4:03
    • 回答としてマーク れいじ 2015年12月21日 1:35
    2015年12月16日 13:54

すべての返信

  • 「パスデータ」というのは何でしょうか? パスのマークアップ構文で使われるミニ言語によって記述されたデータですか?

    自分はResourceDictionaryにStreamGeometryのリソースを作成し、Path要素のDataプロパティにStaticResource経由で指定するようにしています。

    方法 : StreamGeometry を使用して図形を作成する

    最近は単色のModernスタイルアイコンのみを作成&使用するようにしているので、PathのFillプロパティをバインディングによってButtonなどの上位要素の状態に連動させることで、Enabled/Disabledに応じてアイコンの色を変えるようにしています。アイコンを多色成型したい場合は様々なアプローチがあると思いますが。

    Image系は主にラスター画像をホストするための要素のはずなので、特にベクトルデータ用に使うメリットはないかと。

    • 回答としてマーク れいじ 2015年12月21日 1:35
    2015年12月16日 13:50
  • 試してみた

    >1
    表示しますが…ウィンドウのアイコンですか?
    どうにも表示しないのであれば、作っている間は適当なコントロールを置いて仮表示させるとか。Blendで作ってデータだけコピペするとか。

    >2
    初期状態のImageに設定しても長方形を維持してます。
    ImageのStretchかStretchDirectionが変更されていたりしませんか?
    あるいはImageより外で正方形に制限しているとか。

    <!-- Dictionary1.xaml-->
    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StreamGeometry x:Key="triangle" >M2,0 L2,20 30,10z</StreamGeometry>
        <StreamGeometry x:Key="square" >M0,0 L0,20 20,20 20,0z</StreamGeometry>
        <EllipseGeometry x:Key="ellipse" RadiusX="5" RadiusY="5" Center="10,10" />
        <CombinedGeometry x:Key="comb1" Geometry1="{StaticResource  triangle}" Geometry2="{StaticResource ellipse}" GeometryCombineMode="Xor" />
        <CombinedGeometry x:Key="comb2" Geometry1="{StaticResource square}" Geometry2="{StaticResource comb1}" GeometryCombineMode="Xor"  />
    
        <Path x:Key="path1" Data="{StaticResource comb2}" Stroke="Blue" StrokeThickness="1"  />
    
        <DrawingImage x:Key="draw1"  >
            <DrawingImage.Drawing>
                <DrawingGroup>
                    <GeometryDrawing Brush="LightSkyBlue" Geometry="{StaticResource comb1}" >
                        <GeometryDrawing.Pen>
                            <Pen Brush="Green" />
                        </GeometryDrawing.Pen>
                    </GeometryDrawing>
                    <GeometryDrawing Brush="{x:Null}" Geometry="{StaticResource square}" >
                        <GeometryDrawing.Pen>
                            <Pen Brush="Red" />
                        </GeometryDrawing.Pen>
                    </GeometryDrawing>
                </DrawingGroup>
            </DrawingImage.Drawing>
        </DrawingImage>
    </ResourceDictionary>
    
    <!-- App.xaml -->
    <Application x:Class="WpfApplication1.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 StartupUri="MainWindow.xaml">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Dictionary1.xaml" />
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    
    <!-- MainWindow.xaml-->
    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="400" Width="400"
            Icon="{StaticResource draw1}">
        <StackPanel>
            <StackPanel.LayoutTransform>
                <ScaleTransform ScaleX="3" ScaleY="3" />
            </StackPanel.LayoutTransform>
            <WrapPanel>
                <Button Margin="2"><Path Data="{StaticResource triangle}" Stroke="Red" StrokeThickness="1" StrokeLineJoin="Round" StrokeDashArray="2,1" /></Button>
                <Button Margin="2"><Path Data="{StaticResource comb1}" Stroke="Red" StrokeThickness="1" Fill="Black"/></Button>
                <Button Margin="2"><Path Data="{StaticResource comb2}" Stroke="Red" StrokeThickness="1" Fill="Black" /></Button>
                <Button Margin="2" Content="{StaticResource path1}" />
                <Button Margin="2" ><Image Source="{StaticResource draw1}" Width="16" Height="16" /></Button> 
                <Button Margin="2" ><Image Source="{StaticResource draw1}" Width="16" Height="16" StretchDirection="UpOnly" /></Button>
                <Button Margin="2" ><Image Source="{StaticResource draw1}" Width="16" Height="16" Stretch="None" /></Button>
                <Button Margin="2" >
                    <Grid Width="16" Height="16" >
                        <Image Source="{StaticResource draw1}" Stretch="None" />
                    </Grid>
                </Button>
                <Button Margin="2" >
                    <Grid Width="16" Height="16" >
                        <Viewbox>
                            <Image Source="{StaticResource draw1}" Stretch="None" />
                        </Viewbox>
                    </Grid>
                </Button>
            </WrapPanel>
        </StackPanel>
    </Window>
    >3
    私はほぼ単色Pathでアイコン作ってそのままContentControlに入れてるだけなので、複数の色を使うアイコンの場合は判らないです。

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2015年12月16日 13:55
    • 回答の候補に設定 星 睦美 2015年12月18日 4:03
    • 回答としてマーク れいじ 2015年12月21日 1:35
    2015年12月16日 13:54
  • >syghさん

    回答ありがとうございます。

    また、返信が遅れて申し訳ありません。

    >「パスデータ」というのは何でしょうか? パスのマークアップ構文で使われるミニ言語によって記述されたデータですか?

    はい。その通りです。しっかりと理解していないため、伝え方が悪くなってしまいました。

    >最近は単色のModernスタイルアイコンのみを作成&使用するようにしているので、PathのFillプロパティをバインディングによってButtonなどの上位要素の状態に連動させることで、Enabled/Disabledに応じてアイコンの色を変えるようにしています。アイコンを多色成型したい場合は様々なアプローチがあると思いますが。

    上記、参考になるようなページをご存じないでしょうか?

    2015年12月18日 5:47
  • >gekkaさん

    回答、サンプルの提示ありがとうございます。

    また、返信が遅れて申し訳ありません。

    gekkaさんのサンプルで<Button>内に<Path>で描画ができることを初めて知りました。

    1についてですが、VSで画像を編集するのと同じようにResourceDictionary内の<StreamGeometry>内のデータ(画像)を

    表示できないのかと思ったのですが、無理そうな気がしてきました。

    <StreamGeometry>内の数字の羅列は座標?的なもので、何色・線の太さ等は<Path>内でのプロパティで指定する認識であってますでしょうか?

    3についてですが、複数アイコン存在した場合は1つのファイルにまとめて記述するのが一般的なのでしょうか?

    アイコンが大量に存在する場合は、管理面も考えると複数のファイルに分割して扱ったほうがいいでしょうか?

    ※追記です。

    長方形の表示ができない件ですが、自身のResourceDictionaryを見なおしてみると『ClipGeometry』の記述があり、これが原因だったようです。

    • 編集済み れいじ 2015年12月18日 10:18
    2015年12月18日 6:17
  • まず自分で試してから人に聞くようにしてください。Enabled/Disabled状態への連動制御はただのデータバインディングです。WPFのデータバインディングはそれこそWeb検索すればいくらでも情報が手に入ります。まずは自分で限界まで調査・チャレンジして、どうしても分からないときだけ人に聞くようにしないと、いざというときに応用の効かない中途半端な知識しか身につきません。
    • 編集済み sygh 2015年12月18日 9:23
    2015年12月18日 9:17
  • >syghさん

    回答ありがとうございます。

    仰るとおりですね。申し訳ありません。

    Enabled/Disabledの制御はIsEnabled="{Binding Path=IsEnabledButton}"のような記述をXamlにすることで可能だとはわかったのですが、『PathのFillプロパティをバインディングによってButtonなどの上位要素の状態に連動させることで』の意味が理解できなかったため質問してしまいました。

    2015年12月18日 9:52
  • 慣れていない方には確かにちょっと説明が分かりづらかったですね。PathのFillプロパティを連動させるというのは、具体的なコードだとこういう感じです。

    <StackPanel Orientation="Horizontal">
        <StackPanel.Resources>
            <Style TargetType="Button">
                <Style.Triggers>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
            <StreamGeometry x:Key="MyPlayButtonIconKey">M 20,10 L 2,0 L 2,20</StreamGeometry>
            <StreamGeometry x:Key="MyPauseButtonIconKey">M 1,1 L 8,1 L 8,19 L 1,19 M 12,1 L 19,1 L 19,19 L 12,19</StreamGeometry>
        </StackPanel.Resources>
        <CheckBox Name="check1" VerticalAlignment="Center" Content="ボタン群を有効にする"/>
        <Separator Visibility="Hidden" Width="10"/>
        <Button IsEnabled="{Binding ElementName=check1, Path=IsChecked}" Padding="4">
            <DockPanel>
                <Path Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Data="{StaticResource MyPlayButtonIconKey}" VerticalAlignment="Center"/>
                <Label Content="再生"/>
            </DockPanel>
        </Button>
        <Separator Visibility="Hidden" Width="10"/>
        <Button IsEnabled="{Binding ElementName=check1, Path=IsChecked}" Padding="4">
            <DockPanel>
                <Path Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Data="{StaticResource MyPauseButtonIconKey}" VerticalAlignment="Center"/>
                <Label Content="一時停止"/>
            </DockPanel>
        </Button>
        <Separator Visibility="Hidden" Width="10"/>
        <Button IsEnabled="{Binding ElementName=check1, Path=IsChecked}" Padding="4">
            <DockPanel>
                <Rectangle Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Width="18" Height="18" VerticalAlignment="Center"/>
                <Label Content="停止"/>
            </DockPanel>
        </Button>
    </StackPanel>
    
    StyleやStreamGeometryをStackPanel.Resourcesに含めていますが、これは説明を簡単にするためであり、特に深い意味や理由はありません。また、上記の連動方法やベクトルベースアイコンの作成方法はあくまで一例にすぎず、他にも様々な方法があるはずです。あとは自分で模索してみてください。
    2015年12月18日 12:41
  • >syghさん

    回答ありがとうございます。

    サンプル、丁寧な説明もありがとうございます。

    参考になりました。

    2015年12月21日 1:35