2010年3月14日日曜日

第25回 選択アイテムのスタイルを変更する

WPFでListBoxを使うと、デフォルトの配色は選択アイテムが青くなると思います。
(Windowsの画面の配色設定次第で変わるかもしれません)
今作っているクライアントも選択(クリック)したつぶやきは背景が青くなりますね。
これは結構見づらいと思うので、選択アイテムは枠に影が付くように変更したいと思います。

TimelineView.xamlだけをいじります。

正直に言うと、前回まで
DataTemplateとControlTemplateの違いがよくわかっていませんでした。
今でも正しく理解できたかどうか怪しいので、
以降の説明ももしかしたら違っているかもしれないと思いながら読んでください。

なので、最初に私の理解を説明します。
間違っていることに気づいた人は指摘してください。

まずControlTemplateというのはControlの見た目などをカスタマイズするための
テンプレートですね。そのまま。
Controlはボタンとかリストボックスとか、見た目を提供するとともに
Clickイベントなどの制御(=Control)も提供する、あるまとまった単位の部品。
Controlとしては、ほとんどの場合、.net環境で用意されているものを使う。

DataTemplateもそのまま、データのテンプレート。
データというのは文字列だったり、クラスのインスタンスだったり、XMLのデータだったり。
今作っているものだと、TimelineItemViewModelとかですね。
データを変更するロジックを持っていても、UIの制御はほとんどの場合持たない。

例えば、タイムラインを表示する場合、ListBox(=Control)があって、
ListBoxはListBoxItem(=Control)をアイテムの数だけ持っていて、
ListBoxItemの上に、つぶやき(=Data)が載っているイメージ。
ここでいうつぶやき(=Data)にはアイコンから名前、テキストまで含まれる。
ListBoxItemが入れ物でつぶやきが中身、みたいな感じですね。

で、これまではすべてDataTemplateで表示していました。
ですが、ControlTemplateでも同じようなことができます。

これまでのDataTemplateでやるやりかたは、
ListBoxとListBoxItemがあって、
その上に載っているTimelineViewModelのインスタンス(=データ)を
DataTemplateを使ってどう表示するかを指定していたような感じです。
ですが、DataTemplateなので入れ物はカスタマイズできません。
(できるかもしれませんが、めんどくさいと思われます)
選択アイテム(選択行)の色を青色にするのはListBoxItemの役目なので、
その上に載っているデータのテンプレートをいじっても変更が難しいというわけですね。

対して、全部ControlTemplateでやろうとすると、
ListBoxItemを、
画像、名前やつぶやきのテキスト、コマンド用のボタンなどに対して
どの位置にどのような形で表示するかのフォーマットを決めるControlとしてカスタマイズにして、
そのカスタマイズされたListBoxItemが要求する画像URLやテキストのデータとして
TimelineItemViewModelの各プロパティをおいていくイメージ。

で、どっちを使うかは設計者の判断しだいのような気もしますが、
今回はどちらを使うべきか考えてみます。

私の考えだと、各つぶやきの外側の灰色の枠(Border)までが入れ物で、
それより中のもの(画像、名前、ボタン、本文テキスト)がデータです。
例えば、タイムラインにtwitpicの画像などフォーマットが異なるデータを表示するように
拡張するときのことをことを考えます。
そうすると、灰色の枠までが共通で、それより中身のデータが変わるでしょう。

※もちろん、アイコン、名前、~分前、ボタンまでは共通フォーマットだ、という考えもできると思います。

とりあえず、以上の考えで書き直してみたものが以下です。
選択されているアイテムがDropShadowのエフェクトがかかるようになっています。



<!--タイムライン表示部分のView-->
 
<UserControl x:Class="WTwitter.View.TimelineView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:compModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
 xmlns:view="clr-namespace:WTwitter.View"
 xmlns:vm="clr-namespace:WTwitter.ViewModel">
 <UserControl.Resources>
  <view:TextComponentsConverter x:Key="TextCompConverter"/>

  <!--アイコン付きボタンのテンプレート-->
  <DataTemplate DataType="{x:Type vm:ImageButton}">
   <Button Command="{Binding Path=Command}" ToolTip="{Binding Path=Description}"
     Background="Transparent" BorderBrush="Transparent">
    <Image Source="{Binding Path=Image}" Width="16" Height="16"/>
   </Button>
  </DataTemplate>

  <DataTemplate x:Key="TimelineItemDataTemplate">
   <DockPanel>
    <!--アイコン-->
    <Image Source="{Binding Path=ProfileImageUrl}" Width="32" Height="32"
        DockPanel.Dock="Left" VerticalAlignment="Top" Margin="5">
     <Image.ToolTip>
      <StackPanel>
       <TextBlock Text="{Binding Path=DetailDescription}"/>
      </StackPanel>
     </Image.ToolTip>
    </Image>
    <DockPanel DockPanel.Dock="Top">
     <!--名前-->
     <TextBlock DockPanel.Dock="Left">
      <Hyperlink Command="{Binding Path=OpenByBrowserCommand}" CommandParameter="{Binding Path=UserUrl}"
           TextDecorations=""
           Style="{DynamicResource TimelineItemNameStyle}">
       <TextBlock Text="{Binding Path=Name}"/>
      </Hyperlink>
      <TextBlock.ContextMenu>
       <ContextMenu>
        <MenuItem Header="このユーザーを_NGに登録" Command="{Binding Path=SetNgUserCommand}"/>
       </ContextMenu>
      </TextBlock.ContextMenu>
     </TextBlock>
     <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
      <!--時刻-->
      <TextBlock Text="{Binding Path=RelativeTimeString}"/>
      <!--ボタン-->
      <ItemsControl ItemsSource="{Binding Path=Commands}">
       <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
         <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
       </ItemsControl.ItemsPanel>
      </ItemsControl>
     </StackPanel>
    </DockPanel>
    <!--本文-->
    <ContentPresenter Style="{DynamicResource TimelineItemTextStyle}"
       Content="{Binding Path=TextComponents, Converter={StaticResource TextCompConverter}}"/>
   </DockPanel>
  </DataTemplate>
  
  <Style TargetType="{x:Type ListBoxItem}" x:Key="{x:Type ListBoxItem}">
   <Setter Property="Template">
    <Setter.Value>
     <ControlTemplate TargetType="{x:Type ListBoxItem}">
      <!--アイテムの枠-->
      <Border BorderBrush="LightGray" BorderThickness="2" Background="White"
        CornerRadius="3" Margin="3" Padding="2"
        x:Name="ItemContainer">
       <ContentPresenter ContentTemplate="{StaticResource TimelineItemDataTemplate}"/>
      </Border>
      <ControlTemplate.Triggers>
       <!--選択アイテムのエフェクト-->
       <Trigger Property="IsSelected" Value="True">
        <Setter TargetName="ItemContainer" Property="BitmapEffect">
         <Setter.Value>
          <OuterGlowBitmapEffect GlowSize="5" GlowColor="Black" Noise="0" Opacity="1"/>
         </Setter.Value>
        </Setter>
       </Trigger>
      </ControlTemplate.Triggers>
     </ControlTemplate>
    </Setter.Value>
   </Setter>
  </Style>

 </UserControl.Resources>
 
 <ListBox ItemsSource="{Binding Path=ViewSource.View}"
    ScrollViewer.HorizontalScrollBarVisibility="Disabled"
    HorizontalContentAlignment="Stretch">
 </ListBox>
</UserControl>



今回のソースコード
http://wtwitter.codeplex.com/SourceControl/changeset/view/43041

0 件のコメント:

コメントを投稿