0

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.

Pawcio
  • 399
  • 5
  • 15
  • tldr, but consider looking into BasedOn this will allow you to create styles based on others. https://stackoverflow.com/questions/5675004/how-to-base-a-style-on-another-style-in-a-resource-dictionary – Denis Schaf Dec 17 '21 at 09:56
  • `ScallableButton` needs to define its own default style which means that it cannot also "can take any Style a button can take". Which one do you want? – mm8 Dec 17 '21 at 14:29
  • I want both! Anyway, I ended with slightly modified version of "C" approach and are happy with my result. – Pawcio Dec 20 '21 at 12:25

0 Answers0