按钮上的 WPF 触发器.带绑定的 LostFocus



我正在设计一个WPF应用程序,我想在其中为窗口的默认按钮提供一个标记(例如红色边框(。标记物的行为应为

  • 当没有按钮具有焦点时,标记应该在默认按钮上可见
  • 如果默认按钮获得焦点,则标记也应该可见
  • 如果任何其他按钮获得焦点,则应隐藏标记

由于我使用的是materialDesign,所以我不得不扩展"MaterialDesignRaisedButton"样式。 我编写了一个转换器,它将检查窗口中存在的所有按钮,并根据我的要求在默认按钮上设置标记。

internal class ButtonDefaultPropertyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool nonDefaultFocus = false;
foreach (Button button in FindVisualChildren<Button>((DependencyObject)value))
{                
if (button.IsFocused && !button.IsDefault)
{
nonDefaultFocus = true;
break;
}
}
foreach (Button button in FindVisualChildren<Button>((DependencyObject)value))
{
if (button.IsDefault)
{
if (!nonDefaultFocus)
button.BorderBrush = Brushes.Red;
else
{
button.BorderBrush = (Brush)Application.Current.Resources["PrimaryHueMidBrush"];
}
}
else
{
button.BorderBrush = (Brush)Application.Current.Resources["PrimaryHueMidBrush"];
}
}
return 1;
}

我已经在不同的属性上编写了触发器,只是为了调用转换器。我在 APP.xaml 中拥有它,以便我可以在整个应用程序中使用它。xaml 如下所示

<Style x:Key="MyButton" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource PrimaryHueMidBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryHueMidBrush}"/>
<Setter Property="Foreground" Value="{StaticResource PrimaryHueLightForegroundBrush}"/>
<Setter Property="materialDesign:ButtonProgressAssist.IndicatorForeground" Value="{DynamicResource PrimaryHueMidForegroundBrush}" />
<Setter Property="materialDesign:ButtonProgressAssist.IndicatorBackground" Value="{StaticResource PrimaryHueMidBrush}" />
<Setter Property="materialDesign:RippleAssist.Feedback" Value="White" />
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="materialDesign:ShadowAssist.ShadowDepth" Value="Depth1" />
<Setter Property="TextBlock.FontWeight" Value="Medium"/>
<Setter Property="TextBlock.FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="16 4 16 4"/>
<Setter Property="Height" Value="32" />
<Setter Property="materialDesign:ButtonProgressAssist.IsIndicatorVisible" Value="False" />
<Setter Property="materialDesign:ButtonProgressAssist.Opacity" Value=".4" />
<Setter Property="materialDesign:ButtonAssist.CornerRadius" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<AdornerDecorator CacheMode="{Binding RelativeSource={RelativeSource Self}, Path=(materialDesign:ShadowAssist.CacheMode)}">
<Grid>
<Border Background="{TemplateBinding Background}" 
CornerRadius="{Binding Path=(materialDesign:ButtonAssist.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
x:Name="border" 
Effect="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ShadowAssist.ShadowDepth), Converter={StaticResource ShadowConverter}}"/>
<ProgressBar x:Name="ProgressBar"
Style="{DynamicResource MaterialDesignLinearProgressBar}"
Minimum="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ButtonProgressAssist.Minimum)}"
Maximum="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ButtonProgressAssist.Maximum)}"
Foreground="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ButtonProgressAssist.IndicatorForeground)}"
Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ButtonProgressAssist.IndicatorBackground)}"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ButtonProgressAssist.Value)}"
IsIndeterminate="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ButtonProgressAssist.IsIndeterminate)}"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ButtonProgressAssist.IsIndicatorVisible), Converter={StaticResource BooleanToVisibilityConverter}}"
Height="{TemplateBinding Height}"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ButtonBase}}, Path=ActualWidth}"
Opacity="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(materialDesign:ButtonProgressAssist.Opacity)}"
HorizontalAlignment="Left"
VerticalAlignment="Center">
</ProgressBar>
</Grid>
</AdornerDecorator>
<materialDesign:Ripple Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Focusable="False"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<materialDesign:Ripple.Clip>
<MultiBinding Converter="{StaticResource BorderClipConverter}">
<Binding ElementName="border" Path="ActualWidth" />
<Binding ElementName="border" Path="ActualHeight" />
<Binding ElementName="border" Path="CornerRadius" />
<Binding ElementName="border" Path="BorderThickness" />
</MultiBinding>
</materialDesign:Ripple.Clip>
</materialDesign:Ripple>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsDefault" Value="True">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}},Converter={StaticResource ButtonDefaultPropertyConverter}}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="border" Property="materialDesign:ShadowAssist.Darken" Value="True" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter TargetName="border" Property="materialDesign:ShadowAssist.Darken" Value="True" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource ButtonDefaultPropertyConverter}, UpdateSourceTrigger=Explicit}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.23"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

按钮看起来像这样

<Button Style="{StaticResource MyButton}" Grid.Row="0" x:Name="btn1" Height="40" Width="100" Content="Button1" IsDefault="True" />
<Button Style="{StaticResource MyButton}" Grid.Row="1" x:Name="btn2" Height="40" Width="100" Content="Button2" />

虽然这几乎达到了我的目的。但唯一的问题是当焦点从按钮丢失时,转换器不会被触发。 我用谷歌搜索了一下,发现我可以对LostFocus事件使用EventTriggers。EventTrigger 的問題是我不能在 EventTrigger 中使用绑定。

所以现在我被困住了。

如果有人能帮忙,那就太好了...

提前致谢

您只需向Style添加一个EventSetter并以编程方式处理LostFocus事件即可。

如果Style是在ResourceDictionary中定义的,则应向其添加一个代码隐藏类,并在此类中定义事件处理程序。

转换器 ButtonDefaultPropertyConverter 只能正确处理前两个条件。 他并不总是能够应付第三个条件。 仅当窗口焦点从一个按钮移动到另一个按钮并且没有其他具有焦点的元素时,这才是正确的。

对于正常操作,转换器必须至少传输所有其他按钮的焦点值。 在我看来,使用附加属性会更容易实现。

最新更新