1

I have a "hero/vertical" button (with an image inside) defined by this Style:

<ControlTemplate TargetType="{x:Type controls:ImageButton}">
    <Grid MinHeight="{TemplateBinding MinHeight}" Background="{TemplateBinding Background}" Width="Auto" SnapsToDevicePixels="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <!-- Image -->
        <Viewbox x:Name="ViewBoxInternal" Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Center" IsEnabled="{TemplateBinding IsEnabled}"
                 Stretch="Uniform" StretchDirection="Both" Effect="{x:Null}"
                 Width="{TemplateBinding MaxSize}" Height="{TemplateBinding MaxSize}" 
                 MaxHeight="{TemplateBinding MaxSize}" MaxWidth="{TemplateBinding MaxSize}">
            <ContentPresenter ContentSource="{TemplateBinding Content}" Width="Auto" Height="Auto" 
                              HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Viewbox>

        <!-- Text -->
        <TextBlock x:Name="TextBlockInternal" Grid.Row="1"
                   HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="3,2,3,3"
                   VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Text="{TemplateBinding Text}" 
                   TextWrapping="Wrap" Effect="{TemplateBinding Effect}" TextAlignment="Center"/>
    </Grid>
</ControlTemplate>

Usage:

<controls:ImageButton Text="Webcam Recording" Content="{StaticResource Vector.Camera.New}" 
          Margin="0" Height="70" MaxSize="30">

MaxSize controls the maximum size of the image inside the control.

Example image:

Image 1

Expected image:

Expected Image

My Problem:

All of my vertical buttons have a MaxWidth of 60, that's okay for English, the problem starts with other languages, when 60px is too small, so the button should grow bigger sideways, but still use all of the height.

So, How can I make the TextBlock use all available height before expanding the width?

TextWrapping only works when there's no width available, like when I set the Width to 60.

Community
  • 1
  • 1
Nicke Manarin
  • 3,026
  • 4
  • 37
  • 79

1 Answers1

1

Interesting question. Without knowing the width of the widest word, you will not know how to define the width of the TextBox to get the automatic wrapping you want. To go all out on this, you could make use of some Language API and use its wordbreakers. For what I believe is an isolated case in your code, perhaps you can do this yourself and let the TextBox auto-size itself, centered, and achieve the result you want.

Write a Converter to force breaks between words. I threw this together to demonstrate the technique...you will want to adapt is appropriately to your solution of course.

Code

public partial class MainWindow : Window
{
    public string LongText
    {
        get { return (string)GetValue(LongTextProperty); }
        set { SetValue(LongTextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for LongText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LongTextProperty =
        DependencyProperty.Register("LongText", typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));


    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        LongText = "Some Really Long Text";
    }
}

public class WordBreakConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var text = (string)value;
        text = text.Replace(" ", Environment.NewLine);
        return text;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

XAML

<Window.Resources>
    <local:WordBreakConverter x:Key="WordBreakConverter"/>
</Window.Resources>

<Grid x:Name="LayoutRoot">
    <StackPanel>
        <TextBlock Text="WPF" FontSize="36" Margin="20" Foreground="Orange" HorizontalAlignment="Center"/>
        <TextBlock Text="{Binding LongText, Converter={StaticResource WordBreakConverter}}" TextAlignment="Center"/>
    </StackPanel>
</Grid>

Result

Word Breaker Text Example

Kory Gill
  • 6,993
  • 1
  • 25
  • 33
  • This could solve it, but there is a problem, this may always increase the height, without expanding the width. Maybe I could add a `\n` inside the `Text` and replace it inside the `Converter` with a `NewLine`. This way I could control the line break, if wanted. – Nicke Manarin Mar 06 '16 at 20:01
  • Yet, a `Converter` may be too taxing to my app, with almost 50 vertical buttons spread across multiple tabs. I'm thinking about extending the `TextBlock` and handling the `TextWrap` myself. – Nicke Manarin Mar 06 '16 at 20:03
  • 1
    Performance of the Converter will be minimal, and most likely unnoticeable. You certainly could extend this technique to "encode" the TextWrapping you desire (like find first space after X characters so text like "New Item" is one line), or just put the newlines in your string resources directly. Without knowing all about your solution, I do not have a generic answer for all use cases. – Kory Gill Mar 06 '16 at 20:08
  • I'll put the converter directly into the `Style` to handle all `\n` inside my string resource. So I'll be able to control the `NewLine`. Thanks. If you want to know about my current implementation, visit http://screentogif.codeplex.com/ – Nicke Manarin Mar 06 '16 at 20:20