I found many examples of how to make round buttons, but not a single decision on how to make a triangle. Is it possible, or is it better to use an image for this purpose?
- Can you please attach a picture of what kind of button you need to get? - Vadim Ovchinnikov
- The usual triangle))) - Maxim Shalashov
- @MaksimShalashov Aha, now I’ll make a triangle, and you say that it’s not that, not ordinary :) - Vadim Ovchinnikov
- Vadim here is not important how it looks, but the principle is it possible to do it or not. To correct it and make up, down, right, left directed I think I can do if I understand the principle of creating such buttons. - Maxim Shalashov
- @ MaksimShalashov did my answer help you or is something needed to be improved? - Vadim Ovchinnikov
2 answers
For a button to be truly triangular, it is necessary to override its pattern.
In order not to write a template from scratch completely, I will take as a basis the standard, for this in Visual Studio in the designer, click the right button and select Edit Template - Edit a Copy ... and enter the desired template name and select its location (you can simply click OK and move / rename later manually).
The studio will add a standard button template, edit it: look for <Setter Property="Template"> I have such a template there:
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true"> <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Border> In another OS it may differ, we rule it:
<Path Name="border" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}" Data="M12,24 L36,0 L36,48 Z" Width="48"/> The button will be simple and will not be able to contain content, so one Path enough.
Now we look at the places that are underlined in blue, the names of the properties in Border and Path are different, so we need to correct these places, the brush for the border is called Stroke (instead of BorderBrush ), and the brush for filling is Fill (instead of Background ). There is no content, so the line <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/> deleted completely.
Triggers corrected, blue underscores should not remain, the designer must earn, you can run the project. The button, in principle, is already functioning normally, but if using the Tab key to focus on it, we will see that it is rectangular. The FocusVisualStyle property is responsible for the focus, we can see in the style that it is set like this: <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/> , i.e. it is also necessary to edit the FocusVisual resource, the Rectangle is now there, we will take our already completed Path , but without a fill and with a broken border (as was the case with Rectangle ):
<Path Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2" Data="M12,24 L36,0 L36,48 Z" Width="48"/> We see that the Path geometry is repeated twice to avoid duplication, we will render it with a separate resource:
<StreamGeometry x:Key="Geometry">M12,24 L36,0 L36,48 Z</StreamGeometry> Then in Path will be enough to specify Data="{StaticResource Geometry}"
And finally, you can remove unnecessary setters from the style, which relate to content.
Also, in order to be able to get the arrow buttons also up / down / right, add the following transformations to the resources:
<RotateTransform x:Key="Rotate90" CenterX="24" CenterY="24" Angle="90"/> <RotateTransform x:Key="Rotate180" CenterX="24" CenterY="24" Angle="180"/> <RotateTransform x:Key="Rotate270" CenterX="24" CenterY="24" Angle="270"/> As a result, the whole example looks like this:
<Window.Resources> <StreamGeometry x:Key="Geometry">M12,24 L36,0 L36,48 Z</StreamGeometry> <Style x:Key="FocusVisual"> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Path Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2" Data="{StaticResource Geometry}" Width="48"/> </ControlTemplate> </Setter.Value> </Setter> </Style> <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"/> <Style x:Key="TriangleButtonStyle" TargetType="{x:Type Button}"> <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/> <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/> <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Path Name="border" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}" Data="{StaticResource Geometry}" Width="48"/> <ControlTemplate.Triggers> <Trigger Property="IsDefaulted" Value="true"> <Setter Property="Stroke" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/> </Trigger> <Trigger Property="IsMouseOver" Value="true"> <Setter Property="Fill" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/> <Setter Property="Stroke" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/> </Trigger> <Trigger Property="IsPressed" Value="true"> <Setter Property="Fill" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/> <Setter Property="Stroke" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Fill" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/> <Setter Property="Stroke" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <RotateTransform x:Key="Rotate090" CenterX="24" CenterY="24" Angle="090"/> <RotateTransform x:Key="Rotate180" CenterX="24" CenterY="24" Angle="180"/> <RotateTransform x:Key="Rotate270" CenterX="24" CenterY="24" Angle="270"/> </Window.Resources> <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"> <Button Style="{DynamicResource TriangleButtonStyle}"/> <Button Style="{DynamicResource TriangleButtonStyle}" RenderTransform="{StaticResource Rotate090}"/> <Button Style="{DynamicResource TriangleButtonStyle}" RenderTransform="{StaticResource Rotate180}"/> <Button Style="{DynamicResource TriangleButtonStyle}" RenderTransform="{StaticResource Rotate270}"/> </StackPanel> - The only
Datavalue forPathwould be to make it not duplicate, and so very good. It is also not entirely clear exactly how to apply the same styles for buttons of different directions. - Vadim Ovchinnikov - Well, this is the little things, I hope the vehicle will handle it on their own. And different directions - but at least like yours through the
RotateTransform(which can also be put into resources and then set to the button itself, such asRenderTransform="{StaticResource Rotate90}") - Andrey NOP - @VadimOvchinnikov, added the answer. - Andrei NOP
- one@VadimOvchinnikov, this version of the button is unscaled. You can, of course, put Path in the ViewBox, but, as you know, it scales the thickness of the lines, which at a large scale will not look good. The idea is to write a converter for the scale and draw geometry using it. As for
RenderTransformOriginmost likely you are right, you should try it with it. - Andrei NOP - oneScaling can still be achieved: 1) you need to replace
CenterX="24"andCenterY="24"withRenderTransformOrigin="0.5, 0.5". 2) For the buttons to scale, set thePathtoStretch="Uniform". 3) In thePathforFocusVisualandButtonyou must removeWidth="48". - Vadim Ovchinnikov
You just need to create an icon in vector style using the Path and remove the borders and background from the button (for this, we style the button style on {StaticResource {x:Static ToolBar.ButtonStyleKey}} , since this style removes the borders and the background of the button).
As a result, XAML windows (for example, MainWindow.xaml ):
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button Style="{StaticResource ArrowButtonStyle}"> <Path Style="{StaticResource ArrowIconButtonStyle}" /> </Button> <Button Style="{StaticResource ArrowButtonStyle}"> <Path Style="{StaticResource ArrowIconButtonStyle}"> <Path.RenderTransform> <RotateTransform Angle="90" /> </Path.RenderTransform> </Path> </Button> <Button Style="{StaticResource ArrowButtonStyle}"> <Path Style="{StaticResource ArrowIconButtonStyle}"> <Path.RenderTransform> <RotateTransform Angle="180" /> </Path.RenderTransform> </Path> </Button> <Button Style="{StaticResource ArrowButtonStyle}"> <Path Style="{StaticResource ArrowIconButtonStyle}"> <Path.RenderTransform> <RotateTransform Angle="-90" /> </Path.RenderTransform> </Path> </Button> </StackPanel> Styles (for example, in App.xaml ):
<Style x:Key="ArrowButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"> <Setter Property="Padding" Value="5" /> <Setter Property="Width" Value="100" /> <Setter Property="Height" Value="100" /> </Style> <Style x:Key="ArrowIconButtonStyle" TargetType="Path"> <Setter Property="Data" Value="F1 M 287.328,237.333L 319.344,255.818L 319.344,218.849L 287.328,237.333 Z " /> <Setter Property="Fill" Value="#00a8f3" /> <Setter Property="Width" Value="40" /> <Setter Property="Height" Value="80" /> <Setter Property="Stretch" Value="Fill" /> <Setter Property="RenderTransformOrigin" Value="0.5, 0.5" /> </Style> As a result, we get different arrows:
If these buttons need to be reused many times, then the RenderTransform property RenderTransform put in separate styles for each direction of the arrow.
MainWindow.xaml
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button Style="{StaticResource ArrowButtonStyle}"> <Path Style="{StaticResource ArrowLeftIconButtonStyle}" /> </Button> <Button Style="{StaticResource ArrowButtonStyle}"> <Path Style="{StaticResource ArrowTopIconButtonStyle}" /> </Button> <Button Style="{StaticResource ArrowButtonStyle}"> <Path Style="{StaticResource ArrowRightIconButtonStyle}" /> </Button> <Button Style="{StaticResource ArrowButtonStyle}"> <Path Style="{StaticResource ArrowBottomIconButtonStyle}" /> </Button> </StackPanel> App.xaml
<Style x:Key="ArrowButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"> <Setter Property="Padding" Value="5" /> <Setter Property="Width" Value="100" /> <Setter Property="Height" Value="100" /> </Style> <Style x:Key="ArrowIconButtonStyle" TargetType="Path"> <Setter Property="Data" Value="F1 M 287.328,237.333L 319.344,255.818L 319.344,218.849L 287.328,237.333 Z " /> <Setter Property="Fill" Value="#00a8f3" /> <Setter Property="Width" Value="40" /> <Setter Property="Height" Value="80" /> <Setter Property="Stretch" Value="Fill" /> <Setter Property="RenderTransformOrigin" Value="0.5, 0.5" /> </Style> <Style x:Key="ArrowLeftIconButtonStyle" TargetType="Path" BasedOn="{StaticResource ArrowIconButtonStyle}"> </Style> <Style x:Key="ArrowRightIconButtonStyle" TargetType="Path" BasedOn="{StaticResource ArrowIconButtonStyle}"> <Setter Property="RenderTransform"> <Setter.Value> <RotateTransform Angle="180" /> </Setter.Value> </Setter> </Style> <Style x:Key="ArrowTopIconButtonStyle" TargetType="Path" BasedOn="{StaticResource ArrowIconButtonStyle}"> <Setter Property="RenderTransform"> <Setter.Value> <RotateTransform Angle="90" /> </Setter.Value> </Setter> </Style> <Style x:Key="ArrowBottomIconButtonStyle" TargetType="Path" BasedOn="{StaticResource ArrowIconButtonStyle}"> <Setter Property="RenderTransform"> <Setter.Value> <RotateTransform Angle="-90" /> </Setter.Value> </Setter> </Style> - And why from
ToolBar.ButtonStyleKey? - Andrei NOP - @AndreyNOP Just because the background and borders are removed using this style. Now I will bring this in response. - Vadim Ovchinnikov
- In fact, they are not deleted, but become invisible, but when you hover or when you get focus, you can see that the button is not triangular - Andrey NOP
- @ AndreiNOP Yes, everything is exactly as you described. I am satisfied with this behavior. Of course, it is possible for the button to be really triangular, but personally I don’t see the meaning and convenience of the user in this. - Vadim Ovchinnikov


