Создание WPF ValueConverter для кисти - PullRequest
3 голосов
/ 04 июня 2009

Сегодня в блоге Nerd Plus Art появилась статья о создании ресурсов WPF для стрелок, которые автор часто использует. У меня есть побочный проект, в котором есть кнопки «Назад» и «Вперед», поэтому я подумал, что стрелки «Влево» и «Вправо» будут отлично работать на этих кнопках.

Я добавил геометрию LeftArrow и RightArrow к ресурсам моего приложения, а затем использовал их в качестве содержимого кнопок:

<Application x:Class="Notes.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Views/MainWindow.xaml">
    <Application.Resources>
        <Geometry x:Key="RightArrow">M0,0 L1,0.5 0,1Z</Geometry>
        <Geometry x:Key="LeftArrow">M0,0.5 L1,1 1,0Z</Geometry>
    </Application.Resources>
</Application>

<Button x:Name="BackButton"
    Padding="5,5,5,5"
    Command="{x:Static n:Commands.GoBackCommand}">
    <Path Data="{StaticResource LeftArrow}" Width="10" Height="8"
        Stretch="Fill" Fill="Black"/>
    </Button>
<Button x:Name="ForwardButton"
    Padding="5,5,5,5"
    Command="{x:Static n:Commands.GoForwardCommand}">
    <Path Data="{StaticResource RightArrow}" Width="10" Height="8"
        Stretch="Fill" Fill="Red" />
</Button>

Это сработало, за исключением того, что стрелки были нарисованы черным независимо от того, была ли кнопка включена или нет. Итак, я создал ValueConverter для перехода от bool к Brush:

class EnabledColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        bool b = (bool)value;
        return b ? Brushes.Black : Brushes.Gray;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

(Я понимаю, что, вероятно, мне следует использовать системные цвета вместо жестко закодированных черного и серого, но сначала я просто хотел, чтобы это работало.)

Я изменил свойство Fill Path для использования моего конвертера (который я создал в ресурсах приложения):

<Path Data="{StaticResource LeftArrow}" Width="10" Height="8"
    Stretch="Fill"
    Fill="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}, Path=IsEnabled, Converter={StaticResource EnabledColorConverter}}"/>

К сожалению, это не работает, и я не уверен, почему. Когда я запускаю его, стрелка вообще не рисуется. Я проверил окно вывода в Visual Studio, и ошибки привязки не отображались. Я также проверил, что bool является правильным значением в преобразователе, основываясь на том, должна ли кнопка быть включена или нет.

Если я верну Path обратно в TextBlock (и свяжу его свойство Foreground таким же образом, как Path.Fill), текст всегда будет черным.

Я что-то не так делаю? Почему Brush, возвращаемый моим конвертером, не используется для визуализации Path в кнопке?

Ответы [ 3 ]

3 голосов
/ 04 июня 2009

Для такого рода обновлений состояния пользовательского интерфейса попробуйте вместо этого использовать триггеры; это избавит вас от написания конвертера значений, и его будет намного короче.

Попробуйте это:

<Application x:Class="Notes.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Views/MainWindow.xaml">
    <Application.Resources>
        <Geometry x:Key="RightArrow">M0,0 L1,0.5 0,1Z</Geometry>
        <Geometry x:Key="LeftArrow">M0,0.5 L1,1 1,0Z</Geometry>

         <!-- Base Arrow Style, with a trigger to toggle it Gray when its parent button is disabled -->
        <Style x:Key="ArrowStyle" TargetType="Path">
            <Setter Property="Width" Value="10"/>
            <Setter Property="Height" Value="8"/>
            <Setter Property="Stretch" Value="Fill"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}, Path=IsEnabled}" Value="False">
                    <Setter Property="Fill" Value="Gray"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>

        <!-- Left Arrow Style, with the Left Arrow fill and data -->
        <Style x:Key="LeftArrowStyle" BasedOn="{StaticResource ArrowStyle}" TargetType="Path">
            <Setter Property="Fill" Value="Black"/>
            <Setter Property="Data" Value="{StaticResource LeftArrow}"/>
        </Style>

        <!-- Right Arrow Style, with the Right Arrow fill and data -->
        <Style x:Key="RightArrowStyle" BasedOn="{StaticResource ArrowStyle}" TargetType="Path">
            <Setter Property="Fill" Value="Red"/>
            <Setter Property="Data" Value="{StaticResource RightArrow}"/>
        </Style>
    </Application.Resources>

    <Button x:Name="BackButton" Padding="5,5,5,5" IsEnabled="False">
        <Path Style="{StaticResource LeftArrowStyle}"/>
    </Button>
    <Button x:Name="ForwardButton" Padding="5,5,5,5">
        <Path Style="{StaticResource RightArrowStyle}"/>
    </Button>
</Application>

Затем вы устанавливаете заливку по умолчанию в LeftArrowStyle и RightArrowStyle для левой и правой стрелок соответственно. Если вы установите его в самом Path, тогда это значение будет иметь приоритет и переопределит все, что может сделать стиль или его триггер. Базовый стиль, ArrowStyle, содержит DataTrigger, связанный с родительской кнопкой - он срабатывает всякий раз, когда IsEnabled имеет значение false, и меняет заполнение пути на серый.

2 голосов
/ 04 июня 2009

Почему бы просто не связать заполнение вашего пути с передним планом кнопки?

<Button x:Name="BackButton"
    Padding="5,5,5,5"
    Command="{x:Static n:Commands.GoBackCommand}">
    <Path Data="{StaticResource LeftArrow}" Width="10" Height="8"
        Stretch="Fill" Fill="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}, Path=Foreground"/>
    </Button>
0 голосов
/ 04 июня 2009

Ваш код по существу работает. Я думаю, что ваш статический ресурс может быть неправильным, поскольку вы не указали, откуда он получает конвертер.

Вам, вероятно, нужно

<Window.Resources>
    <conv:EnabledColorConverter x:Key="brushConv" />
</Window.Resources>

и затем укажите свою привязку как:

{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}, Path=IsEnabled, Converter={StaticResource brushConv}}
...