Criando um WPF IValueConverter para uma escova
-
09-09-2019 - |
Pergunta
No Nerd Além disso Art blog hoje, houve um post sobre a criação de recursos WPF para setas, que os usos autor freqüentemente. Eu tenho um projeto paralelo que tem botões Avançar e Voltar, então eu pensei que o setas Esquerda e Direita seria um grande trabalho nesses botões.
Eu adicionei as geometrias LeftArrow
e RightArrow
aos recursos da minha aplicação, e, em seguida, usou-os como o conteúdo dos botões:
<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>
Isso funcionou, exceto que as setas foram desenhadas em preto, independentemente do botão foi ativado ou não. Então, eu criei um ValueConverter
para ir de um bool
a um 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();
}
}
(eu percebo que eu provavelmente deve usar cores do sistema, em vez de codificado preto e cinza, mas eu só queria começar este trabalho, em primeiro lugar.)
Eu modifiquei a propriedade Fill
do Path
usar meu conversor (que eu criei dentro dos recursos do aplicativo):
<Path Data="{StaticResource LeftArrow}" Width="10" Height="8"
Stretch="Fill"
Fill="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}, Path=IsEnabled, Converter={StaticResource EnabledColorConverter}}"/>
Infelizmente, isso não funciona, e eu não sei porquê. Quando eu executá-lo, a seta não é desenhada em tudo. Eu verifiquei a janela de saída no Visual Studio, e nenhum erro de ligação foram exibidos. Eu também verificou que o bool
é o valor correto no conversor, com base no se o botão deve ser habilitado ou não.
Se eu mudar a parte traseira Path
a um TextBlock
(e vincular sua propriedade Foreground
da mesma maneira como Path.Fill
), o texto é sempre desenhado em preto.
Estou fazendo algo errado? Porque é que a Brush
devolvido pelo meu conversor não usado para processar o Path
no botão?
Solução
Por que não apenas ligam o preenchimento do seu caminho para o primeiro plano do botão?
<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>
Outras dicas
Para este tipo de atualizações de estado de interface do usuário, tente usar gatilhos vez; ele vai te salvar de escrever um conversor de valor, e é muito mais curto para escrever.
Tente isto:
<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>
Em seguida, defina o seu preenchimento padrão no LeftArrowStyle e RightArrowStyle, para as setas esquerda e direita, respectivamente. Se você configurá-lo no próprio caminho, então esse valor teria precedência e substituir qualquer coisa que um estilo ou o seu gatilho pode fazer. O estilo de base, ArrowStyle, contém uma DataTrigger ligado ao botão de pai -. Ele é acionado sempre que IsEnabled é falsa, e muda de preenchimento do Path to Grey
Seu código funciona essencialmente. Eu acho que seu recurso estático pode estar errado quanto a sua não especificando onde isso está ficando o conversor.
Você provavelmente precisará
<Window.Resources>
<conv:EnabledColorConverter x:Key="brushConv" />
</Window.Resources>
e, em seguida, especifique a sua ligação como:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}, Path=IsEnabled, Converter={StaticResource brushConv}}