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

SourceChord

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

特定の値以上だったら動作するTriggerを作る

C# WPF

WPFのTrigger、便利ですよね。
Styleを作るときに、特定の条件の時に表示を切り替えたり簡単にできて、非常に便利。
でも、ちょっと痒いとこに手が届かない。
Triggerだと、指定したプロパティが特定の値の場合に動作します。
なので、「指定したプロパティが○○以上だったら」みたいな条件を指定できません。


そんな時は、Converterを併用することで前述の動作を実装できます。
こんなコンバーターを用意しておくと、何かと便利だと思います。

    public class IsGreaterThanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var v = System.Convert.ToDouble(value);
            var compareValue = double.Parse(parameter as string);
            return v > compareValue;
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class IsLessThanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var v = System.Convert.ToDouble(value);
            var compareValue = double.Parse(parameter as string);
            return v < compareValue;
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

※制限
ConverterParameterで条件に用いる数値を指定してます。
なので、この条件の数値はバインドすることができず、動的な条件を実現することはできません。

使用例1

f:id:minami_SC:20141102032719p:plain:w300
スライダーのValueプロパティを、TextBlockのTextにバインドしています。
TextBlockのStyleでDataTriggerを設定し、
IsLessThanConverterを使って、Valueが0以下だったら、TextBlockの文字色が赤くなるようにしています。

MainWindow.xaml
<Window x:Class="WpfBaseTemplate1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfBaseTemplate1"
        Title="MainWindow"
        Width="400"
        Height="200">
    <Window.Resources>
        <local:IsLessThanConverter x:Key="IsLessThanConverter" />
    </Window.Resources>
    <Grid>
        <StackPanel Margin="50">
            <TextBlock Margin="5" Text="{Binding Value, ElementName=sldValue, StringFormat=\{0:F2\}}">
                <TextBlock.Style>
                    <Style TargetType="{x:Type TextBlock}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Value,
                                                           Converter={StaticResource IsLessThanConverter},
                                                           ConverterParameter=0,
                                                           ElementName=sldValue}"
                                         Value="True">
                                <Setter Property="Foreground" Value="Red" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </TextBlock.Style>
            </TextBlock>
            <Slider x:Name="sldValue" Minimum="-10" />
        </StackPanel>
    </Grid>
</Window>

使用例2

f:id:minami_SC:20141102032727p:plain:w300
今度は、DataContextに設定したVMのプロパティとバインドしてます。
ボタンを押すと、リストボックスの項目が増えていきますが、項目数が5個になったら、リストボックスの枠を赤くするようにDataTriggerの設定をしてます。

MainWindow.xaml
<Window x:Class="WpfBaseTemplate1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfBaseTemplate1"
        Title="MainWindow"
        Width="400"
        Height="300">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Window.Resources>
        <local:IsGreaterThanConverter x:Key="IsGreaterThanConverter" />
    </Window.Resources>
    <Grid>
        <ListBox HorizontalAlignment="Left" Margin="10" Width="158"
                 ItemsSource="{Binding Items}">
            <ListBox.Style>
                <Style TargetType="{x:Type ListBox}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Items.Count,
                                                       Converter={StaticResource IsGreaterThanConverter},
                                                       ConverterParameter=5}"
                                     Value="True">
                            <Setter Property="BorderBrush" Value="Red" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ListBox.Style>
        </ListBox>
        <Button Content="Add" HorizontalAlignment="Left" Margin="173,10,0,0" VerticalAlignment="Top" Width="75"
                Command="{Binding AddCommand}"/>
    </Grid>
</Window>
MainWindowViewModel.cs
    class MainWindowViewModel : BindableBase
    {
        private ObservableCollection<string> items;
        public ObservableCollection<string> Items
        {
            get { return items; }
            set { this.SetProperty(ref this.items, value); }
        }

        public MainWindowViewModel()
        {
            this.Items = new ObservableCollection<string>();
        }


        private RelayCommand addCommand;
        public RelayCommand AddCommand
        {
            get { return addCommand = addCommand ?? new RelayCommand(Add); }
        }

        private void Add()
        {
            this.Items.Add("hoge");
        }
    }