Create one Button like this:
class ActionButton : FrameworkElement
{
Border border;
string icon;
public string Icon {
get { return icon; }
set { icon = value; ((Path)border.Child).Data = Geometry.Parse(value); }
}
public Action Command { get; set; }
protected SolidColorBrush brush;
protected ColorAnimation anim;
protected Color normalColor, highlightColor, downColor;
public ActionButton() {
normalColor = Colors.Black;
highlightColor = Colors.Coral;
downColor = Colors.Red;
brush = new SolidColorBrush(normalColor);
border = new Border() {
Background = Brushes.Transparent,
Child = new Path() {
Fill = brush,
Stretch = Stretch.Uniform
}
};
anim = new ColorAnimation() {
Duration = TimeSpan.FromSeconds(0.5),
EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }
};
FocusVisualStyle = null;
AddVisualChild(border);
}
protected void animateBrush(Color color) {
anim.To = color;
brush.BeginAnimation(SolidColorBrush.ColorProperty, anim);
}
protected override Size MeasureOverride(Size availableSize) {
border.Measure(new Size(Width, Height));
border.Arrange(new Rect(border.DesiredSize));
return border.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize) => new Size(Width, Height);
protected override void OnMouseEnter(MouseEventArgs e) => animateBrush(highlightColor);
protected override void OnMouseLeave(MouseEventArgs e) => animateBrush(normalColor);
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) => Command.Invoke();
protected override void OnMouseDown(MouseButtonEventArgs e) => animateBrush(downColor);
protected override Visual GetVisualChild(int index) => border;
protected override int VisualChildrenCount => 1;
}
and put that in Grid/StackPanel, etc. as many times as you want like this:
<Grid Margin="10">
<Grid.Resources>
<Style TargetType="local:ActionButton">
<Setter Property="Width" Value="24"/>
<Setter Property="Height" Value="24"/>
<Setter Property="Margin" Value="5 0 5 0"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<local:ActionButton Icon="m 1396 2749.6 0.7 -513.8 5.1 -13.6 c 8.6 -22.7 19.4 -39.3 36 -55.6 16.4 -16.1 38.1 -28.3 61.7 -35.1 17.3 -4.9 1658.2 -4.9 1676 0 45.6 12.6 80.2 44.6 97.7 90.7 l 5.1 13.6 0.7 513.8 0.5 513.5 h -942 -942 z m 1698.9 179.3 v -28 h -757.3 -757.3 v 28 28 h 757.3 757.3 z m 0 -209.2 v -26.9 h -757.3 -757.3 v 26.9 26.9 h 757.3 757.3 z m 0 -209.2 v -28 h -757.3 -757.3 v 28 28 h 757.3 757.3 z"/>
<local:ActionButton Grid.Column="1" Icon="m 1882.9 1934 c -48.4 -7.7 -89.8 -45.8 -105.9 -97.9 -3 -10.3 -3.7 -57 -4.4 -396.7 l -1.2 -385.2 h 605.6 605.6 v 385.2 c 0 320.7 -0.7 386.9 -3.3 395.5 -13.3 43.2 -46.8 77.6 -91.4 93.5 l -14 4.9 -492 0.9 c -270.7 0.5 -495.1 0.2 -499.1 -0.2 z M 2787 1802.6 c 14.7 -5.4 31.6 -20.3 40.2 -35.8 l 7 -12.6 v -259.5 -259.5 l -6.3 -12.2 c -7.5 -14 -20.3 -27.1 -33.4 -34.4 -9.4 -4.9 -10.5 -4.9 -148.4 -4.9 H 2507 l -12.4 7.7 c -14.7 8.9 -21.7 16.4 -30.4 32 l -6.3 11.7 0.2 258.3 0.2 258.3 5.4 11.5 c 7.2 16.1 24.3 32.5 40 38.3 12.2 4.7 16.4 4.9 142.4 4.9 115.2 -0.2 130.9 -0.5 141 -4 z"/>
<local:ActionButton Grid.Column="2" Icon="m 1217.8 3596.2 c -77.8 -14.7 -142.1 -69.7 -167.4 -142.8 -11.9 -34.6 -11.2 47.5 -11.9 -1186 -0.9 -1065.9 -0.7 -1145.6 3 -1164.1 14.5 -73.9 70.6 -138.8 140 -162.7 35.8 -12.2 -40 -11.2 982.9 -11.9 858.1 -0.7 947.2 -0.5 963 2.8 17.8 4 35.5 9.4 52.6 16.4 46.3 18.9 44.6 17.3 246.8 220.2 178.6 178.8 184.2 184.7 192.1 201.7 4.7 9.6 10.3 25.5 12.6 35.1 4 16.8 4.2 60.3 4.2 1014.5 v 996.9 l -5.4 19.9 c -10.3 38.8 -29.9 72.5 -59.1 100.7 -26.2 25.5 -55.4 42.5 -92.8 54.2 -15.4 4.7 -20.8 4.9 -286.8 6.5 -149.1 0.7 -650.8 1.6 -1115 1.6 -659.2 0 -847.1 -0.7 -859 -3 z m 2049.5 -218.6 c -0.5 -96.8 0 -121.6 2.3 -121.6 2.3 0 2.8 -99.8 2.3 -506.5 l -0.7 -506.8 -5.1 -13.6 c -17.5 -46 -52.1 -78.1 -97.7 -90.7 -17.8 -4.9 -1644.7 -4.9 -1662 0 -46 13.1 -80.2 44.6 -97.7 90.7 l -5.1 13.6 -0.7 506.8 c -0.5 407.2 0 506.5 2.3 506.1 2.3 -0.5 3.3 24.5 3.7 121.6 l 0.5 122 H 2338.6 3268 Z M 2880.9 1921.3 c 44.6 -15.9 78.1 -50.3 91.4 -93.5 2.6 -8.6 3.3 -73.9 3.3 -388.5 V 1061.1 H 2377 1778.4 l 1.2 378.2 c 0.9 405.8 0.2 383.1 12.2 410.7 5.6 13.1 24.8 38.3 35.8 47.2 19.2 15.4 42.3 26.4 62.4 29.7 4 0.5 225.3 0.7 492 0.2 l 485 -0.9 14 -4.9 z"/>
<local:ActionButton Grid.Column="3" Icon="m 2496.7 1808.8 c -15.7 -5.8 -32.7 -22.2 -40 -38.3 l -5.4 -11.5 -0.2 -265.3 -0.2 -265.3 6.3 -11.7 c 8.6 -15.7 15.7 -23.1 30.4 -32 l 12.4 -7.7 h 146.1 c 144.9 0 146.1 0 155.4 4.9 13.1 7.2 25.9 20.3 33.4 34.4 l 6.3 12.2 v 266.5 266.5 l -7 12.6 c -3.7 7 -11.9 16.8 -18 22.2 -20.8 17.8 -16.4 17.3 -170.2 17.5 -133 0 -137 -0.2 -149.4 -4.9 z"/>
<local:ActionButton Grid.Column="4" Icon="m 1403 3380.7 c 0.5 -69.2 1.4 -125.5 2.6 -125.1 0.9 0.2 421.9 0.5 935.5 0.5 h 933.8 v 125.1 125.1 h -936.2 -936.2 l 0.5 -125.5004 z"/>
</Grid>
With some of your Path Data, it looks like this:
To set Color
or other properties in xaml, expose those as Full Property
if you don't have to bind or as DependencyProperty
if you want to bind .
With this approach, you have full control over your Controls and can extend these Controls to add more functionality, for example, I've another button, CommandButton, which is an ActionButton
with custom enabled/disabled style and Keyboard functionality:
class CommandButton : ActionButton
{
Color disabledColor;
public CommandButton() : base() {
Focusable = true;
normalColor = Colors.CornflowerBlue;
brush.Color = normalColor;
disabledColor = Colors.LightGray;
IsEnabledChanged += (s,e) => animateBrush(IsEnabled ? normalColor : disabledColor);
}
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) => animateBrush(highlightColor);
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) => animateBrush(normalColor);
protected override void OnMouseLeave(MouseEventArgs e) { if (IsEnabled) base.OnMouseLeave(e); }
protected override void OnKeyUp(KeyEventArgs e) {
if (e.Key != Key.Space) return;
Command.Invoke();
}
protected override void OnKeyDown(KeyEventArgs e) {
if (e.Key != Key.Space) return;
animateBrush(downColor);
}
}
and have one more button, MultiCommandButton, which is a CommandButton
that executes two tasks sequentially:
class MultiCommandButton : CommandButton
{
public Action BeforeCommand { get; set; }
public MultiCommandButton() : base() { }
protected override void OnKeyUp(KeyEventArgs e) {
if (e.Key != Key.Space) return;
BeforeCommand.Invoke();
Command.Invoke();
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) {
BeforeCommand.Invoke();
Command.Invoke();
}
}