I am designing a WPF application where I want to give a marker(e.g. Red Border) to the default button of a window. The behaviour of the marker should be
As I am using the materialDesign, so I had to extend the "MaterialDesignRaisedButton" style. I have written one converter which will check all the buttons present in the window and set the marker on the default button as per my requirement.
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;
}
I have written the trigger on a different property, just to call the converter. I have it in the APP.xaml so that I can use it through out my application. The xaml looks like this
<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>
And the button simply looks like this
<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" />
Although this almost serves my purpose. But the only problem is when the focus is lost from a button then the converter is not being triggered. I have googled it and found that I can use EventTriggers for LostFocus event. The problem with EventTrigger is that I can not use bindings in the EventTriggers.
So now I am stuck.
Would be really nice if somebody can help...
Thanks in advance
You could simply add an EventSetter
to the Style
and handle the LostFocus
event programmatically.
If the Style
is defined in a ResourceDictionary
, you should add a code-behind class to it and define the event handler in this one.