Skip to content

Instantly share code, notes, and snippets.

@MrAliev
Last active May 7, 2021 19:12
Show Gist options
  • Save MrAliev/8ca7cea539e4c402900fa7e3abb44794 to your computer and use it in GitHub Desktop.
Save MrAliev/8ca7cea539e4c402900fa7e3abb44794 to your computer and use it in GitHub Desktop.
WPF Button сontrol with automatic start of the timer command via a trigger
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WPFUserControls.Controls">
<!--#region Static Resources -->
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
<!--#endregion-->
<!--#region TimerButton.TimerTextBlock Style -->
<Style x:Key="TimerButton.TimerTextBlockStyle" TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
<!--#endregion-->
<!--#region Focus visual style-->
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" StrokeDashArray="1 2" SnapsToDevicePixels="true" StrokeThickness="1" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--#endregion-->
<!--#region TimerButton style -->
<Style TargetType="{x:Type controls:TimerButton}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="TimerStart" Value="False"/>
<Setter Property="TimerTextBlockStyle" Value="{StaticResource TimerButton.TimerTextBlockStyle}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:TimerButton}">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="true">
<DockPanel>
<TextBlock DockPanel.Dock="Right"
x:Name="timerText"
Text="{Binding TimerSeconds, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:TimerButton}}}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
Margin="10,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{TemplateBinding TimerTextBlockStyle}">
</TextBlock>
<ContentPresenter x:Name="contentPresenter"
Focusable="False"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush"
TargetName="border"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
<Setter Property="Background" TargetName="timerText" Value="{StaticResource Button.MouseOver.Background}"/>
<!--<Setter Property="TimerStart" Value="True"></Setter>-->
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
</Trigger>
<Trigger Property="TimerStart" Value="True">
<Setter Property="TextBlock.Visibility" TargetName="timerText" Value="Visible"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--#endregion-->
</ResourceDictionary>
<Window x:Class="ControlsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ControlsTest"
xmlns:controls="clr-namespace:WPFUserControls.Controls;assembly=WPFUserControls"
d:DataContext="{d:DesignInstance Type=local:MainWindowViewModel}"
Title="MainWindow" Height="240" Width="470">
<Grid>
<StackPanel>
<controls:TimerButton
Width="150" Height="50"
TimerSeconds="5"
Command="{Binding TestButtonCommand}"
CommandParameter="Command Parameter">
<controls:TimerButton.Content>
<StackPanel>
<TextBlock Text="The timer button"></TextBlock>
</StackPanel>
</controls:TimerButton.Content>
<controls:TimerButton.Style>
<Style>
<Style.Triggers>
<Trigger Property="controls:TimerButton.IsMouseOver" Value="True">
<Setter Property="controls:TimerButton.TimerStart" Value="True"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</controls:TimerButton.Style>
</controls:TimerButton>
</StackPanel>
</Grid>
</Window>
namespace WPFUserControls.Controls
{
public class TimerButton : Button, INotifyPropertyChanged
{
#region private fields
private Timer _timer;
private int _timerSeconds;
private int _remainingSeconds;
#endregion
#region Dependency Properties
#region TimerSeconds : int - The number of seconds before the command starts automaticall
/// <summary>Seconds for auto run command</summary>
public static readonly DependencyProperty TimerSecondsProperty =
DependencyProperty.Register(
nameof(TimerSeconds),
typeof(int),
typeof(TimerButton),
new PropertyMetadata(default(int)));
/// <summary>Seconds for auto run command</summary>
[Description("The number of seconds before the command starts automaticall")]
public int TimerSeconds
{
get => (int)GetValue(TimerSecondsProperty);
set => SetValue(TimerSecondsProperty, value);
}
#endregion
#region TimerTextBlockStyle : Style - Timer TextBlockStyle
/// <summary>Timer TextBlockStyle</summary>
public static readonly DependencyProperty TimerTextBlockStyleProperty =
DependencyProperty.Register(
nameof(TimerTextBlockStyle),
typeof(Style),
typeof(TimerButton),
new PropertyMetadata(default(Style)));
/// <summary>Timer TextBlockStyle</summary>
[Description("Timer TextBlockStyle")]
public Style TimerTextBlockStyle
{
get => (Style) GetValue(TimerTextBlockStyleProperty);
set => SetValue(TimerTextBlockStyleProperty, value);
}
#endregion
#region TimerStart : bool - summary
/// <summary>summary</summary>
public static readonly DependencyProperty TimerStartProperty =
DependencyProperty.Register(
nameof(TimerStart),
typeof(bool),
typeof(TimerButton),
new PropertyMetadata(default(bool), OnTimerStartPropertyChanged));
private static void OnTimerStartPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TimerButton timerButton)
{
timerButton.OnPropertyChanged(e.Property.Name);
}
}
/// <summary>summary</summary>
[Description("summary")]
public bool TimerStart
{
get => (bool)GetValue(TimerStartProperty);
set => SetValue(TimerStartProperty, value);
}
#endregion
#endregion
#region Constructors
static TimerButton() =>
DefaultStyleKeyProperty.OverrideMetadata(typeof(TimerButton),
new FrameworkPropertyMetadata(typeof(TimerButton)));
public TimerButton() => PropertyChanged += OnPropertyChanged;
#endregion
#region Private methods
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!e.PropertyName.Equals(nameof(TimerStart))) return;
var timerState = false;
Dispatcher.Invoke(() => timerState = (bool)GetValue(TimerStartProperty));
if (timerState)
{
Dispatcher.Invoke(() => _timerSeconds = (int)GetValue(TimerSecondsProperty));
_timer = new Timer(1000) { AutoReset = true };
_timer.Elapsed += Timer_Elapsed;
_remainingSeconds = _timerSeconds;
_timer.Start();
}
else
{
_timer.Dispose();
Dispatcher.Invoke(() => SetValue(TimerSecondsProperty, _timerSeconds));
}
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
_remainingSeconds--;
if (_remainingSeconds == 0) RunCommand();
else
{
Dispatcher.Invoke(() => SetValue(TimerSecondsProperty, _remainingSeconds));
}
}
private void RunCommand()
{
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
}
Dispatcher.InvokeAsync(() => Command.Execute(CommandParameter));
}
#endregion
#region INPC
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
}
}
@Infarh
Copy link

Infarh commented May 7, 2021

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment