//D:\C\WpfApp12\NumUpDown.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp12
{
/// <summary>
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
///
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:WpfApp12"
///
///
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:WpfApp12;assembly=WpfApp12"
///
/// You will also need to add a project reference from the project where the XAML file lives
/// to this project and Rebuild to avoid compilation errors:
///
/// Right click on the target project in the Solution Explorer and
/// "Add Reference"->"Projects"->[Browse to and select this project]
///
///
/// Step 2)
/// Go ahead and use your control in the XAML file.
///
/// <MyNamespace:NumUpDown/>
///
/// </summary>
public class NumUpDown : Control
{
private Button upBtn, downBtn;
static NumUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumUpDown), new FrameworkPropertyMetadata(typeof(NumUpDown)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var tempUpBtn = GetTemplateChild("PART_UpBtn") as Button;
if (tempUpBtn != null)
{
upBtn = tempUpBtn;
upBtn.Click += UpBtn_Click;
}
var tempDownBtn = GetTemplateChild("PART_DownBtn") as Button;
if (tempDownBtn != null)
{
downBtn = tempDownBtn;
downBtn.Click += DownBtn_Click;
}
}
private void DownBtn_Click(object sender, RoutedEventArgs e)
{
if (--Num < MinNum)
{
Num = MaxNum;
}
NumStr = $"{Num:D2}";
}
private void UpBtn_Click(object sender, RoutedEventArgs e)
{
if (++Num > MaxNum)
{
Num = MinNum;
}
NumStr = $"{Num:D2}";
}
public int Num
{
get { return (int)GetValue(NumProperty); }
set { SetValue(NumProperty, value); }
}
// Using a DependencyProperty as the backing store for Num. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NumProperty =
DependencyProperty.Register(nameof(Num), typeof(int), typeof(NumUpDown),
new PropertyMetadata(0, OnNumChanged));
private static void OnNumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public int MaxNum
{
get { return (int)GetValue(MaxNumProperty); }
set { SetValue(MaxNumProperty, value); }
}
// Using a DependencyProperty as the backing store for MaxNum. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MaxNumProperty =
DependencyProperty.Register(nameof(MaxNum),
typeof(int),
typeof(NumUpDown),
new PropertyMetadata(0));
public int MinNum
{
get { return (int)GetValue(MinNumProperty); }
set { SetValue(MinNumProperty, value); }
}
// Using a DependencyProperty as the backing store for MinNum. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MinNumProperty =
DependencyProperty.Register(nameof(MinNum), typeof(int), typeof(NumUpDown), new PropertyMetadata(0));
public string NumStr
{
get { return (string)GetValue(NumStrProperty); }
set { SetValue(NumStrProperty, value); }
}
// Using a DependencyProperty as the backing store for NumStr. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NumStrProperty =
DependencyProperty.Register(nameof(NumStr),
typeof(string),
typeof(NumUpDown),
new PropertyMetadata("00"));
public string ControlName
{
get { return (string)GetValue(ControlNameProperty); }
set { SetValue(ControlNameProperty, value); }
}
// Using a DependencyProperty as the backing store for ControlName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ControlNameProperty =
DependencyProperty.Register(nameof(ControlName),
typeof(string),
typeof(NumUpDown),
new PropertyMetadata(""));
}
}
//D:\C\WpfApp12\TimePicker.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp12
{
/// <summary>
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
///
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:WpfApp12"
///
///
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:WpfApp12;assembly=WpfApp12"
///
/// You will also need to add a project reference from the project where the XAML file lives
/// to this project and Rebuild to avoid compilation errors:
///
/// Right click on the target project in the Solution Explorer and
/// "Add Reference"->"Projects"->[Browse to and select this project]
///
///
/// Step 2)
/// Go ahead and use your control in the XAML file.
///
/// <MyNamespace:TimePicker/>
///
/// </summary>
public class TimePicker : Control
{
static TimePicker()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(typeof(TimePicker)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
public int HourNum
{
get { return (int)GetValue(HourNumProperty); }
set { SetValue(HourNumProperty, value); }
}
// Using a DependencyProperty as the backing store for HourNum. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HourNumProperty =
DependencyProperty.Register(nameof(HourNum), typeof(int), typeof(TimePicker), new PropertyMetadata(0,OnHourNumChanged));
private static void OnHourNumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tmPicker=d as TimePicker;
if(tmPicker!=null)
{
tmPicker.TimeStr = $"{tmPicker.HourNum:D2}:{tmPicker.MinuteNum:D2}:{tmPicker.SecondNum:D2}";
}
}
public int MinuteNum
{
get { return (int)GetValue(MinuteNumProperty); }
set { SetValue(MinuteNumProperty, value); }
}
// Using a DependencyProperty as the backing store for MinuteNum. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MinuteNumProperty =
DependencyProperty.Register(nameof(MinuteNum),
typeof(int),
typeof(TimePicker),
new PropertyMetadata(0,OnMinuteNumChanged));
private static void OnMinuteNumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tmPicker = d as TimePicker;
if (tmPicker != null)
{
tmPicker.TimeStr = $"{tmPicker.HourNum:D2}:{tmPicker.MinuteNum:D2}:{tmPicker.SecondNum:D2}";
}
}
public int SecondNum
{
get { return (int)GetValue(SecondNumProperty); }
set { SetValue(SecondNumProperty, value); }
}
// Using a DependencyProperty as the backing store for SecondNum. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SecondNumProperty =
DependencyProperty.Register(nameof(SecondNum),
typeof(int),
typeof(TimePicker),
new PropertyMetadata(0, OnSecondNumChanged));
private static void OnSecondNumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tmPicker = d as TimePicker;
if (tmPicker != null)
{
tmPicker.TimeStr = $"{tmPicker.HourNum:D2}:{tmPicker.MinuteNum:D2}:{tmPicker.SecondNum:D2}";
}
}
public string TimeStr
{
get { return (string)GetValue(TimeStrProperty); }
set { SetValue(TimeStrProperty, value); }
}
// Using a DependencyProperty as the backing store for TimeStr. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TimeStrProperty =
DependencyProperty.Register(nameof(TimeStr),
typeof(string),
typeof(TimePicker),
new PropertyMetadata("00:00:00"));
}
}
//D:\C\WpfApp12\Themes\Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp12">
<Style TargetType="{x:Type local:NumUpDown}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:NumUpDown}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Width="90" Height="70">
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontSize" Value="20"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Right"
Text="{Binding Path=ControlName,RelativeSource={RelativeSource AncestorType={x:Type local:NumUpDown}}}"/>
<Button x:Name="PART_UpBtn"
Grid.Row="0"
Grid.Column="1"
ToolTip="{Binding Path=MaxNum,RelativeSource={RelativeSource AncestorType={x:Type local:NumUpDown}}}">
<Path Data="M0,8 L8,0 L16,8 Z"
Fill="Black"
Width="10"
Height="10"
Stretch="Uniform"/>
</Button>
<TextBlock Grid.Row="1"
Grid.Column="1"
Text="{Binding Path=NumStr,RelativeSource={RelativeSource AncestorType={x:Type local:NumUpDown}}}"/>
<Button x:Name="PART_DownBtn"
Grid.Row="2"
Grid.Column="1"
ToolTip="{Binding Path=MinNum,RelativeSource={RelativeSource AncestorType={x:Type local:NumUpDown}}}">
<Path Data="M0,0 L8,8 L16,0 Z"
Fill="Black"
Width="10"
Height="10"
Stretch="Uniform"/>
</Button>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:TimePicker}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TimePicker}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Height="110" Width="300">
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontSize" Value="30"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Time:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"
Text="{Binding Path=TimeStr,RelativeSource={RelativeSource AncestorType={x:Type local:TimePicker}}}"/>
<local:NumUpDown MaxNum="23" MinNum="0" ControlName="Hour:"
Grid.Row="1" Grid.Column="0"
Num="{Binding Path=HourNum,Mode=TwoWay,RelativeSource={RelativeSource AncestorType={x:Type local:TimePicker}}}"/>
<local:NumUpDown MaxNum="59" MinNum="0" ControlName="Minute:"
Grid.Row="1" Grid.Column="1"
Num="{Binding Path=MinuteNum,Mode=TwoWay,RelativeSource={RelativeSource AncestorType={x:Type local:TimePicker}}}"/>
<local:NumUpDown MaxNum="59" MinNum="0" ControlName="Second:"
Grid.Row="1" Grid.Column="2"
Num="{Binding Path=SecondNum,Mode=TwoWay,RelativeSource={RelativeSource AncestorType={x:Type local:TimePicker}}}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
//D:\C\WpfApp12\MainWindow.xaml
<Window x:Class="WpfApp12.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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp12"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel Height="100" Width="300"
Orientation="Horizontal">
<local:TimePicker/>
</StackPanel>
</Grid>
</Window>