I want to make content of a Button
displayed in a ViewBox
so that text is scaled when the button is resized. I think the main source of the problem is the fact that I have already defined, legacy styles for the buttons. There is a big dictionary with many styles for all the elements in whole application and I don't know where a particular style could be used (there is a lot of buttons in the app with similar styles). So I generally don't want to modify the styles but just want to specify that text in SOME OF the buttons I currently work with should be scaled with button size...
So I want to make this:
<Button x:Name="buttonManual" Grid.Row="1" Margin="2" Style="{StaticResource ButtonModeDefault}">
<Viewbox Margin="0,3,0,3">
<Label Foreground="#FF3A4149" Content="Blabla bla" />
</Viewbox>
</Button>
Into something like this:
<g:ScallableButton Margin="2" Content="Blabla bla" Style="{StaticResource ButtonModeDefault}" />
Where the ScallableButton
can take any Style
a button can take.
Generally button styles look smillar to this:
<Style x:Key="ButtonModeDefault" TargetType="{x:Type Button}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="#FF3A4149"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="grid">
<Border x:Name="border" CornerRadius="1" BorderBrush="#FF555756" BorderThickness="1">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFEEEEEE" Offset="0.0" />
<GradientStop Color="#FFBDC3C7" Offset="0.25" />
</LinearGradientBrush>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
TextElement.FontWeight="Bold"></ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="border">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFDADFE1" Offset="0.0" />
<GradientStop Color="#FF6C7A89" Offset="0.25" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="grid" Value="0.45"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I've tried 3 different approaches but I'm not fully satisfied with the result.
A) Extending button class with control template:
<Button x:Class="...ScallableButton"
...>
<Button.Template>
<ControlTemplate>
<Viewbox Margin="0,3,0,3">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" Content="Click me"/>
</Viewbox>
</ControlTemplate>
</Button.Template>
</Button>
This completely replaces ControlTemplate
of ButtonModeDefault
style, leaving a nude label on transparent background.
B) Overriding ContentPropertyAttribute:
<Button x:Class="...ScallableButton"
...>
<Viewbox Margin="0,3,0,3">
<Label x:Name="label" Foreground="#FF3A4149" Content="Click Me" />
</Viewbox>
</Button>
and:
[ContentProperty(nameof(ActualContent))]
public partial class ScallableButton : Button {
public static readonly DependencyProperty ActualContentProperty
= DependencyProperty.Register(
nameof(ActualContent),
typeof(object),
typeof(ScallableButton),
new PropertyMetadata(onContentChange)
);
private static void onContentChange(DependencyObject d, DependencyPropertyChangedEventArgs e) {
((ScallableButton)d).label.Content = e.NewValue;
}
public object ActualContent {
get { return GetValue(ActualContentProperty); }
set { SetValue(ActualContentProperty, value); }
}
public ScallableButton() {
InitializeComponent();
}
}
This just doesn't work - like the attribute is ignored. The button works as usual, replacing the ViewBox
with new content.
C) Make a UserControl which holds a button:
<UserControl x:Class="...ScallableButton"
...>
<Button x:Name="main" Grid.Row="0" Margin="2">-->
<Viewbox Margin="0,3,0,3">
<Label x:Name="label" Foreground="#FF3A4149" Content="Click Me" />
</Viewbox>
</Button>
</UserControl>
And:
public partial class ScallableButton : UserControl {
public Button button => main;
public Style buttonStyle {
get => main.Style;
set => main.Style = value;
}
public ScallableButton() {
InitializeComponent();
}
private bool fixing = false;
protected override void OnContentChanged(object oldContent, object newContent) {
if (main == null || fixing) return;
fixing = true;
Content = main;
label.Content = newContent;
fixing = false;
}
}
This generally works but it has a downside in that I can no longer use properties of the button directly:
<g:ScallableButton Content="Blabla bla" buttonStyle="{StaticResource ButtonModeDefault}" Margin="2"/>
This in itself is not a big of a deal, but more importantly I would need Foreground
of the label be taken from the style definition and not hardcoded in my ScallableButton
.