1
votes

I have a custom WPF control based on Soroosh Davaee’s ImageButton example at http://www.codeproject.com/Tips/773386/WPF-ImageButton. The custom control combines an Image and TextBlock in a horizontal StackPanel within a Button. (BTW, to get Soroosh’s example to run, I had to edit the solution properties so that “SampleView” is the startup project rather than “ExtendedButton” being the startup project.)

I want the text in the TextBlock to automatically shrink if necessary to avoid clipping at the right edge if the text is too long to fit naturally in the button. For example, if I edit Soroosh's MainWindow.xaml to make the button text too long to fit...

    ...
    <EB:ImageButton Width="100" Height="30" Content="TextTooLongToFitInTheButton" Grid.Row="2"
    ...
    <EB:ImageButton Width="100" Height="30" Content="TextTooLongToFitInTheButton" Grid.Row="2"
    ...

...the result is the following buttons with clipped text:

Buttons with clipped text

In researching this, it seems the simplest way to auto-shrink the content of a TextBlock is to wrap it within a Viewbox:

<Viewbox StretchDirection="DownOnly" Stretch="Fill">
    <TextBlock ... />
</Viewbox>

DownOnly apparently prevents the Viewbox from enlarging the text to fill the space, and Fill (as opposed to Uniform) seems to tell it to stretch (shrink) only the dimension that needs to shrink (i.e. the horizontal dimension in my case).

In Soroosh's example Generic.xaml file, I wrapped the TextBlock in such a Viewbox:

     <Button >
         <StackPanel Orientation="Horizontal">
             <Image Margin="2 0"
                    Source="{TemplateBinding Image}"
                    Width="{TemplateBinding ImageWidth}"
                    Height="{TemplateBinding ImageHeight}"
                    Visibility="{TemplateBinding Image,Converter={StaticResource VisibilityConvertor}}"
                    VerticalAlignment="Center"/>
I added-->   <Viewbox StretchDirection="DownOnly" Stretch="Fill">
             <TextBlock Text="{TemplateBinding Content}"
                    VerticalAlignment="Center"/>
I added-->   </Viewbox>
         </StackPanel>
     </Button>

This produced exactly the same clipped button text. Just experimenting, I tried forcing the Viewbox to have a fixed width...

             <Viewbox StretchDirection="DownOnly" Stretch="Fill" Width="60">

...which produced this:

Viewbox with manually-sized width

...which shows the capability of the Viewbox, if only it could somehow know its available width when it's inside the StackPanel.

I did note that if I wrap the Viewbox around the whole StackPanel, it successfully auto-shrinks the entire content of the StackPanel:

     <Button >
         <Viewbox StretchDirection="DownOnly" Stretch="Fill" Width="60">
             <StackPanel Orientation="Horizontal">
                 <Image Margin="2 0"
                    Source="{TemplateBinding Image}"
                    Width="{TemplateBinding ImageWidth}"
                    Height="{TemplateBinding ImageHeight}"
                    Visibility="{TemplateBinding Image,Converter={StaticResource VisibilityConvertor}}"
                    VerticalAlignment="Center"/>
                 <TextBlock Text="{TemplateBinding Content}"
                    VerticalAlignment="Center"/>
             </StackPanel>
         </Viewbox>
     </Button>

...which produces very nearly what I want:

Viewbox wrapping the entire StackPanel

...but both the image and text are shrunk, and I want only the text shrunk.

How can I make the Viewbox, wrapping only the TextBox, know its available width (and height, I suppose) from within a cell of the StackPanel?

1

1 Answers

2
votes

This is a common problem. The solution is simply to not use a StackPanel to do any kind of layout that requires re-sizing of child controls. It's simply not the correct Panel for the job. Instead, try using a Grid panel, which will resize its child controls. The StackPanel control is really only good for the most basic of layout duties... try anything more adventurous and you'll find yourself getting these issues.

One other alternative is to use the TextBlock.TextTrimming Property to trim the text instead... you could put the full text into a ToolTip too.